diff --git a/.github/actions/setup-playwright/action.yml b/.github/actions/setup-playwright/action.yml new file mode 100644 index 0000000..9616178 --- /dev/null +++ b/.github/actions/setup-playwright/action.yml @@ -0,0 +1,32 @@ +name: 'Setup and Cache Playwright' +description: 'Installs playwright browsers and caches them.' + +inputs: + version: + description: 'The version of playwright' + required: true + +outputs: + cache-hit: + description: 'Whether the cache was hit or not' + value: ${{ steps.playwright-cache.outputs.cache-hit }} + +runs: + using: 'composite' + steps: + - name: Cache playwright browsers + id: playwright-cache + uses: actions/cache@v4 + with: + key: playwright-browsers-${{ runner.os }}-${{ inputs.version }} + path: ~/.cache/ms-playwright + + - name: Install playwright browsers if they don't exist + shell: bash + run: npx playwright install --with-deps + if: steps.playwright-cache.outputs.cache-hit != 'true' + + - name: Install playwright dependencies if binaries exist + shell: bash + run: npx playwright install-deps + if: steps.playwright-cache.outputs.cache-hit == 'true' diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..eae4f22 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,28 @@ +name: Tests +on: + push: + branches: [main, next] + pull_request: + branches: [main, next] +jobs: + test: + timeout-minutes: 60 + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: lts/* + cache: 'npm' + - name: Install dependencies + run: npm ci + - name: Build libs + run: npm run build + - name: Get playwright version + run: echo "PLAYWRIGHT_VERSION=$(npm ls @playwright/test | grep @playwright | sed 's/.*@//')" >> $GITHUB_ENV + - name: Setup playwright + uses: ./.github/actions/setup-playwright + with: + version: ${{ env.PLAYWRIGHT_VERSION }} + - name: Tests + run: npm run test diff --git a/.gitignore b/.gitignore index 5ef93b0..d3aa47c 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,7 @@ dbs/ tsconfig.tsbuildinfo .env .DS_Store +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..41583e3 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +@jsr:registry=https://npm.jsr.io diff --git a/.vscode/settings.json b/.vscode/settings.json index 3a3d8c3..d4a7cdd 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -13,5 +13,7 @@ }, "[typescriptreact]": { "editor.defaultFormatter": "biomejs.biome" - } + }, + "deno.enable": true, + "deno.enablePaths": ["./packages/pg-gateway/test/deno"] } diff --git a/biome.json b/biome.json index 92dea50..8ae72d6 100644 --- a/biome.json +++ b/biome.json @@ -12,7 +12,8 @@ "formatter": { "enabled": true, "indentStyle": "space", - "indentWidth": 2 + "indentWidth": 2, + "lineWidth": 100 }, "javascript": { "formatter": { diff --git a/deno.lock b/deno.lock new file mode 100644 index 0000000..e795e93 --- /dev/null +++ b/deno.lock @@ -0,0 +1,2122 @@ +{ + "version": "3", + "packages": { + "specifiers": { + "jsr:@std/assert@^1.0.3": "jsr:@std/assert@1.0.3", + "jsr:@std/expect": "jsr:@std/expect@1.0.1", + "jsr:@std/internal@^1.0.2": "jsr:@std/internal@1.0.2", + "jsr:@std/testing": "jsr:@std/testing@1.0.1", + "npm:@biomejs/biome@1.8.3": "npm:@biomejs/biome@1.8.3", + "npm:@biomejs/biome@^1.8.3": "npm:@biomejs/biome@1.8.3", + "npm:@electric-sql/pglite": "npm:@electric-sql/pglite@0.2.6", + "npm:@electric-sql/pglite@^0.2.6": "npm:@electric-sql/pglite@0.2.6", + "npm:@jsr/std__bytes@^1.0.2": "npm:@jsr/std__bytes@1.0.2", + "npm:@jsr/std__crypto@^1.0.3": "npm:@jsr/std__crypto@1.0.3", + "npm:@jsr/std__encoding@^1.0.3": "npm:@jsr/std__encoding@1.0.3", + "npm:@nodeweb/knex@^3.1.0-alpha.13": "npm:@nodeweb/knex@3.1.0-alpha.13", + "npm:@nodeweb/pg@^8.12.0-alpha.5": "npm:@nodeweb/pg@8.12.0-alpha.5", + "npm:@total-typescript/tsconfig@^1.0.4": "npm:@total-typescript/tsconfig@1.0.4", + "npm:@types/node": "npm:@types/node@18.16.19", + "npm:@types/node@^20.14.11": "npm:@types/node@20.16.2", + "npm:@types/pg": "npm:@types/pg@8.11.8", + "npm:@types/pg@^8.11.8": "npm:@types/pg@8.11.8", + "npm:@vitest/browser@^2.0.5": "npm:@vitest/browser@2.0.5_playwright@1.47.0_vitest@2.0.5__@types+node@18.16.19_@testing-library+dom@10.4.0_typescript@5.5.4_@types+node@18.16.19", + "npm:asn1js@^3.0.5": "npm:asn1js@3.0.5", + "npm:pg": "npm:pg@8.12.0", + "npm:pg-gateway": "npm:pg-gateway@0.3.0-alpha.6", + "npm:pg-protocol@^1.6.1": "npm:pg-protocol@1.6.1", + "npm:pg@^8.12.0": "npm:pg@8.12.0", + "npm:pkijs@^3.2.4": "npm:pkijs@3.2.4", + "npm:playwright@^1.47.0": "npm:playwright@1.47.0", + "npm:postgres@^3.4.4": "npm:postgres@3.4.4", + "npm:tsup@^8.2.3": "npm:tsup@8.2.4_typescript@5.5.4_esbuild@0.23.1_tsx@4.19.0", + "npm:tsx@^4.16.2": "npm:tsx@4.19.0", + "npm:typescript@^5.5.3": "npm:typescript@5.5.4", + "npm:vitest@^2.0.5": "npm:vitest@2.0.5_@types+node@18.16.19" + }, + "jsr": { + "@std/assert@1.0.3": { + "integrity": "b0d03ce1ced880df67132eea140623010d415848df66f6aa5df76507ca7c26d8", + "dependencies": [ + "jsr:@std/internal@^1.0.2" + ] + }, + "@std/expect@1.0.1": { + "integrity": "44075d9c2cb701ddfc4d5a260da2fb26ba3cfd94c45d3a0359f63cabbf85a058", + "dependencies": [ + "jsr:@std/assert@^1.0.3", + "jsr:@std/internal@^1.0.2" + ] + }, + "@std/internal@1.0.2": { + "integrity": "f4cabe2021352e8bfc24e6569700df87bf070914fc38d4b23eddd20108ac4495" + }, + "@std/testing@1.0.1": { + "integrity": "9c25841137ee818933e1722091bb9ed5fdc251c35e84c97979a52196bdb6c5c3" + } + }, + "npm": { + "@ampproject/remapping@2.3.0": { + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dependencies": { + "@jridgewell/gen-mapping": "@jridgewell/gen-mapping@0.3.5", + "@jridgewell/trace-mapping": "@jridgewell/trace-mapping@0.3.25" + } + }, + "@babel/code-frame@7.24.7": { + "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "dependencies": { + "@babel/highlight": "@babel/highlight@7.24.7", + "picocolors": "picocolors@1.0.1" + } + }, + "@babel/helper-validator-identifier@7.24.7": { + "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "dependencies": {} + }, + "@babel/highlight@7.24.7": { + "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", + "dependencies": { + "@babel/helper-validator-identifier": "@babel/helper-validator-identifier@7.24.7", + "chalk": "chalk@2.4.2", + "js-tokens": "js-tokens@4.0.0", + "picocolors": "picocolors@1.0.1" + } + }, + "@babel/runtime@7.25.6": { + "integrity": "sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==", + "dependencies": { + "regenerator-runtime": "regenerator-runtime@0.14.1" + } + }, + "@biomejs/biome@1.8.3": { + "integrity": "sha512-/uUV3MV+vyAczO+vKrPdOW0Iaet7UnJMU4bNMinggGJTAnBPjCoLEYcyYtYHNnUNYlv4xZMH6hVIQCAozq8d5w==", + "dependencies": { + "@biomejs/cli-darwin-arm64": "@biomejs/cli-darwin-arm64@1.8.3", + "@biomejs/cli-darwin-x64": "@biomejs/cli-darwin-x64@1.8.3", + "@biomejs/cli-linux-arm64": "@biomejs/cli-linux-arm64@1.8.3", + "@biomejs/cli-linux-arm64-musl": "@biomejs/cli-linux-arm64-musl@1.8.3", + "@biomejs/cli-linux-x64": "@biomejs/cli-linux-x64@1.8.3", + "@biomejs/cli-linux-x64-musl": "@biomejs/cli-linux-x64-musl@1.8.3", + "@biomejs/cli-win32-arm64": "@biomejs/cli-win32-arm64@1.8.3", + "@biomejs/cli-win32-x64": "@biomejs/cli-win32-x64@1.8.3" + } + }, + "@biomejs/cli-darwin-arm64@1.8.3": { + "integrity": "sha512-9DYOjclFpKrH/m1Oz75SSExR8VKvNSSsLnVIqdnKexj6NwmiMlKk94Wa1kZEdv6MCOHGHgyyoV57Cw8WzL5n3A==", + "dependencies": {} + }, + "@biomejs/cli-darwin-x64@1.8.3": { + "integrity": "sha512-UeW44L/AtbmOF7KXLCoM+9PSgPo0IDcyEUfIoOXYeANaNXXf9mLUwV1GeF2OWjyic5zj6CnAJ9uzk2LT3v/wAw==", + "dependencies": {} + }, + "@biomejs/cli-linux-arm64-musl@1.8.3": { + "integrity": "sha512-9yjUfOFN7wrYsXt/T/gEWfvVxKlnh3yBpnScw98IF+oOeCYb5/b/+K7YNqKROV2i1DlMjg9g/EcN9wvj+NkMuQ==", + "dependencies": {} + }, + "@biomejs/cli-linux-arm64@1.8.3": { + "integrity": "sha512-fed2ji8s+I/m8upWpTJGanqiJ0rnlHOK3DdxsyVLZQ8ClY6qLuPc9uehCREBifRJLl/iJyQpHIRufLDeotsPtw==", + "dependencies": {} + }, + "@biomejs/cli-linux-x64-musl@1.8.3": { + "integrity": "sha512-UHrGJX7PrKMKzPGoEsooKC9jXJMa28TUSMjcIlbDnIO4EAavCoVmNQaIuUSH0Ls2mpGMwUIf+aZJv657zfWWjA==", + "dependencies": {} + }, + "@biomejs/cli-linux-x64@1.8.3": { + "integrity": "sha512-I8G2QmuE1teISyT8ie1HXsjFRz9L1m5n83U1O6m30Kw+kPMPSKjag6QGUn+sXT8V+XWIZxFFBoTDEDZW2KPDDw==", + "dependencies": {} + }, + "@biomejs/cli-win32-arm64@1.8.3": { + "integrity": "sha512-J+Hu9WvrBevfy06eU1Na0lpc7uR9tibm9maHynLIoAjLZpQU3IW+OKHUtyL8p6/3pT2Ju5t5emReeIS2SAxhkQ==", + "dependencies": {} + }, + "@biomejs/cli-win32-x64@1.8.3": { + "integrity": "sha512-/PJ59vA1pnQeKahemaQf4Nyj7IKUvGQSc3Ze1uIGi+Wvr1xF7rGobSrAAG01T/gUDG21vkDsZYM03NAmPiVkqg==", + "dependencies": {} + }, + "@bundled-es-modules/cookie@2.0.0": { + "integrity": "sha512-Or6YHg/kamKHpxULAdSqhGqnWFneIXu1NKvvfBBzKGwpVsYuFIQ5aBPHDnnoR3ghW1nvSkALd+EF9iMtY7Vjxw==", + "dependencies": { + "cookie": "cookie@0.5.0" + } + }, + "@bundled-es-modules/statuses@1.0.1": { + "integrity": "sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==", + "dependencies": { + "statuses": "statuses@2.0.1" + } + }, + "@bundled-es-modules/tough-cookie@0.1.6": { + "integrity": "sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==", + "dependencies": { + "@types/tough-cookie": "@types/tough-cookie@4.0.5", + "tough-cookie": "tough-cookie@4.1.4" + } + }, + "@electric-sql/pglite@0.2.6": { + "integrity": "sha512-tyWWxj1Z1Pd4BqBZL1ER2SXaCn5s9N0bxTQCAkGaaWe8r9EEe1bNs20RAG3/+ZeBJtDrk8y5xjocactL+4aIXg==", + "dependencies": {} + }, + "@esbuild/aix-ppc64@0.21.5": { + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "dependencies": {} + }, + "@esbuild/aix-ppc64@0.23.1": { + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", + "dependencies": {} + }, + "@esbuild/android-arm64@0.21.5": { + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "dependencies": {} + }, + "@esbuild/android-arm64@0.23.1": { + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", + "dependencies": {} + }, + "@esbuild/android-arm@0.21.5": { + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "dependencies": {} + }, + "@esbuild/android-arm@0.23.1": { + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", + "dependencies": {} + }, + "@esbuild/android-x64@0.21.5": { + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "dependencies": {} + }, + "@esbuild/android-x64@0.23.1": { + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", + "dependencies": {} + }, + "@esbuild/darwin-arm64@0.21.5": { + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "dependencies": {} + }, + "@esbuild/darwin-arm64@0.23.1": { + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", + "dependencies": {} + }, + "@esbuild/darwin-x64@0.21.5": { + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "dependencies": {} + }, + "@esbuild/darwin-x64@0.23.1": { + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", + "dependencies": {} + }, + "@esbuild/freebsd-arm64@0.21.5": { + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "dependencies": {} + }, + "@esbuild/freebsd-arm64@0.23.1": { + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", + "dependencies": {} + }, + "@esbuild/freebsd-x64@0.21.5": { + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "dependencies": {} + }, + "@esbuild/freebsd-x64@0.23.1": { + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", + "dependencies": {} + }, + "@esbuild/linux-arm64@0.21.5": { + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "dependencies": {} + }, + "@esbuild/linux-arm64@0.23.1": { + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", + "dependencies": {} + }, + "@esbuild/linux-arm@0.21.5": { + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "dependencies": {} + }, + "@esbuild/linux-arm@0.23.1": { + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", + "dependencies": {} + }, + "@esbuild/linux-ia32@0.21.5": { + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "dependencies": {} + }, + "@esbuild/linux-ia32@0.23.1": { + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", + "dependencies": {} + }, + "@esbuild/linux-loong64@0.21.5": { + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "dependencies": {} + }, + "@esbuild/linux-loong64@0.23.1": { + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", + "dependencies": {} + }, + "@esbuild/linux-mips64el@0.21.5": { + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "dependencies": {} + }, + "@esbuild/linux-mips64el@0.23.1": { + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", + "dependencies": {} + }, + "@esbuild/linux-ppc64@0.21.5": { + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "dependencies": {} + }, + "@esbuild/linux-ppc64@0.23.1": { + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", + "dependencies": {} + }, + "@esbuild/linux-riscv64@0.21.5": { + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "dependencies": {} + }, + "@esbuild/linux-riscv64@0.23.1": { + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", + "dependencies": {} + }, + "@esbuild/linux-s390x@0.21.5": { + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "dependencies": {} + }, + "@esbuild/linux-s390x@0.23.1": { + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", + "dependencies": {} + }, + "@esbuild/linux-x64@0.21.5": { + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "dependencies": {} + }, + "@esbuild/linux-x64@0.23.1": { + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", + "dependencies": {} + }, + "@esbuild/netbsd-x64@0.21.5": { + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "dependencies": {} + }, + "@esbuild/netbsd-x64@0.23.1": { + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", + "dependencies": {} + }, + "@esbuild/openbsd-arm64@0.23.1": { + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "dependencies": {} + }, + "@esbuild/openbsd-x64@0.21.5": { + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "dependencies": {} + }, + "@esbuild/openbsd-x64@0.23.1": { + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", + "dependencies": {} + }, + "@esbuild/sunos-x64@0.21.5": { + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "dependencies": {} + }, + "@esbuild/sunos-x64@0.23.1": { + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", + "dependencies": {} + }, + "@esbuild/win32-arm64@0.21.5": { + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "dependencies": {} + }, + "@esbuild/win32-arm64@0.23.1": { + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", + "dependencies": {} + }, + "@esbuild/win32-ia32@0.21.5": { + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "dependencies": {} + }, + "@esbuild/win32-ia32@0.23.1": { + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", + "dependencies": {} + }, + "@esbuild/win32-x64@0.21.5": { + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "dependencies": {} + }, + "@esbuild/win32-x64@0.23.1": { + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", + "dependencies": {} + }, + "@inquirer/confirm@3.2.0": { + "integrity": "sha512-oOIwPs0Dvq5220Z8lGL/6LHRTEr9TgLHmiI99Rj1PJ1p1czTys+olrgBqZk4E2qC0YTzeHprxSQmoHioVdJ7Lw==", + "dependencies": { + "@inquirer/core": "@inquirer/core@9.1.0", + "@inquirer/type": "@inquirer/type@1.5.3" + } + }, + "@inquirer/core@9.1.0": { + "integrity": "sha512-RZVfH//2ytTjmaBIzeKT1zefcQZzuruwkpTwwbe/i2jTl4o9M+iML5ChULzz6iw1Ok8iUBBsRCjY2IEbD8Ft4w==", + "dependencies": { + "@inquirer/figures": "@inquirer/figures@1.0.5", + "@inquirer/type": "@inquirer/type@1.5.3", + "@types/mute-stream": "@types/mute-stream@0.0.4", + "@types/node": "@types/node@22.5.4", + "@types/wrap-ansi": "@types/wrap-ansi@3.0.0", + "ansi-escapes": "ansi-escapes@4.3.2", + "cli-spinners": "cli-spinners@2.9.2", + "cli-width": "cli-width@4.1.0", + "mute-stream": "mute-stream@1.0.0", + "signal-exit": "signal-exit@4.1.0", + "strip-ansi": "strip-ansi@6.0.1", + "wrap-ansi": "wrap-ansi@6.2.0", + "yoctocolors-cjs": "yoctocolors-cjs@2.1.2" + } + }, + "@inquirer/figures@1.0.5": { + "integrity": "sha512-79hP/VWdZ2UVc9bFGJnoQ/lQMpL74mGgzSYX1xUqCVk7/v73vJCMw1VuyWN1jGkZ9B3z7THAbySqGbCNefcjfA==", + "dependencies": {} + }, + "@inquirer/type@1.5.3": { + "integrity": "sha512-xUQ14WQGR/HK5ei+2CvgcwoH9fQ4PgPGmVFSN0pc1+fVyDL3MREhyAY7nxEErSu6CkllBM3D7e3e+kOvtu+eIg==", + "dependencies": { + "mute-stream": "mute-stream@1.0.0" + } + }, + "@isaacs/cliui@8.0.2": { + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dependencies": { + "string-width": "string-width@5.1.2", + "string-width-cjs": "string-width@4.2.3", + "strip-ansi": "strip-ansi@7.1.0", + "strip-ansi-cjs": "strip-ansi@6.0.1", + "wrap-ansi": "wrap-ansi@8.1.0", + "wrap-ansi-cjs": "wrap-ansi@7.0.0" + } + }, + "@jridgewell/gen-mapping@0.3.5": { + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", + "dependencies": { + "@jridgewell/set-array": "@jridgewell/set-array@1.2.1", + "@jridgewell/sourcemap-codec": "@jridgewell/sourcemap-codec@1.5.0", + "@jridgewell/trace-mapping": "@jridgewell/trace-mapping@0.3.25" + } + }, + "@jridgewell/resolve-uri@3.1.2": { + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dependencies": {} + }, + "@jridgewell/set-array@1.2.1": { + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dependencies": {} + }, + "@jridgewell/sourcemap-codec@1.5.0": { + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dependencies": {} + }, + "@jridgewell/trace-mapping@0.3.25": { + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dependencies": { + "@jridgewell/resolve-uri": "@jridgewell/resolve-uri@3.1.2", + "@jridgewell/sourcemap-codec": "@jridgewell/sourcemap-codec@1.5.0" + } + }, + "@jsr/std__bytes@1.0.2": { + "integrity": "sha512-bkZ1rllRB1qsxFbPqtO1VAYTW2+3ZDmf6pcy8xihKS33r0Z1ly6/E/5DoapnJsNy05LdnANUySWt5kj/awgGdg==", + "dependencies": {} + }, + "@jsr/std__crypto@1.0.3": { + "integrity": "sha512-B/4PCckQfbfVMyQvG2I15QbN4vfx1S0tXKstpSlzuSmVwGmAOT/DLCtrbrAKlMiJBN4Zb53KaAJQm8VgkZ+bDg==", + "dependencies": {} + }, + "@jsr/std__encoding@1.0.3": { + "integrity": "sha512-l4R27qOsop6yYub8zpMj4tT6OCW3AqD14XswRrOs9DiD9DHWG95lxPNAek3+W4flBqIg7ExQy25VKu+qHqSV0g==", + "dependencies": {} + }, + "@mswjs/interceptors@0.35.1": { + "integrity": "sha512-nMuUaMCtg8oKSTHwAnsoGRN6c1RZXNm6+ebEoe9SBGALVlBIzniZoSuC/itNCLOt51YEEYsF0svB/sOzYhqLPA==", + "dependencies": { + "@open-draft/deferred-promise": "@open-draft/deferred-promise@2.2.0", + "@open-draft/logger": "@open-draft/logger@0.3.0", + "@open-draft/until": "@open-draft/until@2.1.0", + "is-node-process": "is-node-process@1.2.0", + "outvariant": "outvariant@1.4.3", + "strict-event-emitter": "strict-event-emitter@0.5.1" + } + }, + "@noble/hashes@1.5.0": { + "integrity": "sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==", + "dependencies": {} + }, + "@nodelib/fs.scandir@2.1.5": { + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dependencies": { + "@nodelib/fs.stat": "@nodelib/fs.stat@2.0.5", + "run-parallel": "run-parallel@1.2.0" + } + }, + "@nodelib/fs.stat@2.0.5": { + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dependencies": {} + }, + "@nodelib/fs.walk@1.2.8": { + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dependencies": { + "@nodelib/fs.scandir": "@nodelib/fs.scandir@2.1.5", + "fastq": "fastq@1.17.1" + } + }, + "@nodeweb/knex@3.1.0-alpha.13": { + "integrity": "sha512-T20NxBcJ8J7rYqLYwwiMNQ0drQljsntmOHBZrYtPkB1TdzqGDvZ4eCmUbc7Blh0UsfOo6I2O7agJyqPC8DsIgg==", + "dependencies": {} + }, + "@nodeweb/pg@8.12.0-alpha.5": { + "integrity": "sha512-BQaEeaymGgyclEz/ZX48FhK12nQJSAZrfRmZMgTECTaA0Ty7b2qsSuDW9pkZWPCISGZTgizOZ3s2k2hAFIAPzw==", + "dependencies": {} + }, + "@open-draft/deferred-promise@2.2.0": { + "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", + "dependencies": {} + }, + "@open-draft/logger@0.3.0": { + "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", + "dependencies": { + "is-node-process": "is-node-process@1.2.0", + "outvariant": "outvariant@1.4.3" + } + }, + "@open-draft/until@2.1.0": { + "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", + "dependencies": {} + }, + "@pkgjs/parseargs@0.11.0": { + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dependencies": {} + }, + "@polka/url@1.0.0-next.25": { + "integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==", + "dependencies": {} + }, + "@rollup/rollup-android-arm-eabi@4.21.1": { + "integrity": "sha512-2thheikVEuU7ZxFXubPDOtspKn1x0yqaYQwvALVtEcvFhMifPADBrgRPyHV0TF3b+9BgvgjgagVyvA/UqPZHmg==", + "dependencies": {} + }, + "@rollup/rollup-android-arm64@4.21.1": { + "integrity": "sha512-t1lLYn4V9WgnIFHXy1d2Di/7gyzBWS8G5pQSXdZqfrdCGTwi1VasRMSS81DTYb+avDs/Zz4A6dzERki5oRYz1g==", + "dependencies": {} + }, + "@rollup/rollup-darwin-arm64@4.21.1": { + "integrity": "sha512-AH/wNWSEEHvs6t4iJ3RANxW5ZCK3fUnmf0gyMxWCesY1AlUj8jY7GC+rQE4wd3gwmZ9XDOpL0kcFnCjtN7FXlA==", + "dependencies": {} + }, + "@rollup/rollup-darwin-x64@4.21.1": { + "integrity": "sha512-dO0BIz/+5ZdkLZrVgQrDdW7m2RkrLwYTh2YMFG9IpBtlC1x1NPNSXkfczhZieOlOLEqgXOFH3wYHB7PmBtf+Bg==", + "dependencies": {} + }, + "@rollup/rollup-linux-arm-gnueabihf@4.21.1": { + "integrity": "sha512-sWWgdQ1fq+XKrlda8PsMCfut8caFwZBmhYeoehJ05FdI0YZXk6ZyUjWLrIgbR/VgiGycrFKMMgp7eJ69HOF2pQ==", + "dependencies": {} + }, + "@rollup/rollup-linux-arm-musleabihf@4.21.1": { + "integrity": "sha512-9OIiSuj5EsYQlmwhmFRA0LRO0dRRjdCVZA3hnmZe1rEwRk11Jy3ECGGq3a7RrVEZ0/pCsYWx8jG3IvcrJ6RCew==", + "dependencies": {} + }, + "@rollup/rollup-linux-arm64-gnu@4.21.1": { + "integrity": "sha512-0kuAkRK4MeIUbzQYu63NrJmfoUVicajoRAL1bpwdYIYRcs57iyIV9NLcuyDyDXE2GiZCL4uhKSYAnyWpjZkWow==", + "dependencies": {} + }, + "@rollup/rollup-linux-arm64-musl@4.21.1": { + "integrity": "sha512-/6dYC9fZtfEY0vozpc5bx1RP4VrtEOhNQGb0HwvYNwXD1BBbwQ5cKIbUVVU7G2d5WRE90NfB922elN8ASXAJEA==", + "dependencies": {} + }, + "@rollup/rollup-linux-powerpc64le-gnu@4.21.1": { + "integrity": "sha512-ltUWy+sHeAh3YZ91NUsV4Xg3uBXAlscQe8ZOXRCVAKLsivGuJsrkawYPUEyCV3DYa9urgJugMLn8Z3Z/6CeyRQ==", + "dependencies": {} + }, + "@rollup/rollup-linux-riscv64-gnu@4.21.1": { + "integrity": "sha512-BggMndzI7Tlv4/abrgLwa/dxNEMn2gC61DCLrTzw8LkpSKel4o+O+gtjbnkevZ18SKkeN3ihRGPuBxjaetWzWg==", + "dependencies": {} + }, + "@rollup/rollup-linux-s390x-gnu@4.21.1": { + "integrity": "sha512-z/9rtlGd/OMv+gb1mNSjElasMf9yXusAxnRDrBaYB+eS1shFm6/4/xDH1SAISO5729fFKUkJ88TkGPRUh8WSAA==", + "dependencies": {} + }, + "@rollup/rollup-linux-x64-gnu@4.21.1": { + "integrity": "sha512-kXQVcWqDcDKw0S2E0TmhlTLlUgAmMVqPrJZR+KpH/1ZaZhLSl23GZpQVmawBQGVhyP5WXIsIQ/zqbDBBYmxm5w==", + "dependencies": {} + }, + "@rollup/rollup-linux-x64-musl@4.21.1": { + "integrity": "sha512-CbFv/WMQsSdl+bpX6rVbzR4kAjSSBuDgCqb1l4J68UYsQNalz5wOqLGYj4ZI0thGpyX5kc+LLZ9CL+kpqDovZA==", + "dependencies": {} + }, + "@rollup/rollup-win32-arm64-msvc@4.21.1": { + "integrity": "sha512-3Q3brDgA86gHXWHklrwdREKIrIbxC0ZgU8lwpj0eEKGBQH+31uPqr0P2v11pn0tSIxHvcdOWxa4j+YvLNx1i6g==", + "dependencies": {} + }, + "@rollup/rollup-win32-ia32-msvc@4.21.1": { + "integrity": "sha512-tNg+jJcKR3Uwe4L0/wY3Ro0H+u3nrb04+tcq1GSYzBEmKLeOQF2emk1whxlzNqb6MMrQ2JOcQEpuuiPLyRcSIw==", + "dependencies": {} + }, + "@rollup/rollup-win32-x64-msvc@4.21.1": { + "integrity": "sha512-xGiIH95H1zU7naUyTKEyOA/I0aexNMUdO9qRv0bLKN3qu25bBdrxZHqA3PTJ24YNN/GdMzG4xkDcd/GvjuhfLg==", + "dependencies": {} + }, + "@testing-library/dom@10.4.0": { + "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", + "dependencies": { + "@babel/code-frame": "@babel/code-frame@7.24.7", + "@babel/runtime": "@babel/runtime@7.25.6", + "@types/aria-query": "@types/aria-query@5.0.4", + "aria-query": "aria-query@5.3.0", + "chalk": "chalk@4.1.2", + "dom-accessibility-api": "dom-accessibility-api@0.5.16", + "lz-string": "lz-string@1.5.0", + "pretty-format": "pretty-format@27.5.1" + } + }, + "@testing-library/user-event@14.5.2_@testing-library+dom@10.4.0": { + "integrity": "sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==", + "dependencies": { + "@testing-library/dom": "@testing-library/dom@10.4.0" + } + }, + "@total-typescript/tsconfig@1.0.4": { + "integrity": "sha512-fO4ctMPGz1kOFOQ4RCPBRBfMy3gDn+pegUfrGyUFRMv/Rd0ZM3/SHH3hFCYG4u6bPLG8OlmOGcBLDexvyr3A5w==", + "dependencies": {} + }, + "@types/aria-query@5.0.4": { + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dependencies": {} + }, + "@types/cookie@0.6.0": { + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "dependencies": {} + }, + "@types/estree@1.0.5": { + "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", + "dependencies": {} + }, + "@types/mute-stream@0.0.4": { + "integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==", + "dependencies": { + "@types/node": "@types/node@18.16.19" + } + }, + "@types/node@18.16.19": { + "integrity": "sha512-IXl7o+R9iti9eBW4Wg2hx1xQDig183jj7YLn8F7udNceyfkbn1ZxmzZXuak20gR40D7pIkIY1kYGx5VIGbaHKA==", + "dependencies": {} + }, + "@types/node@20.16.2": { + "integrity": "sha512-91s/n4qUPV/wg8eE9KHYW1kouTfDk2FPGjXbBMfRWP/2vg1rCXNQL1OCabwGs0XSdukuK+MwCDXE30QpSeMUhQ==", + "dependencies": { + "undici-types": "undici-types@6.19.8" + } + }, + "@types/node@22.5.4": { + "integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==", + "dependencies": { + "undici-types": "undici-types@6.19.8" + } + }, + "@types/pg@8.11.8": { + "integrity": "sha512-IqpCf8/569txXN/HoP5i1LjXfKZWL76Yr2R77xgeIICUbAYHeoaEZFhYHo2uDftecLWrTJUq63JvQu8q3lnDyA==", + "dependencies": { + "@types/node": "@types/node@18.16.19", + "pg-protocol": "pg-protocol@1.6.1", + "pg-types": "pg-types@4.0.2" + } + }, + "@types/statuses@2.0.5": { + "integrity": "sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==", + "dependencies": {} + }, + "@types/tough-cookie@4.0.5": { + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dependencies": {} + }, + "@types/wrap-ansi@3.0.0": { + "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==", + "dependencies": {} + }, + "@vitest/browser@2.0.5_playwright@1.47.0_vitest@2.0.5__@types+node@18.16.19_@testing-library+dom@10.4.0_typescript@5.5.4_@types+node@18.16.19": { + "integrity": "sha512-VbOYtu/6R3d7ASZREcrJmRY/sQuRFO9wMVsEDqfYbWiJRh2fDNi8CL1Csn7Ux31pOcPmmM5QvzFCMpiojvVh8g==", + "dependencies": { + "@testing-library/dom": "@testing-library/dom@10.4.0", + "@testing-library/user-event": "@testing-library/user-event@14.5.2_@testing-library+dom@10.4.0", + "@vitest/utils": "@vitest/utils@2.0.5", + "magic-string": "magic-string@0.30.11", + "msw": "msw@2.4.5_typescript@5.5.4", + "playwright": "playwright@1.47.0", + "sirv": "sirv@2.0.4", + "vitest": "vitest@2.0.5_@types+node@18.16.19_@vitest+browser@2.0.5__playwright@1.47.0__vitest@2.0.5___@types+node@18.16.19__@testing-library+dom@10.4.0__typescript@5.5.4__@types+node@18.16.19", + "ws": "ws@8.18.0" + } + }, + "@vitest/expect@2.0.5": { + "integrity": "sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==", + "dependencies": { + "@vitest/spy": "@vitest/spy@2.0.5", + "@vitest/utils": "@vitest/utils@2.0.5", + "chai": "chai@5.1.1", + "tinyrainbow": "tinyrainbow@1.2.0" + } + }, + "@vitest/pretty-format@2.0.5": { + "integrity": "sha512-h8k+1oWHfwTkyTkb9egzwNMfJAEx4veaPSnMeKbVSjp4euqGSbQlm5+6VHwTr7u4FJslVVsUG5nopCaAYdOmSQ==", + "dependencies": { + "tinyrainbow": "tinyrainbow@1.2.0" + } + }, + "@vitest/runner@2.0.5": { + "integrity": "sha512-TfRfZa6Bkk9ky4tW0z20WKXFEwwvWhRY+84CnSEtq4+3ZvDlJyY32oNTJtM7AW9ihW90tX/1Q78cb6FjoAs+ig==", + "dependencies": { + "@vitest/utils": "@vitest/utils@2.0.5", + "pathe": "pathe@1.1.2" + } + }, + "@vitest/snapshot@2.0.5": { + "integrity": "sha512-SgCPUeDFLaM0mIUHfaArq8fD2WbaXG/zVXjRupthYfYGzc8ztbFbu6dUNOblBG7XLMR1kEhS/DNnfCZ2IhdDew==", + "dependencies": { + "@vitest/pretty-format": "@vitest/pretty-format@2.0.5", + "magic-string": "magic-string@0.30.11", + "pathe": "pathe@1.1.2" + } + }, + "@vitest/spy@2.0.5": { + "integrity": "sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA==", + "dependencies": { + "tinyspy": "tinyspy@3.0.0" + } + }, + "@vitest/utils@2.0.5": { + "integrity": "sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==", + "dependencies": { + "@vitest/pretty-format": "@vitest/pretty-format@2.0.5", + "estree-walker": "estree-walker@3.0.3", + "loupe": "loupe@3.1.1", + "tinyrainbow": "tinyrainbow@1.2.0" + } + }, + "ansi-escapes@4.3.2": { + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dependencies": { + "type-fest": "type-fest@0.21.3" + } + }, + "ansi-regex@5.0.1": { + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dependencies": {} + }, + "ansi-regex@6.0.1": { + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dependencies": {} + }, + "ansi-styles@3.2.1": { + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "color-convert@1.9.3" + } + }, + "ansi-styles@4.3.0": { + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "color-convert@2.0.1" + } + }, + "ansi-styles@5.2.0": { + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dependencies": {} + }, + "ansi-styles@6.2.1": { + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dependencies": {} + }, + "any-promise@1.3.0": { + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dependencies": {} + }, + "anymatch@3.1.3": { + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "normalize-path@3.0.0", + "picomatch": "picomatch@2.3.1" + } + }, + "aria-query@5.3.0": { + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dependencies": { + "dequal": "dequal@2.0.3" + } + }, + "array-union@2.1.0": { + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dependencies": {} + }, + "asn1js@3.0.5": { + "integrity": "sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==", + "dependencies": { + "pvtsutils": "pvtsutils@1.3.5", + "pvutils": "pvutils@1.1.3", + "tslib": "tslib@2.7.0" + } + }, + "assertion-error@2.0.1": { + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dependencies": {} + }, + "balanced-match@1.0.2": { + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dependencies": {} + }, + "binary-extensions@2.3.0": { + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dependencies": {} + }, + "brace-expansion@2.0.1": { + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "balanced-match@1.0.2" + } + }, + "braces@3.0.3": { + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dependencies": { + "fill-range": "fill-range@7.1.1" + } + }, + "bundle-require@5.0.0_esbuild@0.23.1": { + "integrity": "sha512-GuziW3fSSmopcx4KRymQEJVbZUfqlCqcq7dvs6TYwKRZiegK/2buMxQTPs6MGlNv50wms1699qYO54R8XfRX4w==", + "dependencies": { + "esbuild": "esbuild@0.23.1", + "load-tsconfig": "load-tsconfig@0.2.5" + } + }, + "bytestreamjs@2.0.1": { + "integrity": "sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==", + "dependencies": {} + }, + "cac@6.7.14": { + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dependencies": {} + }, + "chai@5.1.1": { + "integrity": "sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==", + "dependencies": { + "assertion-error": "assertion-error@2.0.1", + "check-error": "check-error@2.1.1", + "deep-eql": "deep-eql@5.0.2", + "loupe": "loupe@3.1.1", + "pathval": "pathval@2.0.0" + } + }, + "chalk@2.4.2": { + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "ansi-styles@3.2.1", + "escape-string-regexp": "escape-string-regexp@1.0.5", + "supports-color": "supports-color@5.5.0" + } + }, + "chalk@4.1.2": { + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "ansi-styles@4.3.0", + "supports-color": "supports-color@7.2.0" + } + }, + "check-error@2.1.1": { + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", + "dependencies": {} + }, + "chokidar@3.6.0": { + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dependencies": { + "anymatch": "anymatch@3.1.3", + "braces": "braces@3.0.3", + "fsevents": "fsevents@2.3.3", + "glob-parent": "glob-parent@5.1.2", + "is-binary-path": "is-binary-path@2.1.0", + "is-glob": "is-glob@4.0.3", + "normalize-path": "normalize-path@3.0.0", + "readdirp": "readdirp@3.6.0" + } + }, + "cli-spinners@2.9.2": { + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dependencies": {} + }, + "cli-width@4.1.0": { + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dependencies": {} + }, + "cliui@8.0.1": { + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dependencies": { + "string-width": "string-width@4.2.3", + "strip-ansi": "strip-ansi@6.0.1", + "wrap-ansi": "wrap-ansi@7.0.0" + } + }, + "color-convert@1.9.3": { + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "color-name@1.1.3" + } + }, + "color-convert@2.0.1": { + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "color-name@1.1.4" + } + }, + "color-name@1.1.3": { + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dependencies": {} + }, + "color-name@1.1.4": { + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dependencies": {} + }, + "commander@4.1.1": { + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dependencies": {} + }, + "consola@3.2.3": { + "integrity": "sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==", + "dependencies": {} + }, + "cookie@0.5.0": { + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dependencies": {} + }, + "cross-spawn@7.0.3": { + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dependencies": { + "path-key": "path-key@3.1.1", + "shebang-command": "shebang-command@2.0.0", + "which": "which@2.0.2" + } + }, + "debug@4.3.6": { + "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", + "dependencies": { + "ms": "ms@2.1.2" + } + }, + "deep-eql@5.0.2": { + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dependencies": {} + }, + "dequal@2.0.3": { + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dependencies": {} + }, + "dir-glob@3.0.1": { + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dependencies": { + "path-type": "path-type@4.0.0" + } + }, + "dom-accessibility-api@0.5.16": { + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dependencies": {} + }, + "eastasianwidth@0.2.0": { + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dependencies": {} + }, + "emoji-regex@8.0.0": { + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dependencies": {} + }, + "emoji-regex@9.2.2": { + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dependencies": {} + }, + "esbuild@0.21.5": { + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dependencies": { + "@esbuild/aix-ppc64": "@esbuild/aix-ppc64@0.21.5", + "@esbuild/android-arm": "@esbuild/android-arm@0.21.5", + "@esbuild/android-arm64": "@esbuild/android-arm64@0.21.5", + "@esbuild/android-x64": "@esbuild/android-x64@0.21.5", + "@esbuild/darwin-arm64": "@esbuild/darwin-arm64@0.21.5", + "@esbuild/darwin-x64": "@esbuild/darwin-x64@0.21.5", + "@esbuild/freebsd-arm64": "@esbuild/freebsd-arm64@0.21.5", + "@esbuild/freebsd-x64": "@esbuild/freebsd-x64@0.21.5", + "@esbuild/linux-arm": "@esbuild/linux-arm@0.21.5", + "@esbuild/linux-arm64": "@esbuild/linux-arm64@0.21.5", + "@esbuild/linux-ia32": "@esbuild/linux-ia32@0.21.5", + "@esbuild/linux-loong64": "@esbuild/linux-loong64@0.21.5", + "@esbuild/linux-mips64el": "@esbuild/linux-mips64el@0.21.5", + "@esbuild/linux-ppc64": "@esbuild/linux-ppc64@0.21.5", + "@esbuild/linux-riscv64": "@esbuild/linux-riscv64@0.21.5", + "@esbuild/linux-s390x": "@esbuild/linux-s390x@0.21.5", + "@esbuild/linux-x64": "@esbuild/linux-x64@0.21.5", + "@esbuild/netbsd-x64": "@esbuild/netbsd-x64@0.21.5", + "@esbuild/openbsd-x64": "@esbuild/openbsd-x64@0.21.5", + "@esbuild/sunos-x64": "@esbuild/sunos-x64@0.21.5", + "@esbuild/win32-arm64": "@esbuild/win32-arm64@0.21.5", + "@esbuild/win32-ia32": "@esbuild/win32-ia32@0.21.5", + "@esbuild/win32-x64": "@esbuild/win32-x64@0.21.5" + } + }, + "esbuild@0.23.1": { + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", + "dependencies": { + "@esbuild/aix-ppc64": "@esbuild/aix-ppc64@0.23.1", + "@esbuild/android-arm": "@esbuild/android-arm@0.23.1", + "@esbuild/android-arm64": "@esbuild/android-arm64@0.23.1", + "@esbuild/android-x64": "@esbuild/android-x64@0.23.1", + "@esbuild/darwin-arm64": "@esbuild/darwin-arm64@0.23.1", + "@esbuild/darwin-x64": "@esbuild/darwin-x64@0.23.1", + "@esbuild/freebsd-arm64": "@esbuild/freebsd-arm64@0.23.1", + "@esbuild/freebsd-x64": "@esbuild/freebsd-x64@0.23.1", + "@esbuild/linux-arm": "@esbuild/linux-arm@0.23.1", + "@esbuild/linux-arm64": "@esbuild/linux-arm64@0.23.1", + "@esbuild/linux-ia32": "@esbuild/linux-ia32@0.23.1", + "@esbuild/linux-loong64": "@esbuild/linux-loong64@0.23.1", + "@esbuild/linux-mips64el": "@esbuild/linux-mips64el@0.23.1", + "@esbuild/linux-ppc64": "@esbuild/linux-ppc64@0.23.1", + "@esbuild/linux-riscv64": "@esbuild/linux-riscv64@0.23.1", + "@esbuild/linux-s390x": "@esbuild/linux-s390x@0.23.1", + "@esbuild/linux-x64": "@esbuild/linux-x64@0.23.1", + "@esbuild/netbsd-x64": "@esbuild/netbsd-x64@0.23.1", + "@esbuild/openbsd-arm64": "@esbuild/openbsd-arm64@0.23.1", + "@esbuild/openbsd-x64": "@esbuild/openbsd-x64@0.23.1", + "@esbuild/sunos-x64": "@esbuild/sunos-x64@0.23.1", + "@esbuild/win32-arm64": "@esbuild/win32-arm64@0.23.1", + "@esbuild/win32-ia32": "@esbuild/win32-ia32@0.23.1", + "@esbuild/win32-x64": "@esbuild/win32-x64@0.23.1" + } + }, + "escalade@3.2.0": { + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dependencies": {} + }, + "escape-string-regexp@1.0.5": { + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dependencies": {} + }, + "estree-walker@3.0.3": { + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dependencies": { + "@types/estree": "@types/estree@1.0.5" + } + }, + "execa@5.1.1": { + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", + "dependencies": { + "cross-spawn": "cross-spawn@7.0.3", + "get-stream": "get-stream@6.0.1", + "human-signals": "human-signals@2.1.0", + "is-stream": "is-stream@2.0.1", + "merge-stream": "merge-stream@2.0.0", + "npm-run-path": "npm-run-path@4.0.1", + "onetime": "onetime@5.1.2", + "signal-exit": "signal-exit@3.0.7", + "strip-final-newline": "strip-final-newline@2.0.0" + } + }, + "execa@8.0.1": { + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dependencies": { + "cross-spawn": "cross-spawn@7.0.3", + "get-stream": "get-stream@8.0.1", + "human-signals": "human-signals@5.0.0", + "is-stream": "is-stream@3.0.0", + "merge-stream": "merge-stream@2.0.0", + "npm-run-path": "npm-run-path@5.3.0", + "onetime": "onetime@6.0.0", + "signal-exit": "signal-exit@4.1.0", + "strip-final-newline": "strip-final-newline@3.0.0" + } + }, + "fast-glob@3.3.2": { + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dependencies": { + "@nodelib/fs.stat": "@nodelib/fs.stat@2.0.5", + "@nodelib/fs.walk": "@nodelib/fs.walk@1.2.8", + "glob-parent": "glob-parent@5.1.2", + "merge2": "merge2@1.4.1", + "micromatch": "micromatch@4.0.8" + } + }, + "fastq@1.17.1": { + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", + "dependencies": { + "reusify": "reusify@1.0.4" + } + }, + "fill-range@7.1.1": { + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dependencies": { + "to-regex-range": "to-regex-range@5.0.1" + } + }, + "foreground-child@3.3.0": { + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dependencies": { + "cross-spawn": "cross-spawn@7.0.3", + "signal-exit": "signal-exit@4.1.0" + } + }, + "fsevents@2.3.2": { + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dependencies": {} + }, + "fsevents@2.3.3": { + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dependencies": {} + }, + "get-caller-file@2.0.5": { + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dependencies": {} + }, + "get-func-name@2.0.2": { + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dependencies": {} + }, + "get-stream@6.0.1": { + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", + "dependencies": {} + }, + "get-stream@8.0.1": { + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "dependencies": {} + }, + "get-tsconfig@4.8.0": { + "integrity": "sha512-Pgba6TExTZ0FJAn1qkJAjIeKoDJ3CsI2ChuLohJnZl/tTU8MVrq3b+2t5UOPfRa4RMsorClBjJALkJUMjG1PAw==", + "dependencies": { + "resolve-pkg-maps": "resolve-pkg-maps@1.0.0" + } + }, + "glob-parent@5.1.2": { + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "is-glob@4.0.3" + } + }, + "glob@10.4.5": { + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dependencies": { + "foreground-child": "foreground-child@3.3.0", + "jackspeak": "jackspeak@3.4.3", + "minimatch": "minimatch@9.0.5", + "minipass": "minipass@7.1.2", + "package-json-from-dist": "package-json-from-dist@1.0.0", + "path-scurry": "path-scurry@1.11.1" + } + }, + "globby@11.1.0": { + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dependencies": { + "array-union": "array-union@2.1.0", + "dir-glob": "dir-glob@3.0.1", + "fast-glob": "fast-glob@3.3.2", + "ignore": "ignore@5.3.2", + "merge2": "merge2@1.4.1", + "slash": "slash@3.0.0" + } + }, + "graphql@16.9.0": { + "integrity": "sha512-GGTKBX4SD7Wdb8mqeDLni2oaRGYQWjWHGKPQ24ZMnUtKfcsVoiv4uX8+LJr1K6U5VW2Lu1BwJnj7uiori0YtRw==", + "dependencies": {} + }, + "has-flag@3.0.0": { + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dependencies": {} + }, + "has-flag@4.0.0": { + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dependencies": {} + }, + "headers-polyfill@4.0.3": { + "integrity": "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==", + "dependencies": {} + }, + "human-signals@2.1.0": { + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dependencies": {} + }, + "human-signals@5.0.0": { + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "dependencies": {} + }, + "ignore@5.3.2": { + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dependencies": {} + }, + "is-binary-path@2.1.0": { + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "binary-extensions@2.3.0" + } + }, + "is-extglob@2.1.1": { + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dependencies": {} + }, + "is-fullwidth-code-point@3.0.0": { + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dependencies": {} + }, + "is-glob@4.0.3": { + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "is-extglob@2.1.1" + } + }, + "is-node-process@1.2.0": { + "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", + "dependencies": {} + }, + "is-number@7.0.0": { + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dependencies": {} + }, + "is-stream@2.0.1": { + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dependencies": {} + }, + "is-stream@3.0.0": { + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "dependencies": {} + }, + "isexe@2.0.0": { + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dependencies": {} + }, + "jackspeak@3.4.3": { + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dependencies": { + "@isaacs/cliui": "@isaacs/cliui@8.0.2", + "@pkgjs/parseargs": "@pkgjs/parseargs@0.11.0" + } + }, + "joycon@3.1.1": { + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "dependencies": {} + }, + "js-tokens@4.0.0": { + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dependencies": {} + }, + "lilconfig@3.1.2": { + "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", + "dependencies": {} + }, + "lines-and-columns@1.2.4": { + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dependencies": {} + }, + "load-tsconfig@0.2.5": { + "integrity": "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==", + "dependencies": {} + }, + "lodash.sortby@4.7.0": { + "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", + "dependencies": {} + }, + "loupe@3.1.1": { + "integrity": "sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==", + "dependencies": { + "get-func-name": "get-func-name@2.0.2" + } + }, + "lru-cache@10.4.3": { + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dependencies": {} + }, + "lz-string@1.5.0": { + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dependencies": {} + }, + "magic-string@0.30.11": { + "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", + "dependencies": { + "@jridgewell/sourcemap-codec": "@jridgewell/sourcemap-codec@1.5.0" + } + }, + "merge-stream@2.0.0": { + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dependencies": {} + }, + "merge2@1.4.1": { + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dependencies": {} + }, + "micromatch@4.0.8": { + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dependencies": { + "braces": "braces@3.0.3", + "picomatch": "picomatch@2.3.1" + } + }, + "mimic-fn@2.1.0": { + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dependencies": {} + }, + "mimic-fn@4.0.0": { + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "dependencies": {} + }, + "minimatch@9.0.5": { + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dependencies": { + "brace-expansion": "brace-expansion@2.0.1" + } + }, + "minipass@7.1.2": { + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dependencies": {} + }, + "mrmime@2.0.0": { + "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", + "dependencies": {} + }, + "ms@2.1.2": { + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dependencies": {} + }, + "msw@2.4.5_typescript@5.5.4": { + "integrity": "sha512-lX7knS9us1EKs1Y/ihB4eYEJ9VCq+ZGezrPSspbRw0iEaoOn1q0vZkR0joZ6w1S4u46ZD+VLJ522EQihIIkRCg==", + "dependencies": { + "@bundled-es-modules/cookie": "@bundled-es-modules/cookie@2.0.0", + "@bundled-es-modules/statuses": "@bundled-es-modules/statuses@1.0.1", + "@bundled-es-modules/tough-cookie": "@bundled-es-modules/tough-cookie@0.1.6", + "@inquirer/confirm": "@inquirer/confirm@3.2.0", + "@mswjs/interceptors": "@mswjs/interceptors@0.35.1", + "@open-draft/until": "@open-draft/until@2.1.0", + "@types/cookie": "@types/cookie@0.6.0", + "@types/statuses": "@types/statuses@2.0.5", + "chalk": "chalk@4.1.2", + "graphql": "graphql@16.9.0", + "headers-polyfill": "headers-polyfill@4.0.3", + "is-node-process": "is-node-process@1.2.0", + "outvariant": "outvariant@1.4.3", + "path-to-regexp": "path-to-regexp@6.2.2", + "strict-event-emitter": "strict-event-emitter@0.5.1", + "type-fest": "type-fest@4.26.1", + "typescript": "typescript@5.5.4", + "yargs": "yargs@17.7.2" + } + }, + "mute-stream@1.0.0": { + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", + "dependencies": {} + }, + "mz@2.7.0": { + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dependencies": { + "any-promise": "any-promise@1.3.0", + "object-assign": "object-assign@4.1.1", + "thenify-all": "thenify-all@1.6.0" + } + }, + "nanoid@3.3.7": { + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dependencies": {} + }, + "normalize-path@3.0.0": { + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dependencies": {} + }, + "npm-run-path@4.0.1": { + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", + "dependencies": { + "path-key": "path-key@3.1.1" + } + }, + "npm-run-path@5.3.0": { + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "dependencies": { + "path-key": "path-key@4.0.0" + } + }, + "object-assign@4.1.1": { + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dependencies": {} + }, + "obuf@1.1.2": { + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dependencies": {} + }, + "onetime@5.1.2": { + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dependencies": { + "mimic-fn": "mimic-fn@2.1.0" + } + }, + "onetime@6.0.0": { + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "dependencies": { + "mimic-fn": "mimic-fn@4.0.0" + } + }, + "outvariant@1.4.3": { + "integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==", + "dependencies": {} + }, + "package-json-from-dist@1.0.0": { + "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", + "dependencies": {} + }, + "path-key@3.1.1": { + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dependencies": {} + }, + "path-key@4.0.0": { + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "dependencies": {} + }, + "path-scurry@1.11.1": { + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dependencies": { + "lru-cache": "lru-cache@10.4.3", + "minipass": "minipass@7.1.2" + } + }, + "path-to-regexp@6.2.2": { + "integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==", + "dependencies": {} + }, + "path-type@4.0.0": { + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dependencies": {} + }, + "pathe@1.1.2": { + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dependencies": {} + }, + "pathval@2.0.0": { + "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", + "dependencies": {} + }, + "pg-cloudflare@1.1.1": { + "integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==", + "dependencies": {} + }, + "pg-connection-string@2.6.4": { + "integrity": "sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA==", + "dependencies": {} + }, + "pg-gateway@0.3.0-alpha.6": { + "integrity": "sha512-Av2ujUOokVw1O+P4AtBYek4Np7/2O7bZ9clRVYlCPCeD+NbHj026u+uoiropipsFbf7QyQktJylqbz1aRPGOfA==", + "dependencies": {} + }, + "pg-int8@1.0.1": { + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "dependencies": {} + }, + "pg-numeric@1.0.2": { + "integrity": "sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==", + "dependencies": {} + }, + "pg-pool@3.6.2_pg@8.12.0": { + "integrity": "sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==", + "dependencies": { + "pg": "pg@8.12.0" + } + }, + "pg-protocol@1.6.1": { + "integrity": "sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg==", + "dependencies": {} + }, + "pg-types@2.2.0": { + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dependencies": { + "pg-int8": "pg-int8@1.0.1", + "postgres-array": "postgres-array@2.0.0", + "postgres-bytea": "postgres-bytea@1.0.0", + "postgres-date": "postgres-date@1.0.7", + "postgres-interval": "postgres-interval@1.2.0" + } + }, + "pg-types@4.0.2": { + "integrity": "sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==", + "dependencies": { + "pg-int8": "pg-int8@1.0.1", + "pg-numeric": "pg-numeric@1.0.2", + "postgres-array": "postgres-array@3.0.2", + "postgres-bytea": "postgres-bytea@3.0.0", + "postgres-date": "postgres-date@2.1.0", + "postgres-interval": "postgres-interval@3.0.0", + "postgres-range": "postgres-range@1.1.4" + } + }, + "pg@8.12.0": { + "integrity": "sha512-A+LHUSnwnxrnL/tZ+OLfqR1SxLN3c/pgDztZ47Rpbsd4jUytsTtwQo/TLPRzPJMp/1pbhYVhH9cuSZLAajNfjQ==", + "dependencies": { + "pg-cloudflare": "pg-cloudflare@1.1.1", + "pg-connection-string": "pg-connection-string@2.6.4", + "pg-pool": "pg-pool@3.6.2_pg@8.12.0", + "pg-protocol": "pg-protocol@1.6.1", + "pg-types": "pg-types@2.2.0", + "pgpass": "pgpass@1.0.5" + } + }, + "pgpass@1.0.5": { + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "dependencies": { + "split2": "split2@4.2.0" + } + }, + "picocolors@1.0.1": { + "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "dependencies": {} + }, + "picomatch@2.3.1": { + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dependencies": {} + }, + "pirates@4.0.6": { + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dependencies": {} + }, + "pkijs@3.2.4": { + "integrity": "sha512-Et9V5QpvBilPFgagJcaKBqXjKrrgF5JL2mSDELk1vvbOTt4fuBhSSsGn9Tcz0TQTfS5GCpXQ31Whrpqeqp0VRg==", + "dependencies": { + "@noble/hashes": "@noble/hashes@1.5.0", + "asn1js": "asn1js@3.0.5", + "bytestreamjs": "bytestreamjs@2.0.1", + "pvtsutils": "pvtsutils@1.3.5", + "pvutils": "pvutils@1.1.3", + "tslib": "tslib@2.7.0" + } + }, + "playwright-core@1.47.0": { + "integrity": "sha512-1DyHT8OqkcfCkYUD9zzUTfg7EfTd+6a8MkD/NWOvjo0u/SCNd5YmY/lJwFvUZOxJbWNds+ei7ic2+R/cRz/PDg==", + "dependencies": {} + }, + "playwright@1.47.0": { + "integrity": "sha512-jOWiRq2pdNAX/mwLiwFYnPHpEZ4rM+fRSQpRHwEwZlP2PUANvL3+aJOF/bvISMhFD30rqMxUB4RJx9aQbfh4Ww==", + "dependencies": { + "fsevents": "fsevents@2.3.2", + "playwright-core": "playwright-core@1.47.0" + } + }, + "postcss-load-config@6.0.1_tsx@4.19.0": { + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dependencies": { + "lilconfig": "lilconfig@3.1.2", + "tsx": "tsx@4.19.0" + } + }, + "postcss@8.4.41": { + "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", + "dependencies": { + "nanoid": "nanoid@3.3.7", + "picocolors": "picocolors@1.0.1", + "source-map-js": "source-map-js@1.2.0" + } + }, + "postgres-array@2.0.0": { + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "dependencies": {} + }, + "postgres-array@3.0.2": { + "integrity": "sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==", + "dependencies": {} + }, + "postgres-bytea@1.0.0": { + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "dependencies": {} + }, + "postgres-bytea@3.0.0": { + "integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==", + "dependencies": { + "obuf": "obuf@1.1.2" + } + }, + "postgres-date@1.0.7": { + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "dependencies": {} + }, + "postgres-date@2.1.0": { + "integrity": "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==", + "dependencies": {} + }, + "postgres-interval@1.2.0": { + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dependencies": { + "xtend": "xtend@4.0.2" + } + }, + "postgres-interval@3.0.0": { + "integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==", + "dependencies": {} + }, + "postgres-range@1.1.4": { + "integrity": "sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==", + "dependencies": {} + }, + "postgres@3.4.4": { + "integrity": "sha512-IbyN+9KslkqcXa8AO9fxpk97PA4pzewvpi2B3Dwy9u4zpV32QicaEdgmF3eSQUzdRk7ttDHQejNgAEr4XoeH4A==", + "dependencies": {} + }, + "pretty-format@27.5.1": { + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dependencies": { + "ansi-regex": "ansi-regex@5.0.1", + "ansi-styles": "ansi-styles@5.2.0", + "react-is": "react-is@17.0.2" + } + }, + "psl@1.9.0": { + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "dependencies": {} + }, + "punycode@2.3.1": { + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dependencies": {} + }, + "pvtsutils@1.3.5": { + "integrity": "sha512-ARvb14YB9Nm2Xi6nBq1ZX6dAM0FsJnuk+31aUp4TrcZEdKUlSqOqsxJHUPJDNE3qiIp+iUPEIeR6Je/tgV7zsA==", + "dependencies": { + "tslib": "tslib@2.7.0" + } + }, + "pvutils@1.1.3": { + "integrity": "sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==", + "dependencies": {} + }, + "querystringify@2.2.0": { + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dependencies": {} + }, + "queue-microtask@1.2.3": { + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dependencies": {} + }, + "react-is@17.0.2": { + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dependencies": {} + }, + "readdirp@3.6.0": { + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "picomatch@2.3.1" + } + }, + "regenerator-runtime@0.14.1": { + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dependencies": {} + }, + "require-directory@2.1.1": { + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dependencies": {} + }, + "requires-port@1.0.0": { + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dependencies": {} + }, + "resolve-from@5.0.0": { + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "dependencies": {} + }, + "resolve-pkg-maps@1.0.0": { + "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", + "dependencies": {} + }, + "reusify@1.0.4": { + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dependencies": {} + }, + "rollup@4.21.1": { + "integrity": "sha512-ZnYyKvscThhgd3M5+Qt3pmhO4jIRR5RGzaSovB6Q7rGNrK5cUncrtLmcTTJVSdcKXyZjW8X8MB0JMSuH9bcAJg==", + "dependencies": { + "@rollup/rollup-android-arm-eabi": "@rollup/rollup-android-arm-eabi@4.21.1", + "@rollup/rollup-android-arm64": "@rollup/rollup-android-arm64@4.21.1", + "@rollup/rollup-darwin-arm64": "@rollup/rollup-darwin-arm64@4.21.1", + "@rollup/rollup-darwin-x64": "@rollup/rollup-darwin-x64@4.21.1", + "@rollup/rollup-linux-arm-gnueabihf": "@rollup/rollup-linux-arm-gnueabihf@4.21.1", + "@rollup/rollup-linux-arm-musleabihf": "@rollup/rollup-linux-arm-musleabihf@4.21.1", + "@rollup/rollup-linux-arm64-gnu": "@rollup/rollup-linux-arm64-gnu@4.21.1", + "@rollup/rollup-linux-arm64-musl": "@rollup/rollup-linux-arm64-musl@4.21.1", + "@rollup/rollup-linux-powerpc64le-gnu": "@rollup/rollup-linux-powerpc64le-gnu@4.21.1", + "@rollup/rollup-linux-riscv64-gnu": "@rollup/rollup-linux-riscv64-gnu@4.21.1", + "@rollup/rollup-linux-s390x-gnu": "@rollup/rollup-linux-s390x-gnu@4.21.1", + "@rollup/rollup-linux-x64-gnu": "@rollup/rollup-linux-x64-gnu@4.21.1", + "@rollup/rollup-linux-x64-musl": "@rollup/rollup-linux-x64-musl@4.21.1", + "@rollup/rollup-win32-arm64-msvc": "@rollup/rollup-win32-arm64-msvc@4.21.1", + "@rollup/rollup-win32-ia32-msvc": "@rollup/rollup-win32-ia32-msvc@4.21.1", + "@rollup/rollup-win32-x64-msvc": "@rollup/rollup-win32-x64-msvc@4.21.1", + "@types/estree": "@types/estree@1.0.5", + "fsevents": "fsevents@2.3.3" + } + }, + "run-parallel@1.2.0": { + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dependencies": { + "queue-microtask": "queue-microtask@1.2.3" + } + }, + "shebang-command@2.0.0": { + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dependencies": { + "shebang-regex": "shebang-regex@3.0.0" + } + }, + "shebang-regex@3.0.0": { + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dependencies": {} + }, + "siginfo@2.0.0": { + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dependencies": {} + }, + "signal-exit@3.0.7": { + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dependencies": {} + }, + "signal-exit@4.1.0": { + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dependencies": {} + }, + "sirv@2.0.4": { + "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", + "dependencies": { + "@polka/url": "@polka/url@1.0.0-next.25", + "mrmime": "mrmime@2.0.0", + "totalist": "totalist@3.0.1" + } + }, + "slash@3.0.0": { + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dependencies": {} + }, + "source-map-js@1.2.0": { + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "dependencies": {} + }, + "source-map@0.8.0-beta.0": { + "integrity": "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==", + "dependencies": { + "whatwg-url": "whatwg-url@7.1.0" + } + }, + "split2@4.2.0": { + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "dependencies": {} + }, + "stackback@0.0.2": { + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dependencies": {} + }, + "statuses@2.0.1": { + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dependencies": {} + }, + "std-env@3.7.0": { + "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==", + "dependencies": {} + }, + "strict-event-emitter@0.5.1": { + "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==", + "dependencies": {} + }, + "string-width@4.2.3": { + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "emoji-regex@8.0.0", + "is-fullwidth-code-point": "is-fullwidth-code-point@3.0.0", + "strip-ansi": "strip-ansi@6.0.1" + } + }, + "string-width@5.1.2": { + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dependencies": { + "eastasianwidth": "eastasianwidth@0.2.0", + "emoji-regex": "emoji-regex@9.2.2", + "strip-ansi": "strip-ansi@7.1.0" + } + }, + "strip-ansi@6.0.1": { + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "ansi-regex@5.0.1" + } + }, + "strip-ansi@7.1.0": { + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dependencies": { + "ansi-regex": "ansi-regex@6.0.1" + } + }, + "strip-final-newline@2.0.0": { + "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", + "dependencies": {} + }, + "strip-final-newline@3.0.0": { + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dependencies": {} + }, + "sucrase@3.35.0": { + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dependencies": { + "@jridgewell/gen-mapping": "@jridgewell/gen-mapping@0.3.5", + "commander": "commander@4.1.1", + "glob": "glob@10.4.5", + "lines-and-columns": "lines-and-columns@1.2.4", + "mz": "mz@2.7.0", + "pirates": "pirates@4.0.6", + "ts-interface-checker": "ts-interface-checker@0.1.13" + } + }, + "supports-color@5.5.0": { + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "has-flag@3.0.0" + } + }, + "supports-color@7.2.0": { + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "has-flag@4.0.0" + } + }, + "thenify-all@1.6.0": { + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dependencies": { + "thenify": "thenify@3.3.1" + } + }, + "thenify@3.3.1": { + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dependencies": { + "any-promise": "any-promise@1.3.0" + } + }, + "tinybench@2.9.0": { + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dependencies": {} + }, + "tinypool@1.0.1": { + "integrity": "sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==", + "dependencies": {} + }, + "tinyrainbow@1.2.0": { + "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", + "dependencies": {} + }, + "tinyspy@3.0.0": { + "integrity": "sha512-q5nmENpTHgiPVd1cJDDc9cVoYN5x4vCvwT3FMilvKPKneCBZAxn2YWQjDF0UMcE9k0Cay1gBiDfTMU0g+mPMQA==", + "dependencies": {} + }, + "to-regex-range@5.0.1": { + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "is-number@7.0.0" + } + }, + "totalist@3.0.1": { + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dependencies": {} + }, + "tough-cookie@4.1.4": { + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "dependencies": { + "psl": "psl@1.9.0", + "punycode": "punycode@2.3.1", + "universalify": "universalify@0.2.0", + "url-parse": "url-parse@1.5.10" + } + }, + "tr46@1.0.1": { + "integrity": "sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==", + "dependencies": { + "punycode": "punycode@2.3.1" + } + }, + "tree-kill@1.2.2": { + "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==", + "dependencies": {} + }, + "ts-interface-checker@0.1.13": { + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dependencies": {} + }, + "tslib@2.7.0": { + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "dependencies": {} + }, + "tsup@8.2.4_typescript@5.5.4_esbuild@0.23.1_tsx@4.19.0": { + "integrity": "sha512-akpCPePnBnC/CXgRrcy72ZSntgIEUa1jN0oJbbvpALWKNOz1B7aM+UVDWGRGIO/T/PZugAESWDJUAb5FD48o8Q==", + "dependencies": { + "bundle-require": "bundle-require@5.0.0_esbuild@0.23.1", + "cac": "cac@6.7.14", + "chokidar": "chokidar@3.6.0", + "consola": "consola@3.2.3", + "debug": "debug@4.3.6", + "esbuild": "esbuild@0.23.1", + "execa": "execa@5.1.1", + "globby": "globby@11.1.0", + "joycon": "joycon@3.1.1", + "picocolors": "picocolors@1.0.1", + "postcss-load-config": "postcss-load-config@6.0.1_tsx@4.19.0", + "resolve-from": "resolve-from@5.0.0", + "rollup": "rollup@4.21.1", + "source-map": "source-map@0.8.0-beta.0", + "sucrase": "sucrase@3.35.0", + "tree-kill": "tree-kill@1.2.2", + "typescript": "typescript@5.5.4" + } + }, + "tsx@4.19.0": { + "integrity": "sha512-bV30kM7bsLZKZIOCHeMNVMJ32/LuJzLVajkQI/qf92J2Qr08ueLQvW00PUZGiuLPP760UINwupgUj8qrSCPUKg==", + "dependencies": { + "esbuild": "esbuild@0.23.1", + "fsevents": "fsevents@2.3.3", + "get-tsconfig": "get-tsconfig@4.8.0" + } + }, + "type-fest@0.21.3": { + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dependencies": {} + }, + "type-fest@4.26.1": { + "integrity": "sha512-yOGpmOAL7CkKe/91I5O3gPICmJNLJ1G4zFYVAsRHg7M64biSnPtRj0WNQt++bRkjYOqjWXrhnUw1utzmVErAdg==", + "dependencies": {} + }, + "typescript@5.5.4": { + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "dependencies": {} + }, + "undici-types@6.19.8": { + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dependencies": {} + }, + "universalify@0.2.0": { + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dependencies": {} + }, + "url-parse@1.5.10": { + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dependencies": { + "querystringify": "querystringify@2.2.0", + "requires-port": "requires-port@1.0.0" + } + }, + "vite-node@2.0.5_@types+node@18.16.19": { + "integrity": "sha512-LdsW4pxj0Ot69FAoXZ1yTnA9bjGohr2yNBU7QKRxpz8ITSkhuDl6h3zS/tvgz4qrNjeRnvrWeXQ8ZF7Um4W00Q==", + "dependencies": { + "cac": "cac@6.7.14", + "debug": "debug@4.3.6", + "pathe": "pathe@1.1.2", + "tinyrainbow": "tinyrainbow@1.2.0", + "vite": "vite@5.4.2_@types+node@18.16.19" + } + }, + "vite@5.4.2_@types+node@18.16.19": { + "integrity": "sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==", + "dependencies": { + "@types/node": "@types/node@18.16.19", + "esbuild": "esbuild@0.21.5", + "fsevents": "fsevents@2.3.3", + "postcss": "postcss@8.4.41", + "rollup": "rollup@4.21.1" + } + }, + "vitest@2.0.5_@types+node@18.16.19": { + "integrity": "sha512-8GUxONfauuIdeSl5f9GTgVEpg5BTOlplET4WEDaeY2QBiN8wSm68vxN/tb5z405OwppfoCavnwXafiaYBC/xOA==", + "dependencies": { + "@ampproject/remapping": "@ampproject/remapping@2.3.0", + "@types/node": "@types/node@18.16.19", + "@vitest/expect": "@vitest/expect@2.0.5", + "@vitest/pretty-format": "@vitest/pretty-format@2.0.5", + "@vitest/runner": "@vitest/runner@2.0.5", + "@vitest/snapshot": "@vitest/snapshot@2.0.5", + "@vitest/spy": "@vitest/spy@2.0.5", + "@vitest/utils": "@vitest/utils@2.0.5", + "chai": "chai@5.1.1", + "debug": "debug@4.3.6", + "execa": "execa@8.0.1", + "magic-string": "magic-string@0.30.11", + "pathe": "pathe@1.1.2", + "std-env": "std-env@3.7.0", + "tinybench": "tinybench@2.9.0", + "tinypool": "tinypool@1.0.1", + "tinyrainbow": "tinyrainbow@1.2.0", + "vite": "vite@5.4.2_@types+node@18.16.19", + "vite-node": "vite-node@2.0.5_@types+node@18.16.19", + "why-is-node-running": "why-is-node-running@2.3.0" + } + }, + "vitest@2.0.5_@types+node@18.16.19_@vitest+browser@2.0.5__playwright@1.47.0__vitest@2.0.5___@types+node@18.16.19__@testing-library+dom@10.4.0__typescript@5.5.4__@types+node@18.16.19": { + "integrity": "sha512-8GUxONfauuIdeSl5f9GTgVEpg5BTOlplET4WEDaeY2QBiN8wSm68vxN/tb5z405OwppfoCavnwXafiaYBC/xOA==", + "dependencies": { + "@ampproject/remapping": "@ampproject/remapping@2.3.0", + "@types/node": "@types/node@18.16.19", + "@vitest/browser": "@vitest/browser@2.0.5_playwright@1.47.0_vitest@2.0.5__@types+node@18.16.19_@testing-library+dom@10.4.0_typescript@5.5.4_@types+node@18.16.19", + "@vitest/expect": "@vitest/expect@2.0.5", + "@vitest/pretty-format": "@vitest/pretty-format@2.0.5", + "@vitest/runner": "@vitest/runner@2.0.5", + "@vitest/snapshot": "@vitest/snapshot@2.0.5", + "@vitest/spy": "@vitest/spy@2.0.5", + "@vitest/utils": "@vitest/utils@2.0.5", + "chai": "chai@5.1.1", + "debug": "debug@4.3.6", + "execa": "execa@8.0.1", + "magic-string": "magic-string@0.30.11", + "pathe": "pathe@1.1.2", + "std-env": "std-env@3.7.0", + "tinybench": "tinybench@2.9.0", + "tinypool": "tinypool@1.0.1", + "tinyrainbow": "tinyrainbow@1.2.0", + "vite": "vite@5.4.2_@types+node@18.16.19", + "vite-node": "vite-node@2.0.5_@types+node@18.16.19", + "why-is-node-running": "why-is-node-running@2.3.0" + } + }, + "webidl-conversions@4.0.2": { + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "dependencies": {} + }, + "whatwg-url@7.1.0": { + "integrity": "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==", + "dependencies": { + "lodash.sortby": "lodash.sortby@4.7.0", + "tr46": "tr46@1.0.1", + "webidl-conversions": "webidl-conversions@4.0.2" + } + }, + "which@2.0.2": { + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dependencies": { + "isexe": "isexe@2.0.0" + } + }, + "why-is-node-running@2.3.0": { + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dependencies": { + "siginfo": "siginfo@2.0.0", + "stackback": "stackback@0.0.2" + } + }, + "wrap-ansi@6.2.0": { + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dependencies": { + "ansi-styles": "ansi-styles@4.3.0", + "string-width": "string-width@4.2.3", + "strip-ansi": "strip-ansi@6.0.1" + } + }, + "wrap-ansi@7.0.0": { + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "ansi-styles@4.3.0", + "string-width": "string-width@4.2.3", + "strip-ansi": "strip-ansi@6.0.1" + } + }, + "wrap-ansi@8.1.0": { + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dependencies": { + "ansi-styles": "ansi-styles@6.2.1", + "string-width": "string-width@5.1.2", + "strip-ansi": "strip-ansi@7.1.0" + } + }, + "ws@8.18.0": { + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "dependencies": {} + }, + "xtend@4.0.2": { + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dependencies": {} + }, + "y18n@5.0.8": { + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dependencies": {} + }, + "yargs-parser@21.1.1": { + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dependencies": {} + }, + "yargs@17.7.2": { + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dependencies": { + "cliui": "cliui@8.0.1", + "escalade": "escalade@3.2.0", + "get-caller-file": "get-caller-file@2.0.5", + "require-directory": "require-directory@2.1.1", + "string-width": "string-width@4.2.3", + "y18n": "y18n@5.0.8", + "yargs-parser": "yargs-parser@21.1.1" + } + }, + "yoctocolors-cjs@2.1.2": { + "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", + "dependencies": {} + } + } + }, + "remote": {}, + "workspace": { + "members": { + "examples/pglite-auth": { + "packageJson": { + "dependencies": [ + "npm:@biomejs/biome@1.8.3", + "npm:@electric-sql/pglite@^0.2.6", + "npm:@total-typescript/tsconfig@^1.0.4", + "npm:@types/node@^20.14.11", + "npm:pg-gateway", + "npm:tsx@^4.16.2", + "npm:typescript@^5.5.3" + ] + } + }, + "examples/pglite-multiple": { + "packageJson": { + "dependencies": [ + "npm:@biomejs/biome@1.8.3", + "npm:@electric-sql/pglite@^0.2.6", + "npm:@total-typescript/tsconfig@^1.0.4", + "npm:@types/node@^20.14.11", + "npm:pg-gateway", + "npm:tsx@^4.16.2", + "npm:typescript@^5.5.3" + ] + } + }, + "examples/reverse-proxy-sni": { + "packageJson": { + "dependencies": [ + "npm:@biomejs/biome@1.8.3", + "npm:@total-typescript/tsconfig@^1.0.4", + "npm:@types/node@^20.14.11", + "npm:pg-gateway", + "npm:tsx@^4.16.2", + "npm:typescript@^5.5.3" + ] + } + }, + "packages/pg-gateway": { + "packageJson": { + "dependencies": [ + "npm:@biomejs/biome@^1.8.3", + "npm:@electric-sql/pglite@^0.2.6", + "npm:@jsr/std__bytes@^1.0.2", + "npm:@jsr/std__crypto@^1.0.3", + "npm:@jsr/std__encoding@^1.0.3", + "npm:@nodeweb/knex@^3.1.0-alpha.13", + "npm:@nodeweb/pg@^8.12.0-alpha.5", + "npm:@total-typescript/tsconfig@^1.0.4", + "npm:@types/node@^20.14.11", + "npm:@types/pg@^8.11.8", + "npm:@vitest/browser@^2.0.5", + "npm:asn1js@^3.0.5", + "npm:pg-protocol@^1.6.1", + "npm:pg@^8.12.0", + "npm:pkijs@^3.2.4", + "npm:playwright@^1.47.0", + "npm:postgres@^3.4.4", + "npm:tsup@^8.2.3", + "npm:tsx@^4.16.2", + "npm:typescript@^5.5.3", + "npm:vitest@^2.0.5" + ] + } + } + } + } +} diff --git a/examples/pglite-auth/cert.ts b/examples/pglite-auth/cert.ts index 65f5991..5f810a4 100644 --- a/examples/pglite-auth/cert.ts +++ b/examples/pglite-auth/cert.ts @@ -1,12 +1,12 @@ +import { PGlite } from '@electric-sql/pglite'; import fs from 'node:fs'; import net from 'node:net'; -import { PGlite } from '@electric-sql/pglite'; -import { type BackendError, PostgresConnection } from 'pg-gateway'; +import { fromNodeSocket } from 'pg-gateway/node'; const db = new PGlite(); -const server = net.createServer((socket) => { - const connection = new PostgresConnection(socket, { +const server = net.createServer(async (socket) => { + const connection = await fromNodeSocket(socket, { serverVersion: '16.3 (PGlite 0.2.0)', tls: { key: fs.readFileSync('key.pem'), diff --git a/examples/pglite-auth/md5.ts b/examples/pglite-auth/md5.ts index 3c0f3e7..dfbdf60 100644 --- a/examples/pglite-auth/md5.ts +++ b/examples/pglite-auth/md5.ts @@ -1,15 +1,12 @@ -import net from 'node:net'; import { PGlite } from '@electric-sql/pglite'; -import { - type BackendError, - PostgresConnection, - createPreHashedPassword, -} from 'pg-gateway'; +import net from 'node:net'; +import { createPreHashedPassword } from 'pg-gateway'; +import { fromNodeSocket } from 'pg-gateway/node'; const db = new PGlite(); -const server = net.createServer((socket) => { - const connection = new PostgresConnection(socket, { +const server = net.createServer(async (socket) => { + const connection = await fromNodeSocket(socket, { serverVersion: '16.3 (PGlite 0.2.0)', auth: { method: 'md5', diff --git a/examples/pglite-auth/package.json b/examples/pglite-auth/package.json index 82dc697..7a7fe41 100644 --- a/examples/pglite-auth/package.json +++ b/examples/pglite-auth/package.json @@ -7,7 +7,7 @@ "type-check": "tsc --noEmit" }, "dependencies": { - "@electric-sql/pglite": "^0.2.4", + "@electric-sql/pglite": "^0.2.6", "pg-gateway": "*" }, "devDependencies": { diff --git a/examples/pglite-auth/password.ts b/examples/pglite-auth/password.ts index 39110d4..2d44a06 100644 --- a/examples/pglite-auth/password.ts +++ b/examples/pglite-auth/password.ts @@ -1,11 +1,11 @@ -import net from 'node:net'; import { PGlite } from '@electric-sql/pglite'; -import { type BackendError, PostgresConnection } from 'pg-gateway'; +import net from 'node:net'; +import { fromNodeSocket } from 'pg-gateway/node'; const db = new PGlite(); -const server = net.createServer((socket) => { - const connection = new PostgresConnection(socket, { +const server = net.createServer(async (socket) => { + const connection = await fromNodeSocket(socket, { serverVersion: '16.3 (PGlite 0.2.0)', auth: { method: 'password', diff --git a/examples/pglite-auth/scram-sha-256.ts b/examples/pglite-auth/scram-sha-256.ts index 25fdfe6..42dcafa 100644 --- a/examples/pglite-auth/scram-sha-256.ts +++ b/examples/pglite-auth/scram-sha-256.ts @@ -1,15 +1,12 @@ -import net from 'node:net'; import { PGlite } from '@electric-sql/pglite'; -import { - type BackendError, - PostgresConnection, - createScramSha256Data, -} from 'pg-gateway'; +import net from 'node:net'; +import { createScramSha256Data } from 'pg-gateway'; +import { fromNodeSocket } from 'pg-gateway/node'; const db = new PGlite(); -const server = net.createServer((socket) => { - const connection = new PostgresConnection(socket, { +const server = net.createServer(async (socket) => { + const connection = await fromNodeSocket(socket, { serverVersion: '16.3 (PGlite 0.2.0)', auth: { method: 'scram-sha-256', diff --git a/examples/pglite-auth/trust.ts b/examples/pglite-auth/trust.ts index 375aad1..8d29ddf 100644 --- a/examples/pglite-auth/trust.ts +++ b/examples/pglite-auth/trust.ts @@ -1,11 +1,11 @@ -import net from 'node:net'; import { PGlite } from '@electric-sql/pglite'; -import { type BackendError, PostgresConnection } from 'pg-gateway'; +import net from 'node:net'; +import { fromNodeSocket } from 'pg-gateway/node'; const db = new PGlite(); -const server = net.createServer((socket) => { - const connection = new PostgresConnection(socket, { +const server = net.createServer(async (socket) => { + const connection = await fromNodeSocket(socket, { serverVersion: '16.3 (PGlite 0.2.0)', auth: { method: 'trust', @@ -24,10 +24,6 @@ const server = net.createServer((socket) => { return await db.execProtocolRaw(data); }, }); - - socket.on('end', () => { - console.log('Client disconnected'); - }); }); server.listen(5432, () => { diff --git a/examples/pglite-multiple/index.ts b/examples/pglite-multiple/index.ts index c9624db..1d88afa 100644 --- a/examples/pglite-multiple/index.ts +++ b/examples/pglite-multiple/index.ts @@ -1,12 +1,8 @@ +import { PGlite, type PGliteInterface } from '@electric-sql/pglite'; import { mkdir, readFile } from 'node:fs/promises'; import net from 'node:net'; -import { PGlite, type PGliteInterface } from '@electric-sql/pglite'; -import { - type BackendError, - PostgresConnection, - type TlsOptionsCallback, - createPreHashedPassword, -} from 'pg-gateway'; +import { type TlsOptionsCallback, createPreHashedPassword } from 'pg-gateway'; +import { fromNodeSocket } from 'pg-gateway/node'; const tls: TlsOptionsCallback = async ({ sniServerName }) => { // Optionally serve different certs based on `sniServerName` @@ -25,10 +21,10 @@ function getIdFromServerName(serverName: string) { return id; } -const server = net.createServer((socket) => { +const server = net.createServer(async (socket) => { let db: PGliteInterface; - const connection = new PostgresConnection(socket, { + const connection = await fromNodeSocket(socket, { serverVersion: '16.3 (PGlite 0.2.0)', auth: { method: 'md5', @@ -44,8 +40,7 @@ const server = net.createServer((socket) => { code: '08000', message: 'ssl connection required', }); - connection.socket.end(); - return; + throw new Error('end socket'); } if (!tlsInfo.sniServerName) { @@ -54,8 +49,7 @@ const server = net.createServer((socket) => { code: '08000', message: 'ssl sni extension required', }); - connection.socket.end(); - return; + throw new Error('end socket'); } const databaseId = getIdFromServerName(tlsInfo.sniServerName); diff --git a/examples/pglite-multiple/package.json b/examples/pglite-multiple/package.json index 2971241..846936a 100644 --- a/examples/pglite-multiple/package.json +++ b/examples/pglite-multiple/package.json @@ -8,7 +8,7 @@ "type-check": "tsc --noEmit" }, "dependencies": { - "@electric-sql/pglite": "^0.2.4", + "@electric-sql/pglite": "^0.2.6", "pg-gateway": "*" }, "devDependencies": { diff --git a/examples/reverse-proxy-sni/index.ts b/examples/reverse-proxy-sni/index.ts index d4e124f..58f06f5 100644 --- a/examples/reverse-proxy-sni/index.ts +++ b/examples/reverse-proxy-sni/index.ts @@ -1,6 +1,8 @@ import { readFile } from 'node:fs/promises'; import net, { connect } from 'node:net'; -import { PostgresConnection, type TlsOptionsCallback } from 'pg-gateway'; +import { Duplex } from 'node:stream'; +import type { TlsOptionsCallback } from 'pg-gateway'; +import { fromNodeSocket } from 'pg-gateway/node'; const tls: TlsOptionsCallback = async ({ sniServerName }) => { // Optionally serve different certs based on `sniServerName` @@ -21,8 +23,8 @@ async function getServerById(id: string) { }; } -const server = net.createServer((socket) => { - const connection = new PostgresConnection(socket, { +const server = net.createServer(async (socket) => { + const connection = await fromNodeSocket(socket, { tls, // This hook occurs before startup messages are received from the client, // so is a good place to establish proxy connections @@ -33,8 +35,7 @@ const server = net.createServer((socket) => { code: '08000', message: 'ssl connection required', }); - connection.socket.end(); - return; + throw new Error('end socket'); } if (!tlsInfo.sniServerName) { @@ -43,8 +44,7 @@ const server = net.createServer((socket) => { code: '08000', message: 'ssl sni extension required', }); - connection.socket.end(); - return; + throw new Error('end socket'); } // In this example the left-most subdomain contains the server ID @@ -57,8 +57,7 @@ const server = net.createServer((socket) => { code: '08000', message: 'server id required', }); - connection.socket.end(); - return; + throw new Error('end socket'); } // Lookup the server host/port based on ID @@ -68,20 +67,21 @@ const server = net.createServer((socket) => { const proxySocket = connect(serverInfo); // Detach from the `PostgresConnection` to prevent further buffering/processing - const socket = connection.detach(); + const duplex = await connection.detach(); + const nodeDuplex = Duplex.fromWeb(duplex); // Pipe data directly between sockets - proxySocket.pipe(socket); - socket.pipe(proxySocket); + proxySocket.pipe(nodeDuplex); + nodeDuplex.pipe(proxySocket); - proxySocket.on('end', () => socket.end()); - socket.on('end', () => proxySocket.end()); + proxySocket.on('end', () => nodeDuplex.end()); + nodeDuplex.on('end', () => proxySocket.end()); - proxySocket.on('error', (err) => socket.destroy(err)); - socket.on('error', (err) => proxySocket.destroy(err)); + proxySocket.on('error', (err) => nodeDuplex.destroy(err)); + nodeDuplex.on('error', (err) => proxySocket.destroy(err)); - proxySocket.on('close', () => socket.destroy()); - socket.on('close', () => proxySocket.destroy()); + proxySocket.on('close', () => nodeDuplex.destroy()); + nodeDuplex.on('close', () => proxySocket.destroy()); }, }); diff --git a/package-lock.json b/package-lock.json index e9240c1..4e33c19 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,7 +12,7 @@ "examples/pglite-auth": { "name": "pglite-auth-example", "dependencies": { - "@electric-sql/pglite": "^0.2.4", + "@electric-sql/pglite": "^0.2.6", "pg-gateway": "*" }, "devDependencies": { @@ -26,7 +26,7 @@ "examples/pglite-multiple": { "name": "pglite-multiple-example", "dependencies": { - "@electric-sql/pglite": "^0.2.4", + "@electric-sql/pglite": "^0.2.6", "pg-gateway": "*" }, "devDependencies": { @@ -50,12 +50,148 @@ "typescript": "^5.5.3" } }, + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.7.tgz", + "integrity": "sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/highlight": "^7.24.7", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.7.tgz", + "integrity": "sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.24.7.tgz", + "integrity": "sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.24.7", + "chalk": "^2.4.2", + "js-tokens": "^4.0.0", + "picocolors": "^1.0.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/highlight/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/@babel/highlight/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/highlight/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/highlight/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/runtime": { + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz", + "integrity": "sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "regenerator-runtime": "^0.14.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@biomejs/biome": { "version": "1.8.3", "resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-1.8.3.tgz", "integrity": "sha512-/uUV3MV+vyAczO+vKrPdOW0Iaet7UnJMU4bNMinggGJTAnBPjCoLEYcyYtYHNnUNYlv4xZMH6hVIQCAozq8d5w==", "dev": true, "hasInstallScript": true, + "license": "MIT OR Apache-2.0", "bin": { "biome": "bin/biome" }, @@ -205,10 +341,41 @@ "node": ">=14.21.3" } }, + "node_modules/@bundled-es-modules/cookie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/cookie/-/cookie-2.0.0.tgz", + "integrity": "sha512-Or6YHg/kamKHpxULAdSqhGqnWFneIXu1NKvvfBBzKGwpVsYuFIQ5aBPHDnnoR3ghW1nvSkALd+EF9iMtY7Vjxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cookie": "^0.5.0" + } + }, + "node_modules/@bundled-es-modules/statuses": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/statuses/-/statuses-1.0.1.tgz", + "integrity": "sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg==", + "dev": true, + "license": "ISC", + "dependencies": { + "statuses": "^2.0.1" + } + }, + "node_modules/@bundled-es-modules/tough-cookie": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@bundled-es-modules/tough-cookie/-/tough-cookie-0.1.6.tgz", + "integrity": "sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==", + "dev": true, + "license": "ISC", + "dependencies": { + "@types/tough-cookie": "^4.0.5", + "tough-cookie": "^4.1.4" + } + }, "node_modules/@electric-sql/pglite": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/@electric-sql/pglite/-/pglite-0.2.4.tgz", - "integrity": "sha512-NN1ATH9aYTCD4257wZH1CjAKdro8jDd5r/BumxZtEVTDNDztBmPYYtBd4TEIHWdi0+QuM7SWk2JRCLvAYCSEWg==", + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@electric-sql/pglite/-/pglite-0.2.6.tgz", + "integrity": "sha512-tyWWxj1Z1Pd4BqBZL1ER2SXaCn5s9N0bxTQCAkGaaWe8r9EEe1bNs20RAG3/+ZeBJtDrk8y5xjocactL+4aIXg==", "license": "Apache-2.0" }, "node_modules/@esbuild/aix-ppc64": { @@ -595,6 +762,174 @@ "node": ">=12" } }, + "node_modules/@inquirer/confirm": { + "version": "3.1.22", + "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-3.1.22.tgz", + "integrity": "sha512-gsAKIOWBm2Q87CDfs9fEo7wJT3fwWIJfnDGMn9Qy74gBnNFOACDNfhUzovubbJjWnKLGBln7/NcSmZwj5DuEXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/core": "^9.0.10", + "@inquirer/type": "^1.5.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/core": { + "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-9.0.10.tgz", + "integrity": "sha512-TdESOKSVwf6+YWDz8GhS6nKscwzkIyakEzCLJ5Vh6O3Co2ClhCJ0A4MG909MUWfaWdpJm7DE45ii51/2Kat9tA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@inquirer/figures": "^1.0.5", + "@inquirer/type": "^1.5.2", + "@types/mute-stream": "^0.0.4", + "@types/node": "^22.1.0", + "@types/wrap-ansi": "^3.0.0", + "ansi-escapes": "^4.3.2", + "cli-spinners": "^2.9.2", + "cli-width": "^4.1.0", + "mute-stream": "^1.0.0", + "signal-exit": "^4.1.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^6.2.0", + "yoctocolors-cjs": "^2.1.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/core/node_modules/@types/node": { + "version": "22.5.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.1.tgz", + "integrity": "sha512-KkHsxej0j9IW1KKOOAA/XBA0z08UFSrRQHErzEfA3Vgq57eXIMYboIlHJuYIfd+lwCQjtKqUu3UnmKbtUc9yRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/@inquirer/core/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/core/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@inquirer/core/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@inquirer/core/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@inquirer/core/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/core/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/core/node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@inquirer/core/node_modules/wrap-ansi": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", + "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@inquirer/figures": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.5.tgz", + "integrity": "sha512-79hP/VWdZ2UVc9bFGJnoQ/lQMpL74mGgzSYX1xUqCVk7/v73vJCMw1VuyWN1jGkZ9B3z7THAbySqGbCNefcjfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@inquirer/type": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-1.5.2.tgz", + "integrity": "sha512-w9qFkumYDCNyDZmNQjf/n6qQuvQ4dMC3BJesY4oF+yr0CxR5vxujflAVeIcS6U336uzi9GM0kAfZlLrZ9UTkpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mute-stream": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -660,6 +995,37 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mswjs/interceptors": { + "version": "0.29.1", + "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.29.1.tgz", + "integrity": "sha512-3rDakgJZ77+RiQUuSK69t1F0m8BQKA8Vh5DCS5V0DWvNY67zob2JhhQrhCO0AKLGINTRSFd1tBaHcJTkhefoSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@open-draft/deferred-promise": "^2.2.0", + "@open-draft/logger": "^0.3.0", + "@open-draft/until": "^2.0.0", + "is-node-process": "^1.2.0", + "outvariant": "^1.2.1", + "strict-event-emitter": "^0.5.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@noble/hashes": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.5.0.tgz", + "integrity": "sha512-1j6kQFb7QRru7eKN3ZDvRcP13rugwdxZqCjbiAVZfIJwgj2A65UmT4TgARXGlXgnRkORLTDTrO19ZErt7+QXgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -695,6 +1061,45 @@ "node": ">= 8" } }, + "node_modules/@nodeweb/knex": { + "version": "3.1.0-alpha.13", + "resolved": "https://registry.npmjs.org/@nodeweb/knex/-/knex-3.1.0-alpha.13.tgz", + "integrity": "sha512-T20NxBcJ8J7rYqLYwwiMNQ0drQljsntmOHBZrYtPkB1TdzqGDvZ4eCmUbc7Blh0UsfOo6I2O7agJyqPC8DsIgg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@nodeweb/pg": { + "version": "8.12.0-alpha.5", + "resolved": "https://registry.npmjs.org/@nodeweb/pg/-/pg-8.12.0-alpha.5.tgz", + "integrity": "sha512-BQaEeaymGgyclEz/ZX48FhK12nQJSAZrfRmZMgTECTaA0Ty7b2qsSuDW9pkZWPCISGZTgizOZ3s2k2hAFIAPzw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@open-draft/deferred-promise": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz", + "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@open-draft/logger": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz", + "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-node-process": "^1.2.0", + "outvariant": "^1.4.0" + } + }, + "node_modules/@open-draft/until": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz", + "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==", + "dev": true, + "license": "MIT" + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -705,226 +1110,325 @@ "node": ">=14" } }, + "node_modules/@polka/url": { + "version": "1.0.0-next.25", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.25.tgz", + "integrity": "sha512-j7P6Rgr3mmtdkeDGTe0E/aYyWEWVtc5yFXtHCRHs28/jptDEWfaVOc5T7cblqy1XKPPfCxJc/8DwQ5YgLOZOVQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.19.1.tgz", - "integrity": "sha512-XzqSg714++M+FXhHfXpS1tDnNZNpgxxuGZWlRG/jSj+VEPmZ0yg6jV4E0AL3uyBKxO8mO3xtOsP5mQ+XLfrlww==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.21.1.tgz", + "integrity": "sha512-2thheikVEuU7ZxFXubPDOtspKn1x0yqaYQwvALVtEcvFhMifPADBrgRPyHV0TF3b+9BgvgjgagVyvA/UqPZHmg==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.19.1.tgz", - "integrity": "sha512-thFUbkHteM20BGShD6P08aungq4irbIZKUNbG70LN8RkO7YztcGPiKTTGZS7Kw+x5h8hOXs0i4OaHwFxlpQN6A==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.21.1.tgz", + "integrity": "sha512-t1lLYn4V9WgnIFHXy1d2Di/7gyzBWS8G5pQSXdZqfrdCGTwi1VasRMSS81DTYb+avDs/Zz4A6dzERki5oRYz1g==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.19.1.tgz", - "integrity": "sha512-8o6eqeFZzVLia2hKPUZk4jdE3zW7LCcZr+MD18tXkgBBid3lssGVAYuox8x6YHoEPDdDa9ixTaStcmx88lio5Q==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.21.1.tgz", + "integrity": "sha512-AH/wNWSEEHvs6t4iJ3RANxW5ZCK3fUnmf0gyMxWCesY1AlUj8jY7GC+rQE4wd3gwmZ9XDOpL0kcFnCjtN7FXlA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.19.1.tgz", - "integrity": "sha512-4T42heKsnbjkn7ovYiAdDVRRWZLU9Kmhdt6HafZxFcUdpjlBlxj4wDrt1yFWLk7G4+E+8p2C9tcmSu0KA6auGA==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.21.1.tgz", + "integrity": "sha512-dO0BIz/+5ZdkLZrVgQrDdW7m2RkrLwYTh2YMFG9IpBtlC1x1NPNSXkfczhZieOlOLEqgXOFH3wYHB7PmBtf+Bg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.19.1.tgz", - "integrity": "sha512-MXg1xp+e5GhZ3Vit1gGEyoC+dyQUBy2JgVQ+3hUrD9wZMkUw/ywgkpK7oZgnB6kPpGrxJ41clkPPnsknuD6M2Q==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.21.1.tgz", + "integrity": "sha512-sWWgdQ1fq+XKrlda8PsMCfut8caFwZBmhYeoehJ05FdI0YZXk6ZyUjWLrIgbR/VgiGycrFKMMgp7eJ69HOF2pQ==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.19.1.tgz", - "integrity": "sha512-DZNLwIY4ftPSRVkJEaxYkq7u2zel7aah57HESuNkUnz+3bZHxwkCUkrfS2IWC1sxK6F2QNIR0Qr/YXw7nkF3Pw==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.21.1.tgz", + "integrity": "sha512-9OIiSuj5EsYQlmwhmFRA0LRO0dRRjdCVZA3hnmZe1rEwRk11Jy3ECGGq3a7RrVEZ0/pCsYWx8jG3IvcrJ6RCew==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.19.1.tgz", - "integrity": "sha512-C7evongnjyxdngSDRRSQv5GvyfISizgtk9RM+z2biV5kY6S/NF/wta7K+DanmktC5DkuaJQgoKGf7KUDmA7RUw==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.21.1.tgz", + "integrity": "sha512-0kuAkRK4MeIUbzQYu63NrJmfoUVicajoRAL1bpwdYIYRcs57iyIV9NLcuyDyDXE2GiZCL4uhKSYAnyWpjZkWow==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.19.1.tgz", - "integrity": "sha512-89tFWqxfxLLHkAthAcrTs9etAoBFRduNfWdl2xUs/yLV+7XDrJ5yuXMHptNqf1Zw0UCA3cAutkAiAokYCkaPtw==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.21.1.tgz", + "integrity": "sha512-/6dYC9fZtfEY0vozpc5bx1RP4VrtEOhNQGb0HwvYNwXD1BBbwQ5cKIbUVVU7G2d5WRE90NfB922elN8ASXAJEA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.19.1.tgz", - "integrity": "sha512-PromGeV50sq+YfaisG8W3fd+Cl6mnOOiNv2qKKqKCpiiEke2KiKVyDqG/Mb9GWKbYMHj5a01fq/qlUR28PFhCQ==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.21.1.tgz", + "integrity": "sha512-ltUWy+sHeAh3YZ91NUsV4Xg3uBXAlscQe8ZOXRCVAKLsivGuJsrkawYPUEyCV3DYa9urgJugMLn8Z3Z/6CeyRQ==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.19.1.tgz", - "integrity": "sha512-/1BmHYh+iz0cNCP0oHCuF8CSiNj0JOGf0jRlSo3L/FAyZyG2rGBuKpkZVH9YF+x58r1jgWxvm1aRg3DHrLDt6A==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.21.1.tgz", + "integrity": "sha512-BggMndzI7Tlv4/abrgLwa/dxNEMn2gC61DCLrTzw8LkpSKel4o+O+gtjbnkevZ18SKkeN3ihRGPuBxjaetWzWg==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.19.1.tgz", - "integrity": "sha512-0cYP5rGkQWRZKy9/HtsWVStLXzCF3cCBTRI+qRL8Z+wkYlqN7zrSYm6FuY5Kd5ysS5aH0q5lVgb/WbG4jqXN1Q==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.21.1.tgz", + "integrity": "sha512-z/9rtlGd/OMv+gb1mNSjElasMf9yXusAxnRDrBaYB+eS1shFm6/4/xDH1SAISO5729fFKUkJ88TkGPRUh8WSAA==", "cpu": [ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.19.1.tgz", - "integrity": "sha512-XUXeI9eM8rMP8aGvii/aOOiMvTs7xlCosq9xCjcqI9+5hBxtjDpD+7Abm1ZhVIFE1J2h2VIg0t2DX/gjespC2Q==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.21.1.tgz", + "integrity": "sha512-kXQVcWqDcDKw0S2E0TmhlTLlUgAmMVqPrJZR+KpH/1ZaZhLSl23GZpQVmawBQGVhyP5WXIsIQ/zqbDBBYmxm5w==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.19.1.tgz", - "integrity": "sha512-V7cBw/cKXMfEVhpSvVZhC+iGifD6U1zJ4tbibjjN+Xi3blSXaj/rJynAkCFFQfoG6VZrAiP7uGVzL440Q6Me2Q==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.21.1.tgz", + "integrity": "sha512-CbFv/WMQsSdl+bpX6rVbzR4kAjSSBuDgCqb1l4J68UYsQNalz5wOqLGYj4ZI0thGpyX5kc+LLZ9CL+kpqDovZA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.19.1.tgz", - "integrity": "sha512-88brja2vldW/76jWATlBqHEoGjJLRnP0WOEKAUbMcXaAZnemNhlAHSyj4jIwMoP2T750LE9lblvD4e2jXleZsA==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.21.1.tgz", + "integrity": "sha512-3Q3brDgA86gHXWHklrwdREKIrIbxC0ZgU8lwpj0eEKGBQH+31uPqr0P2v11pn0tSIxHvcdOWxa4j+YvLNx1i6g==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.19.1.tgz", - "integrity": "sha512-LdxxcqRVSXi6k6JUrTah1rHuaupoeuiv38du8Mt4r4IPer3kwlTo+RuvfE8KzZ/tL6BhaPlzJ3835i6CxrFIRQ==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.21.1.tgz", + "integrity": "sha512-tNg+jJcKR3Uwe4L0/wY3Ro0H+u3nrb04+tcq1GSYzBEmKLeOQF2emk1whxlzNqb6MMrQ2JOcQEpuuiPLyRcSIw==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.19.1.tgz", - "integrity": "sha512-2bIrL28PcK3YCqD9anGxDxamxdiJAxA+l7fWIwM5o8UqNy1t3d1NdAweO2XhA0KTDJ5aH1FsuiT5+7VhtHliXg==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.21.1.tgz", + "integrity": "sha512-xGiIH95H1zU7naUyTKEyOA/I0aexNMUdO9qRv0bLKN3qu25bBdrxZHqA3PTJ24YNN/GdMzG4xkDcd/GvjuhfLg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ] }, - "node_modules/@total-typescript/tsconfig": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@total-typescript/tsconfig/-/tsconfig-1.0.4.tgz", + "node_modules/@std/bytes": { + "name": "@jsr/std__bytes", + "version": "1.0.2", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__bytes/1.0.2.tgz", + "integrity": "sha512-bkZ1rllRB1qsxFbPqtO1VAYTW2+3ZDmf6pcy8xihKS33r0Z1ly6/E/5DoapnJsNy05LdnANUySWt5kj/awgGdg==" + }, + "node_modules/@std/crypto": { + "name": "@jsr/std__crypto", + "version": "1.0.3", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__crypto/1.0.3.tgz", + "integrity": "sha512-B/4PCckQfbfVMyQvG2I15QbN4vfx1S0tXKstpSlzuSmVwGmAOT/DLCtrbrAKlMiJBN4Zb53KaAJQm8VgkZ+bDg==" + }, + "node_modules/@std/encoding": { + "name": "@jsr/std__encoding", + "version": "1.0.3", + "resolved": "https://npm.jsr.io/~/11/@jsr/std__encoding/1.0.3.tgz", + "integrity": "sha512-l4R27qOsop6yYub8zpMj4tT6OCW3AqD14XswRrOs9DiD9DHWG95lxPNAek3+W4flBqIg7ExQy25VKu+qHqSV0g==" + }, + "node_modules/@testing-library/dom": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", + "integrity": "sha512-pemlzrSESWbdAloYml3bAJMEfNh1Z7EduzqPKprCH5S341frlpYnUEW0H72dLxa6IsYr+mPno20GiSm+h9dEdQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "chalk": "^4.1.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/user-event": { + "version": "14.5.2", + "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.5.2.tgz", + "integrity": "sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "peerDependencies": { + "@testing-library/dom": ">=7.21.4" + } + }, + "node_modules/@total-typescript/tsconfig": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@total-typescript/tsconfig/-/tsconfig-1.0.4.tgz", "integrity": "sha512-fO4ctMPGz1kOFOQ4RCPBRBfMy3gDn+pegUfrGyUFRMv/Rd0ZM3/SHH3hFCYG4u6bPLG8OlmOGcBLDexvyr3A5w==", "dev": true }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz", "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==", "dev": true }, + "node_modules/@types/mute-stream": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/@types/mute-stream/-/mute-stream-0.0.4.tgz", + "integrity": "sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, "node_modules/@types/node": { "version": "20.14.11", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.11.tgz", @@ -934,6 +1438,252 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/pg": { + "version": "8.11.8", + "resolved": "https://registry.npmjs.org/@types/pg/-/pg-8.11.8.tgz", + "integrity": "sha512-IqpCf8/569txXN/HoP5i1LjXfKZWL76Yr2R77xgeIICUbAYHeoaEZFhYHo2uDftecLWrTJUq63JvQu8q3lnDyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "pg-protocol": "*", + "pg-types": "^4.0.1" + } + }, + "node_modules/@types/pg/node_modules/pg-types": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-4.0.2.tgz", + "integrity": "sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==", + "dev": true, + "license": "MIT", + "dependencies": { + "pg-int8": "1.0.1", + "pg-numeric": "1.0.2", + "postgres-array": "~3.0.1", + "postgres-bytea": "~3.0.0", + "postgres-date": "~2.1.0", + "postgres-interval": "^3.0.0", + "postgres-range": "^1.1.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@types/pg/node_modules/postgres-array": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-3.0.2.tgz", + "integrity": "sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/@types/pg/node_modules/postgres-bytea": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-3.0.0.tgz", + "integrity": "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "obuf": "~1.1.2" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/pg/node_modules/postgres-date": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-2.1.0.tgz", + "integrity": "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/@types/pg/node_modules/postgres-interval": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-3.0.0.tgz", + "integrity": "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/@types/statuses": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/statuses/-/statuses-2.0.5.tgz", + "integrity": "sha512-jmIUGWrAiwu3dZpxntxieC+1n/5c3mjrImkmOSQ2NC5uP6cYO4aAZDdSmRcI5C1oiTmqlZGHC+/NmJrKogbP5A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/tough-cookie": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", + "integrity": "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/wrap-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/wrap-ansi/-/wrap-ansi-3.0.0.tgz", + "integrity": "sha512-ltIpx+kM7g/MLRZfkbL7EsCEjfzCcScLpkg37eXEtx5kmrAKBkTJwd1GIAjDSL8wTpM6Hzn5YO4pSb91BEwu1g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitest/browser": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@vitest/browser/-/browser-2.0.5.tgz", + "integrity": "sha512-VbOYtu/6R3d7ASZREcrJmRY/sQuRFO9wMVsEDqfYbWiJRh2fDNi8CL1Csn7Ux31pOcPmmM5QvzFCMpiojvVh8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@testing-library/dom": "^10.4.0", + "@testing-library/user-event": "^14.5.2", + "@vitest/utils": "2.0.5", + "magic-string": "^0.30.10", + "msw": "^2.3.2", + "sirv": "^2.0.4", + "ws": "^8.18.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "playwright": "*", + "vitest": "2.0.5", + "webdriverio": "*" + }, + "peerDependenciesMeta": { + "playwright": { + "optional": true + }, + "safaridriver": { + "optional": true + }, + "webdriverio": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.0.5.tgz", + "integrity": "sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.0.5", + "@vitest/utils": "2.0.5", + "chai": "^5.1.1", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/pretty-format": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.0.5.tgz", + "integrity": "sha512-h8k+1oWHfwTkyTkb9egzwNMfJAEx4veaPSnMeKbVSjp4euqGSbQlm5+6VHwTr7u4FJslVVsUG5nopCaAYdOmSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.0.5.tgz", + "integrity": "sha512-TfRfZa6Bkk9ky4tW0z20WKXFEwwvWhRY+84CnSEtq4+3ZvDlJyY32oNTJtM7AW9ihW90tX/1Q78cb6FjoAs+ig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "2.0.5", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.0.5.tgz", + "integrity": "sha512-SgCPUeDFLaM0mIUHfaArq8fD2WbaXG/zVXjRupthYfYGzc8ztbFbu6dUNOblBG7XLMR1kEhS/DNnfCZ2IhdDew==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.0.5", + "magic-string": "^0.30.10", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.0.5.tgz", + "integrity": "sha512-c/jdthAhvJdpfVuaexSrnawxZz6pywlTPe84LUB2m/4t3rl2fTo9NFGBG4oWgaD+FTgDDV8hJ/nibT7IfH3JfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^3.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.0.5.tgz", + "integrity": "sha512-d8HKbqIcya+GR67mkZbrzhS5kKhtp8dQLcmRZLGTscGVg7yImT82cIrhtn2L8+VujWcy6KZweApgNmPsTAO/UQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.0.5", + "estree-walker": "^3.0.3", + "loupe": "^3.1.1", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-escapes/node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ansi-regex": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", @@ -977,6 +1727,16 @@ "node": ">= 8" } }, + "node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "dequal": "^2.0.3" + } + }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -986,6 +1746,31 @@ "node": ">=8" } }, + "node_modules/asn1js": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/asn1js/-/asn1js-3.0.5.tgz", + "integrity": "sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "pvtsutils": "^1.3.2", + "pvutils": "^1.1.3", + "tslib": "^2.4.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -1040,6 +1825,16 @@ "esbuild": ">=0.18" } }, + "node_modules/bytestreamjs": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/bytestreamjs/-/bytestreamjs-2.0.1.tgz", + "integrity": "sha512-U1Z/ob71V/bXfVABvNr/Kumf5VyeQRBEm6Txb0PQ6S7V5GpBM3w4Cbqz/xPDicR5tN0uvDifng8C+5qECeGwyQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/cac": { "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", @@ -1049,6 +1844,66 @@ "node": ">=8" } }, + "node_modules/chai": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.1.1.tgz", + "integrity": "sha512-pT1ZgP8rPNqUgieVaEY+ryQr6Q4HXNg8Ei9UnLUrjN4IA7dvQC5JB+/kxVcPNDHyBcc/26CXPkbNzq3qwrOEKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/check-error": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.1.tgz", + "integrity": "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -1073,6 +1928,123 @@ "fsevents": "~2.3.2" } }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-width": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz", + "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 12" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -1109,6 +2081,16 @@ "node": "^14.18.0 || >=16.10.0" } }, + "node_modules/cookie": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz", + "integrity": "sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1128,6 +2110,7 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.6.tgz", "integrity": "sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==", "dev": true, + "license": "MIT", "dependencies": { "ms": "2.1.2" }, @@ -1140,6 +2123,26 @@ } } }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", @@ -1152,6 +2155,13 @@ "node": ">=8" } }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "license": "MIT" + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -1202,6 +2212,36 @@ "@esbuild/win32-x64": "0.21.5" } }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -1304,6 +2344,26 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -1380,6 +2440,23 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/headers-polyfill": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/headers-polyfill/-/headers-polyfill-4.0.3.tgz", + "integrity": "sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==", + "dev": true, + "license": "MIT" + }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", @@ -1440,6 +2517,13 @@ "node": ">=0.10.0" } }, + "node_modules/is-node-process": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-node-process/-/is-node-process-1.2.0.tgz", + "integrity": "sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw==", + "dev": true, + "license": "MIT" + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -1491,6 +2575,13 @@ "node": ">=10" } }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, "node_modules/lilconfig": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", @@ -1524,12 +2615,42 @@ "integrity": "sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==", "dev": true }, + "node_modules/loupe": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.1.1.tgz", + "integrity": "sha512-edNu/8D5MKVfGVFRhFf8aAxiTM6Wumfz5XsaatSxlD3w4R1d/WEKUTydCdPGbl9K7QG/Ca3GnDV2sIKIpXRQcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-func-name": "^2.0.1" + } + }, "node_modules/lru-cache": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/magic-string": { + "version": "0.30.11", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.11.tgz", + "integrity": "sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -1573,30 +2694,97 @@ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "dependencies": { - "brace-expansion": "^2.0.1" + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mrmime": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.0.tgz", + "integrity": "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/msw": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/msw/-/msw-2.4.1.tgz", + "integrity": "sha512-HXcoQPzYTwEmVk+BGIcRa0vLabBT+J20SSSeYh/QfajaK5ceA6dlD4ZZjfz2dqGEq4vRNCPLP6eXsB94KllPFg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@bundled-es-modules/cookie": "^2.0.0", + "@bundled-es-modules/statuses": "^1.0.1", + "@bundled-es-modules/tough-cookie": "^0.1.6", + "@inquirer/confirm": "^3.0.0", + "@mswjs/interceptors": "^0.29.0", + "@open-draft/until": "^2.1.0", + "@types/cookie": "^0.6.0", + "@types/statuses": "^2.0.4", + "chalk": "^4.1.2", + "headers-polyfill": "^4.0.2", + "is-node-process": "^1.2.0", + "outvariant": "^1.4.2", + "path-to-regexp": "^6.2.0", + "strict-event-emitter": "^0.5.1", + "type-fest": "^4.9.0", + "yargs": "^17.7.2" + }, + "bin": { + "msw": "cli/index.js" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/mswjs" + }, + "peerDependencies": { + "graphql": ">= 16.8.x", + "typescript": ">= 4.7.x" + }, + "peerDependenciesMeta": { + "graphql": { + "optional": true + }, + "typescript": { + "optional": true + } } }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "node_modules/mute-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz", + "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==", "dev": true, + "license": "ISC", "engines": { - "node": ">=16 || 14 >=14.17" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", @@ -1608,6 +2796,25 @@ "thenify-all": "^1.0.0" } }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -1638,6 +2845,13 @@ "node": ">=0.10.0" } }, + "node_modules/obuf": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", + "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", + "dev": true, + "license": "MIT" + }, "node_modules/onetime": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", @@ -1653,6 +2867,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/outvariant": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/outvariant/-/outvariant-1.4.3.tgz", + "integrity": "sha512-+Sl2UErvtsoajRDKCE5/dBz4DIvHXQQnAxtQTF04OJxY0+DyZXSo5P5Bb7XYWOh81syohlYL24hbDwxedPUJCA==", + "dev": true, + "license": "MIT" + }, "node_modules/package-json-from-dist": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", @@ -1684,6 +2905,13 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/path-to-regexp": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.2.2.tgz", + "integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==", + "dev": true, + "license": "MIT" + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -1693,6 +2921,23 @@ "node": ">=8" } }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.0.tgz", + "integrity": "sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, "node_modules/pg": { "version": "8.12.0", "resolved": "https://registry.npmjs.org/pg/-/pg-8.12.0.tgz", @@ -1746,6 +2991,16 @@ "node": ">=4.0.0" } }, + "node_modules/pg-numeric": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/pg-numeric/-/pg-numeric-1.0.2.tgz", + "integrity": "sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=4" + } + }, "node_modules/pg-pool": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.2.tgz", @@ -1821,6 +3076,100 @@ "node": ">= 6" } }, + "node_modules/pkijs": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/pkijs/-/pkijs-3.2.4.tgz", + "integrity": "sha512-Et9V5QpvBilPFgagJcaKBqXjKrrgF5JL2mSDELk1vvbOTt4fuBhSSsGn9Tcz0TQTfS5GCpXQ31Whrpqeqp0VRg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@noble/hashes": "^1.4.0", + "asn1js": "^3.0.5", + "bytestreamjs": "^2.0.0", + "pvtsutils": "^1.3.2", + "pvutils": "^1.1.3", + "tslib": "^2.6.3" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/playwright": { + "version": "1.47.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.47.0.tgz", + "integrity": "sha512-jOWiRq2pdNAX/mwLiwFYnPHpEZ4rM+fRSQpRHwEwZlP2PUANvL3+aJOF/bvISMhFD30rqMxUB4RJx9aQbfh4Ww==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.47.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.47.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.47.0.tgz", + "integrity": "sha512-1DyHT8OqkcfCkYUD9zzUTfg7EfTd+6a8MkD/NWOvjo0u/SCNd5YmY/lJwFvUZOxJbWNds+ei7ic2+R/cRz/PDg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/postcss": { + "version": "8.4.41", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.41.tgz", + "integrity": "sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.1", + "source-map-js": "^1.2.0" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, "node_modules/postcss-load-config": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", @@ -1863,6 +3212,20 @@ } } }, + "node_modules/postgres": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.4.4.tgz", + "integrity": "sha512-IbyN+9KslkqcXa8AO9fxpk97PA4pzewvpi2B3Dwy9u4zpV32QicaEdgmF3eSQUzdRk7ttDHQejNgAEr4XoeH4A==", + "dev": true, + "license": "Unlicense", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/porsager" + } + }, "node_modules/postgres-array": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", @@ -1902,6 +3265,58 @@ "node": ">=0.10.0" } }, + "node_modules/postgres-range": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/postgres-range/-/postgres-range-1.1.4.tgz", + "integrity": "sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/psl": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", + "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", + "dev": true, + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -1911,6 +3326,33 @@ "node": ">=6" } }, + "node_modules/pvtsutils": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/pvtsutils/-/pvtsutils-1.3.5.tgz", + "integrity": "sha512-ARvb14YB9Nm2Xi6nBq1ZX6dAM0FsJnuk+31aUp4TrcZEdKUlSqOqsxJHUPJDNE3qiIp+iUPEIeR6Je/tgV7zsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.6.1" + } + }, + "node_modules/pvutils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/pvutils/-/pvutils-1.1.3.tgz", + "integrity": "sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true, + "license": "MIT" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -1931,6 +3373,13 @@ } ] }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT" + }, "node_modules/readdirp": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", @@ -1943,6 +3392,30 @@ "node": ">=8.10.0" } }, + "node_modules/regenerator-runtime": { + "version": "0.14.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "dev": true, + "license": "MIT" + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true, + "license": "MIT" + }, "node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", @@ -1976,10 +3449,11 @@ "link": true }, "node_modules/rollup": { - "version": "4.19.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.19.1.tgz", - "integrity": "sha512-K5vziVlg7hTpYfFBI+91zHBEMo6jafYXpkMlqZjg7/zhIG9iHqazBf4xz9AVdjS9BruRn280ROqLI7G3OFRIlw==", + "version": "4.21.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.21.1.tgz", + "integrity": "sha512-ZnYyKvscThhgd3M5+Qt3pmhO4jIRR5RGzaSovB6Q7rGNrK5cUncrtLmcTTJVSdcKXyZjW8X8MB0JMSuH9bcAJg==", "dev": true, + "license": "MIT", "dependencies": { "@types/estree": "1.0.5" }, @@ -1991,22 +3465,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.19.1", - "@rollup/rollup-android-arm64": "4.19.1", - "@rollup/rollup-darwin-arm64": "4.19.1", - "@rollup/rollup-darwin-x64": "4.19.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.19.1", - "@rollup/rollup-linux-arm-musleabihf": "4.19.1", - "@rollup/rollup-linux-arm64-gnu": "4.19.1", - "@rollup/rollup-linux-arm64-musl": "4.19.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.19.1", - "@rollup/rollup-linux-riscv64-gnu": "4.19.1", - "@rollup/rollup-linux-s390x-gnu": "4.19.1", - "@rollup/rollup-linux-x64-gnu": "4.19.1", - "@rollup/rollup-linux-x64-musl": "4.19.1", - "@rollup/rollup-win32-arm64-msvc": "4.19.1", - "@rollup/rollup-win32-ia32-msvc": "4.19.1", - "@rollup/rollup-win32-x64-msvc": "4.19.1", + "@rollup/rollup-android-arm-eabi": "4.21.1", + "@rollup/rollup-android-arm64": "4.21.1", + "@rollup/rollup-darwin-arm64": "4.21.1", + "@rollup/rollup-darwin-x64": "4.21.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.21.1", + "@rollup/rollup-linux-arm-musleabihf": "4.21.1", + "@rollup/rollup-linux-arm64-gnu": "4.21.1", + "@rollup/rollup-linux-arm64-musl": "4.21.1", + "@rollup/rollup-linux-powerpc64le-gnu": "4.21.1", + "@rollup/rollup-linux-riscv64-gnu": "4.21.1", + "@rollup/rollup-linux-s390x-gnu": "4.21.1", + "@rollup/rollup-linux-x64-gnu": "4.21.1", + "@rollup/rollup-linux-x64-musl": "4.21.1", + "@rollup/rollup-win32-arm64-msvc": "4.21.1", + "@rollup/rollup-win32-ia32-msvc": "4.21.1", + "@rollup/rollup-win32-x64-msvc": "4.21.1", "fsevents": "~2.3.2" } }, @@ -2054,12 +3528,34 @@ "node": ">=8" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, + "node_modules/sirv": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-2.0.4.tgz", + "integrity": "sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -2081,6 +3577,16 @@ "node": ">= 8" } }, + "node_modules/source-map-js": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", + "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", @@ -2090,6 +3596,37 @@ "node": ">= 10.x" } }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/std-env": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.7.0.tgz", + "integrity": "sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/strict-event-emitter": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz", + "integrity": "sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ==", + "dev": true, + "license": "MIT" + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -2217,6 +3754,19 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", @@ -2238,16 +3788,79 @@ "node": ">=0.8" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinypool": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.0.1.tgz", + "integrity": "sha512-URZYihUbRPcGv95En+sz6MfghfIc2OJ1sv/RmhWZLouPY0/8Vo80viwPvg3dlaS9fuq7fQMEfgRRK7BBZThBEA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", + "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.0.tgz", + "integrity": "sha512-q5nmENpTHgiPVd1cJDDc9cVoYN5x4vCvwT3FMilvKPKneCBZAxn2YWQjDF0UMcE9k0Cay1gBiDfTMU0g+mPMQA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "is-number": "^7.0.0" + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" }, "engines": { - "node": ">=8.0" + "node": ">=6" } }, "node_modules/tr46": { @@ -2274,6 +3887,13 @@ "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", "dev": true }, + "node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "dev": true, + "license": "0BSD" + }, "node_modules/tsup": { "version": "8.2.3", "resolved": "https://registry.npmjs.org/tsup/-/tsup-8.2.3.tgz", @@ -2629,146 +4249,472 @@ "node": ">=18" } }, - "node_modules/tsup/node_modules/@esbuild/sunos-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.0.tgz", - "integrity": "sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==", - "cpu": [ - "x64" - ], + "node_modules/tsup/node_modules/@esbuild/sunos-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.0.tgz", + "integrity": "sha512-BFelBGfrBwk6LVrmFzCq1u1dZbG4zy/Kp93w2+y83Q5UGYF1d8sCzeLI9NXjKyujjBBniQa8R8PzLFAUrSM9OA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsup/node_modules/@esbuild/win32-arm64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.0.tgz", + "integrity": "sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsup/node_modules/@esbuild/win32-ia32": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.0.tgz", + "integrity": "sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsup/node_modules/@esbuild/win32-x64": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.0.tgz", + "integrity": "sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/tsup/node_modules/esbuild": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.0.tgz", + "integrity": "sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==", + "dev": true, + "hasInstallScript": true, + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.23.0", + "@esbuild/android-arm": "0.23.0", + "@esbuild/android-arm64": "0.23.0", + "@esbuild/android-x64": "0.23.0", + "@esbuild/darwin-arm64": "0.23.0", + "@esbuild/darwin-x64": "0.23.0", + "@esbuild/freebsd-arm64": "0.23.0", + "@esbuild/freebsd-x64": "0.23.0", + "@esbuild/linux-arm": "0.23.0", + "@esbuild/linux-arm64": "0.23.0", + "@esbuild/linux-ia32": "0.23.0", + "@esbuild/linux-loong64": "0.23.0", + "@esbuild/linux-mips64el": "0.23.0", + "@esbuild/linux-ppc64": "0.23.0", + "@esbuild/linux-riscv64": "0.23.0", + "@esbuild/linux-s390x": "0.23.0", + "@esbuild/linux-x64": "0.23.0", + "@esbuild/netbsd-x64": "0.23.0", + "@esbuild/openbsd-arm64": "0.23.0", + "@esbuild/openbsd-x64": "0.23.0", + "@esbuild/sunos-x64": "0.23.0", + "@esbuild/win32-arm64": "0.23.0", + "@esbuild/win32-ia32": "0.23.0", + "@esbuild/win32-x64": "0.23.0" + } + }, + "node_modules/tsx": { + "version": "4.16.2", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.16.2.tgz", + "integrity": "sha512-C1uWweJDgdtX2x600HjaFaucXTilT7tgUZHbOE4+ypskZ1OP8CRCSDkCxG6Vya9EwaFIVagWwpaVAn5wzypaqQ==", + "dev": true, + "dependencies": { + "esbuild": "~0.21.5", + "get-tsconfig": "^4.7.5" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/type-fest": { + "version": "4.26.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.26.0.tgz", + "integrity": "sha512-OduNjVJsFbifKb57UqZ2EMP1i4u64Xwow3NYXUtBbD4vIwJdQd4+xl8YDou1dlm4DVrtwT/7Ky8z8WyCULVfxw==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", + "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, + "node_modules/vite": { + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.2.tgz", + "integrity": "sha512-dDrQTRHp5C1fTFzcSaMxjk6vdpKvT+2/mIdE07Gw2ykehT49O0z/VHS3zZ8iV/Gh8BJJKHWOe5RjaNrW5xf/GA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.41", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.0.5.tgz", + "integrity": "sha512-LdsW4pxj0Ot69FAoXZ1yTnA9bjGohr2yNBU7QKRxpz8ITSkhuDl6h3zS/tvgz4qrNjeRnvrWeXQ8ZF7Um4W00Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.5", + "pathe": "^1.1.2", + "tinyrainbow": "^1.2.0", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.0.5.tgz", + "integrity": "sha512-8GUxONfauuIdeSl5f9GTgVEpg5BTOlplET4WEDaeY2QBiN8wSm68vxN/tb5z405OwppfoCavnwXafiaYBC/xOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@ampproject/remapping": "^2.3.0", + "@vitest/expect": "2.0.5", + "@vitest/pretty-format": "^2.0.5", + "@vitest/runner": "2.0.5", + "@vitest/snapshot": "2.0.5", + "@vitest/spy": "2.0.5", + "@vitest/utils": "2.0.5", + "chai": "^5.1.1", + "debug": "^4.3.5", + "execa": "^8.0.1", + "magic-string": "^0.30.10", + "pathe": "^1.1.2", + "std-env": "^3.7.0", + "tinybench": "^2.8.0", + "tinypool": "^1.0.0", + "tinyrainbow": "^1.2.0", + "vite": "^5.0.0", + "vite-node": "2.0.5", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "2.0.5", + "@vitest/ui": "2.0.5", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/vitest/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", "dev": true, - "optional": true, - "os": [ - "sunos" - ], + "license": "MIT", "engines": { - "node": ">=18" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/tsup/node_modules/@esbuild/win32-arm64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.0.tgz", - "integrity": "sha512-lY6AC8p4Cnb7xYHuIxQ6iYPe6MfO2CC43XXKo9nBXDb35krYt7KGhQnOkRGar5psxYkircpCqfbNDB4uJbS2jQ==", - "cpu": [ - "arm64" - ], + "node_modules/vitest/node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", "dev": true, - "optional": true, - "os": [ - "win32" - ], + "license": "Apache-2.0", "engines": { - "node": ">=18" + "node": ">=16.17.0" } }, - "node_modules/tsup/node_modules/@esbuild/win32-ia32": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.0.tgz", - "integrity": "sha512-7L1bHlOTcO4ByvI7OXVI5pNN6HSu6pUQq9yodga8izeuB1KcT2UkHaH6118QJwopExPn0rMHIseCTx1CRo/uNA==", - "cpu": [ - "ia32" - ], + "node_modules/vitest/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", "dev": true, - "optional": true, - "os": [ - "win32" - ], + "license": "MIT", "engines": { - "node": ">=18" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/tsup/node_modules/@esbuild/win32-x64": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.0.tgz", - "integrity": "sha512-Arm+WgUFLUATuoxCJcahGuk6Yj9Pzxd6l11Zb/2aAuv5kWWvvfhLFo2fni4uSK5vzlUdCGZ/BdV5tH8klj8p8g==", - "cpu": [ - "x64" - ], + "node_modules/vitest/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", "dev": true, - "optional": true, - "os": [ - "win32" - ], + "license": "MIT", "engines": { - "node": ">=18" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/tsup/node_modules/esbuild": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.0.tgz", - "integrity": "sha512-1lvV17H2bMYda/WaFb2jLPeHU3zml2k4/yagNMG8Q/YtfMjCwEUZa2eXXMgZTVSL5q1n4H7sQ0X6CdJDqqeCFA==", + "node_modules/vitest/node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", "dev": true, - "hasInstallScript": true, - "bin": { - "esbuild": "bin/esbuild" + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" }, "engines": { - "node": ">=18" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.23.0", - "@esbuild/android-arm": "0.23.0", - "@esbuild/android-arm64": "0.23.0", - "@esbuild/android-x64": "0.23.0", - "@esbuild/darwin-arm64": "0.23.0", - "@esbuild/darwin-x64": "0.23.0", - "@esbuild/freebsd-arm64": "0.23.0", - "@esbuild/freebsd-x64": "0.23.0", - "@esbuild/linux-arm": "0.23.0", - "@esbuild/linux-arm64": "0.23.0", - "@esbuild/linux-ia32": "0.23.0", - "@esbuild/linux-loong64": "0.23.0", - "@esbuild/linux-mips64el": "0.23.0", - "@esbuild/linux-ppc64": "0.23.0", - "@esbuild/linux-riscv64": "0.23.0", - "@esbuild/linux-s390x": "0.23.0", - "@esbuild/linux-x64": "0.23.0", - "@esbuild/netbsd-x64": "0.23.0", - "@esbuild/openbsd-arm64": "0.23.0", - "@esbuild/openbsd-x64": "0.23.0", - "@esbuild/sunos-x64": "0.23.0", - "@esbuild/win32-arm64": "0.23.0", - "@esbuild/win32-ia32": "0.23.0", - "@esbuild/win32-x64": "0.23.0" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/tsx": { - "version": "4.16.2", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.16.2.tgz", - "integrity": "sha512-C1uWweJDgdtX2x600HjaFaucXTilT7tgUZHbOE4+ypskZ1OP8CRCSDkCxG6Vya9EwaFIVagWwpaVAn5wzypaqQ==", + "node_modules/vitest/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", "dev": true, + "license": "MIT", "dependencies": { - "esbuild": "~0.21.5", - "get-tsconfig": "^4.7.5" - }, - "bin": { - "tsx": "dist/cli.mjs" + "mimic-fn": "^4.0.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=12" }, - "optionalDependencies": { - "fsevents": "~2.3.3" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/typescript": { - "version": "5.5.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.3.tgz", - "integrity": "sha512-/hreyEujaB0w76zKo6717l3L0o/qEUtRgdvUBvlkhoWeOVMjMuHNHk0BRBzikzuGDqNmPQbg5ifMEqsHLiIUcQ==", + "node_modules/vitest/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", "dev": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" + "license": "MIT", + "engines": { + "node": ">=12" }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/vitest/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", "engines": { - "node": ">=14.17" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "node_modules/vitest/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/webidl-conversions": { "version": "4.0.2", @@ -2802,6 +4748,23 @@ "node": ">= 8" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", @@ -2893,6 +4856,28 @@ "node": ">=8" } }, + "node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", @@ -2902,18 +4887,130 @@ "node": ">=0.4" } }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yoctocolors-cjs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.2.tgz", + "integrity": "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "packages/pg-gateway": { - "version": "0.3.0-alpha.6", + "version": "0.3.0-alpha.7", "license": "MIT", + "dependencies": { + "@std/bytes": "npm:@jsr/std__bytes@^1.0.2", + "@std/crypto": "npm:@jsr/std__crypto@^1.0.3", + "@std/encoding": "npm:@jsr/std__encoding@^1.0.3" + }, "devDependencies": { - "@biomejs/biome": "1.8.3", + "@biomejs/biome": "^1.8.3", + "@electric-sql/pglite": "^0.2.6", + "@nodeweb/knex": "^3.1.0-alpha.13", + "@nodeweb/pg": "^8.12.0-alpha.5", "@total-typescript/tsconfig": "^1.0.4", "@types/node": "^20.14.11", + "@types/pg": "^8.11.8", + "@vitest/browser": "^2.0.5", + "asn1js": "^3.0.5", "pg": "^8.12.0", "pg-protocol": "^1.6.1", + "pkijs": "^3.2.4", + "playwright": "^1.47.0", + "postgres": "^3.4.4", "tsup": "^8.2.3", "tsx": "^4.16.2", - "typescript": "^5.5.3" + "typescript": "^5.5.3", + "vitest": "^2.0.5" } } } diff --git a/package.json b/package.json index dccb973..0f25e93 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,11 @@ { "private": true, "scripts": { - "dev": "npm run dev --workspaces", - "format": "npm run format --workspaces", - "lint": "npm run lint --workspaces", - "type-check": "npm run type-check --workspaces" + "test": "npm run test --workspaces --if-present", + "build": "npm run build --workspaces --if-present", + "format": "npm run format --workspaces --if-present", + "lint": "npm run lint --workspaces --if-present", + "type-check": "npm run type-check --workspaces --if-present" }, "workspaces": ["packages/*", "examples/*"] } diff --git a/packages/pg-gateway/package.json b/packages/pg-gateway/package.json index 803b57f..3830357 100644 --- a/packages/pg-gateway/package.json +++ b/packages/pg-gateway/package.json @@ -12,26 +12,57 @@ "import": "./dist/index.js", "types": "./dist/index.d.ts", "default": "./dist/index.cjs" + }, + "./node": { + "import": "./dist/platforms/node/index.js", + "types": "./dist/platforms/node/index.d.ts", + "default": "./dist/platforms/node/index.cjs" + }, + "./deno": { + "import": "./dist/platforms/deno/index.js", + "types": "./dist/platforms/deno/index.d.ts", + "default": "./dist/platforms/deno/index.cjs" + }, + "./web": { + "import": "./dist/platforms/web/index.js", + "types": "./dist/platforms/web/index.d.ts", + "default": "./dist/platforms/web/index.cjs" } }, "sideEffects": false, "scripts": { "dev": "tsx examples/pglite/index.ts", "build": "tsup --clean", - "test": "echo \"Error: no test specified\" && exit 1", + "test": "vitest", + "test:deno": "deno test --allow-net --allow-read test/deno", "prepublishOnly": "npm run build", "format": "biome format --write .", "lint": "biome lint --error-on-warnings .", "type-check": "tsc --noEmit" }, + "dependencies": { + "@std/bytes": "npm:@jsr/std__bytes@^1.0.2", + "@std/crypto": "npm:@jsr/std__crypto@^1.0.3", + "@std/encoding": "npm:@jsr/std__encoding@^1.0.3" + }, "devDependencies": { - "@biomejs/biome": "1.8.3", + "@biomejs/biome": "^1.8.3", + "@electric-sql/pglite": "^0.2.6", + "@nodeweb/knex": "^3.1.0-alpha.13", + "@nodeweb/pg": "^8.12.0-alpha.5", "@total-typescript/tsconfig": "^1.0.4", "@types/node": "^20.14.11", + "@types/pg": "^8.11.8", + "@vitest/browser": "^2.0.5", + "asn1js": "^3.0.5", "pg": "^8.12.0", "pg-protocol": "^1.6.1", + "pkijs": "^3.2.4", + "playwright": "^1.47.0", + "postgres": "^3.4.4", "tsup": "^8.2.3", "tsx": "^4.16.2", - "typescript": "^5.5.3" + "typescript": "^5.5.3", + "vitest": "^2.0.5" } } diff --git a/packages/pg-gateway/src/auth/base-auth-flow.ts b/packages/pg-gateway/src/auth/base-auth-flow.ts index d18abe9..399fc37 100644 --- a/packages/pg-gateway/src/auth/base-auth-flow.ts +++ b/packages/pg-gateway/src/auth/base-auth-flow.ts @@ -1,42 +1,32 @@ -import type { Socket } from 'node:net'; -import { - type BackendError, - createBackendErrorMessage, -} from '../backend-error.js'; import type { BufferReader } from '../buffer-reader.js'; import type { BufferWriter } from '../buffer-writer.js'; import type { ConnectionState } from '../connection.types.js'; +import type { ConnectionSignal } from '../signals.js'; export interface AuthFlow { - sendInitialAuthMessage(): void; - handleClientMessage(message: Buffer): Promise; + createInitialAuthMessage(): Uint8Array | undefined; + handleClientMessage(message: BufferSource): AsyncGenerator; isCompleted: boolean; } export abstract class BaseAuthFlow implements AuthFlow { - protected socket: Socket; protected reader: BufferReader; protected writer: BufferWriter; protected connectionState: ConnectionState; constructor(params: { - socket: Socket; reader: BufferReader; writer: BufferWriter; connectionState: ConnectionState; }) { - this.socket = params.socket; this.reader = params.reader; this.writer = params.writer; this.connectionState = params.connectionState; } - abstract sendInitialAuthMessage(): void; - abstract handleClientMessage(message: Buffer): Promise; + abstract createInitialAuthMessage(): Uint8Array | undefined; + abstract handleClientMessage( + message: BufferSource, + ): AsyncGenerator; abstract get isCompleted(): boolean; - - protected sendError(error: BackendError) { - const errorMessage = createBackendErrorMessage(error); - this.socket.write(errorMessage); - } } diff --git a/packages/pg-gateway/src/auth/cert.ts b/packages/pg-gateway/src/auth/cert.ts index 8222cd5..cb9413c 100644 --- a/packages/pg-gateway/src/auth/cert.ts +++ b/packages/pg-gateway/src/auth/cert.ts @@ -1,8 +1,8 @@ -import type { Socket } from 'node:net'; -import { type PeerCertificate, TLSSocket } from 'node:tls'; +import { createBackendErrorMessage } from '../backend-error.js'; import type { BufferReader } from '../buffer-reader.js'; import type { BufferWriter } from '../buffer-writer.js'; import type { ConnectionState } from '../connection.types'; +import { closeSignal } from '../signals.js'; import { BaseAuthFlow } from './base-auth-flow'; export type CertAuthOptions = { @@ -10,7 +10,7 @@ export type CertAuthOptions = { validateCredentials?: ( credentials: { username: string; - certificate: PeerCertificate; + certificate: Uint8Array; }, connectionState: ConnectionState, ) => boolean | Promise; @@ -26,69 +26,67 @@ export class CertAuthFlow extends BaseAuthFlow { constructor(params: { auth: CertAuthOptions; username: string; - socket: Socket; reader: BufferReader; writer: BufferWriter; connectionState: ConnectionState; }) { super(params); + this.auth = { ...params.auth, validateCredentials: params.auth.validateCredentials ?? - (async ({ username, certificate }) => { - return certificate.subject.CN === username; + (() => { + throw new Error('Client certificate validation not implemented'); }), }; this.username = params.username; } - async handleClientMessage(message: Buffer): Promise { - if (!(this.socket instanceof TLSSocket)) { - this.sendError({ + async *handleClientMessage(message: BufferSource) { + if (!this.connectionState.tlsInfo) { + yield createBackendErrorMessage({ severity: 'FATAL', code: '08000', message: `ssl connection required when auth mode is 'certificate'`, }); - this.socket.end(); + yield closeSignal; return; } - if (!this.socket.authorized) { - this.sendError({ + if (!this.connectionState.tlsInfo.clientCertificate) { + yield createBackendErrorMessage({ severity: 'FATAL', code: '08000', - message: 'client certificate is invalid', + message: 'client certificate required', }); - this.socket.end(); + yield closeSignal; return; } - this.socket.pause(); const isValid = await this.auth.validateCredentials( { username: this.username, - certificate: this.socket.getPeerCertificate(), + certificate: this.connectionState.tlsInfo.clientCertificate, }, this.connectionState, ); - this.socket.resume(); if (!isValid) { - this.sendError({ + yield createBackendErrorMessage({ severity: 'FATAL', code: '08000', message: 'client certificate is invalid', }); - this.socket.end(); + yield closeSignal; return; } this.completed = true; } - override sendInitialAuthMessage(): void { - return; + override createInitialAuthMessage() { + return undefined; } get isCompleted(): boolean { diff --git a/packages/pg-gateway/src/auth/index.ts b/packages/pg-gateway/src/auth/index.ts index 3cf3058..a67de15 100644 --- a/packages/pg-gateway/src/auth/index.ts +++ b/packages/pg-gateway/src/auth/index.ts @@ -1,4 +1,3 @@ -import type { Socket } from 'node:net'; import type { BufferReader } from '../buffer-reader.js'; import type { BufferWriter } from '../buffer-writer.js'; import type { ConnectionState } from '../connection.types'; @@ -6,10 +5,7 @@ import type { AuthFlow } from './base-auth-flow'; import { CertAuthFlow, type CertAuthOptions } from './cert'; import { Md5AuthFlow, type Md5AuthOptions } from './md5'; import { PasswordAuthFlow, type PasswordAuthOptions } from './password'; -import { - ScramSha256AuthFlow, - type ScramSha256AuthOptions, -} from './sasl/scram-sha-256'; +import { ScramSha256AuthFlow, type ScramSha256AuthOptions } from './sasl/scram-sha-256'; import type { TrustAuthOptions } from './trust'; export type AuthOptions = @@ -20,7 +16,6 @@ export type AuthOptions = | CertAuthOptions; export function createAuthFlow(options: { - socket: Socket; reader: BufferReader; writer: BufferWriter; auth: AuthOptions; diff --git a/packages/pg-gateway/src/auth/md5.ts b/packages/pg-gateway/src/auth/md5.ts index d5ea347..fdb7568 100644 --- a/packages/pg-gateway/src/auth/md5.ts +++ b/packages/pg-gateway/src/auth/md5.ts @@ -1,10 +1,13 @@ -import { type BinaryLike, createHash } from 'node:crypto'; -import type { Socket } from 'node:net'; +import { concat } from '@std/bytes/concat'; +import { crypto } from '@std/crypto'; +import { encodeHex } from '@std/encoding/hex'; +import { createBackendErrorMessage } from '../backend-error.js'; import type { BufferReader } from '../buffer-reader.js'; import type { BufferWriter } from '../buffer-writer.js'; import type { ConnectionState } from '../connection.types'; import { BackendMessageCode } from '../message-codes'; import { BaseAuthFlow } from './base-auth-flow'; +import { closeSignal } from '../signals.js'; export type Md5AuthOptions = { method: 'md5'; @@ -12,7 +15,7 @@ export type Md5AuthOptions = { credentials: { username: string; preHashedPassword: string; - salt: Buffer; + salt: Uint8Array; hashedPassword: string; }, connectionState: ConnectionState, @@ -28,13 +31,12 @@ export class Md5AuthFlow extends BaseAuthFlow { validateCredentials: NonNullable; }; private username: string; - private salt: Buffer; + private salt: Uint8Array; private completed = false; constructor(params: { auth: Md5AuthOptions; username: string; - socket: Socket; reader: BufferReader; writer: BufferWriter; connectionState: ConnectionState; @@ -45,10 +47,7 @@ export class Md5AuthFlow extends BaseAuthFlow { validateCredentials: params.auth.validateCredentials ?? (async ({ preHashedPassword, hashedPassword, salt }) => { - const expectedHashedPassword = await hashPreHashedPassword( - preHashedPassword, - salt, - ); + const expectedHashedPassword = await hashPreHashedPassword(preHashedPassword, salt); return hashedPassword === expectedHashedPassword; }), }; @@ -56,11 +55,10 @@ export class Md5AuthFlow extends BaseAuthFlow { this.salt = generateMd5Salt(); } - async handleClientMessage(message: Buffer): Promise { + async *handleClientMessage(message: Uint8Array) { const length = this.reader.int32(); const hashedPassword = this.reader.cstring(); - this.socket.pause(); const preHashedPassword = await this.auth.getPreHashedPassword( { username: this.username, @@ -76,23 +74,22 @@ export class Md5AuthFlow extends BaseAuthFlow { }, this.connectionState, ); - this.socket.resume(); if (!isValid) { - this.sendError({ + yield createBackendErrorMessage({ severity: 'FATAL', code: '28P01', message: `password authentication failed for user "${this.username}"`, }); - this.socket.end(); + yield closeSignal; return; } this.completed = true; } - override sendInitialAuthMessage(): void { - this.sendAuthenticationMD5Password(); + override createInitialAuthMessage() { + return this.createAuthenticationMD5Password(); } get isCompleted(): boolean { @@ -100,19 +97,15 @@ export class Md5AuthFlow extends BaseAuthFlow { } /** - * Sends the authentication response to the client. + * Creates the authentication response. * * @see https://www.postgresql.org/docs/current/protocol-flow.html#PROTOCOL-FLOW-START-UP */ - private sendAuthenticationMD5Password(): void { + private createAuthenticationMD5Password() { this.writer.addInt32(5); this.writer.add(Buffer.from(this.salt)); - const response = this.writer.flush( - BackendMessageCode.AuthenticationResponse, - ); - - this.socket.write(response); + return this.writer.flush(BackendMessageCode.AuthenticationResponse); } } @@ -121,30 +114,32 @@ export class Md5AuthFlow extends BaseAuthFlow { * * @see https://www.postgresql.org/docs/current/protocol-flow.html#PROTOCOL-FLOW-START-UP */ -export async function hashPreHashedPassword( - preHashedPassword: string, - salt: Buffer, -) { - const hash = md5(Buffer.concat([Buffer.from(preHashedPassword), salt])); +export async function hashPreHashedPassword(preHashedPassword: string, salt: Uint8Array) { + const hash = await md5(concat([new TextEncoder().encode(preHashedPassword), salt])); return `md5${hash}`; } /** * Computes the MD5 hash of the given value. */ -export function md5(value: BinaryLike) { - return createHash('md5').update(value).digest('hex'); +export async function md5(value: string | Uint8Array) { + const hash = await crypto.subtle.digest( + 'MD5', + typeof value === 'string' ? new TextEncoder().encode(value) : value, + ); + + return encodeHex(hash); } /** * Generates a random 4-byte salt for MD5 hashing. */ export function generateMd5Salt() { - const salt = Buffer.alloc(4); + const salt = new Uint8Array(4); crypto.getRandomValues(salt); return salt; } -export function createPreHashedPassword(username: string, password: string) { - return md5(`${password}${username}`); +export async function createPreHashedPassword(username: string, password: string) { + return await md5(`${password}${username}`); } diff --git a/packages/pg-gateway/src/auth/password.ts b/packages/pg-gateway/src/auth/password.ts index 9cb90da..2714a95 100644 --- a/packages/pg-gateway/src/auth/password.ts +++ b/packages/pg-gateway/src/auth/password.ts @@ -1,8 +1,9 @@ -import type { Socket } from 'node:net'; +import { createBackendErrorMessage } from '../backend-error.js'; import type { BufferReader } from '../buffer-reader.js'; import type { BufferWriter } from '../buffer-writer.js'; import type { ConnectionState } from '../connection.types'; import { BackendMessageCode } from '../message-codes'; +import { closeSignal } from '../signals.js'; import { BaseAuthFlow } from './base-auth-flow'; export type ClearTextPassword = string; @@ -27,9 +28,7 @@ export type PasswordAuthOptions = { export class PasswordAuthFlow extends BaseAuthFlow { private auth: PasswordAuthOptions & { - validateCredentials: NonNullable< - PasswordAuthOptions['validateCredentials'] - >; + validateCredentials: NonNullable; }; private username: string; private completed = false; @@ -37,7 +36,6 @@ export class PasswordAuthFlow extends BaseAuthFlow { constructor(params: { auth: PasswordAuthOptions; username: string; - socket: Socket; reader: BufferReader; writer: BufferWriter; connectionState: ConnectionState; @@ -54,11 +52,10 @@ export class PasswordAuthFlow extends BaseAuthFlow { this.username = params.username; } - async handleClientMessage(message: Buffer): Promise { + async *handleClientMessage(message: BufferSource) { const length = this.reader.int32(); const password = this.reader.cstring(); - this.socket.pause(); const clearTextPassword = await this.auth.getClearTextPassword( { username: this.username, @@ -73,23 +70,22 @@ export class PasswordAuthFlow extends BaseAuthFlow { }, this.connectionState, ); - this.socket.resume(); if (!isValid) { - this.sendError({ + yield createBackendErrorMessage({ severity: 'FATAL', code: '28P01', message: `password authentication failed for user "${this.username}"`, }); - this.socket.end(); + yield closeSignal; return; } this.completed = true; } - override sendInitialAuthMessage(): void { - this.sendAuthenticationCleartextPassword(); + override createInitialAuthMessage() { + return this.createAuthenticationCleartextPassword(); } get isCompleted(): boolean { @@ -97,15 +93,12 @@ export class PasswordAuthFlow extends BaseAuthFlow { } /** - * Sends an "AuthenticationCleartextPassword" message to the frontend. + * Create an "AuthenticationCleartextPassword" message. * * @see https://www.postgresql.org/docs/current/protocol-message-formats.html#PROTOCOL-MESSAGE-FORMATS-AUTHENTICATIONCLEARTEXTPASSWORD */ - private sendAuthenticationCleartextPassword() { + private createAuthenticationCleartextPassword() { this.writer.addInt32(3); - const response = this.writer.flush( - BackendMessageCode.AuthenticationResponse, - ); - this.socket.write(response); + return this.writer.flush(BackendMessageCode.AuthenticationResponse); } } diff --git a/packages/pg-gateway/src/auth/sasl/sasl-mechanism.ts b/packages/pg-gateway/src/auth/sasl/sasl-mechanism.ts index 1e8ac06..77f566f 100644 --- a/packages/pg-gateway/src/auth/sasl/sasl-mechanism.ts +++ b/packages/pg-gateway/src/auth/sasl/sasl-mechanism.ts @@ -1,8 +1,3 @@ -import type { Socket } from 'node:net'; -import { - type BackendError, - createBackendErrorMessage, -} from '../../backend-error.js'; import type { BufferWriter } from '../../buffer-writer.js'; import { BackendMessageCode } from '../../message-codes.js'; @@ -13,56 +8,32 @@ const SaslMessageCode = { } as const; export class SaslMechanism { - socket: Socket; writer: BufferWriter; constructor(params: { - socket: Socket; writer: BufferWriter; }) { - this.socket = params.socket; this.writer = params.writer; } - sendAuthenticationSASL() { + createAuthenticationSASL() { const mechanisms = ['SCRAM-SHA-256']; this.writer.addInt32(SaslMessageCode.AuthenticationSASL); for (const mechanism of mechanisms) { this.writer.addCString(mechanism); } this.writer.addCString(''); - const response = this.writer.flush( - BackendMessageCode.AuthenticationResponse, - ); - this.socket.write(response); + return this.writer.flush(BackendMessageCode.AuthenticationResponse); } - sendAuthenticationSASLContinue(message: string) { + createAuthenticationSASLContinue(message: string) { this.writer.addInt32(SaslMessageCode.AuthenticationSASLContinue); this.writer.addString(message); - const response = this.writer.flush( - BackendMessageCode.AuthenticationResponse, - ); - this.socket.write(response); + return this.writer.flush(BackendMessageCode.AuthenticationResponse); } - sendAuthenticationSASLFinal(message: string) { + createAuthenticationSASLFinal(message: string) { this.writer.addInt32(SaslMessageCode.AuthenticationSASLFinal); this.writer.addString(message); - const response = this.writer.flush( - BackendMessageCode.AuthenticationResponse, - ); - this.socket.write(response); - } - - /** - * Sends an error message to the frontend. - * - * @see https://www.postgresql.org/docs/current/protocol-message-formats.html#PROTOCOL-MESSAGE-FORMATS-ERRORRESPONSE - * - * For error fields, see https://www.postgresql.org/docs/current/protocol-error-fields.html#PROTOCOL-ERROR-FIELDS - */ - sendError(error: BackendError) { - const errorMessage = createBackendErrorMessage(error); - this.socket.write(errorMessage); + return this.writer.flush(BackendMessageCode.AuthenticationResponse); } } diff --git a/packages/pg-gateway/src/auth/sasl/scram-sha-256.ts b/packages/pg-gateway/src/auth/sasl/scram-sha-256.ts index 3ccfc7a..46c19b9 100644 --- a/packages/pg-gateway/src/auth/sasl/scram-sha-256.ts +++ b/packages/pg-gateway/src/auth/sasl/scram-sha-256.ts @@ -1,16 +1,12 @@ -import { - createHash, - createHmac, - pbkdf2Sync, - randomBytes, - timingSafeEqual, -} from 'node:crypto'; -import type { Socket } from 'node:net'; +import { decodeBase64, encodeBase64 } from '@std/encoding/base64'; +import { createBackendErrorMessage } from '../../backend-error.js'; import type { BufferReader } from '../../buffer-reader.js'; import type { BufferWriter } from '../../buffer-writer.js'; import type { ConnectionState } from '../../connection.types'; +import { createHashKey, createHmacKey, pbkdf2, timingSafeEqual } from '../../crypto.js'; import type { AuthFlow } from '../base-auth-flow'; import { SaslMechanism } from './sasl-mechanism'; +import { closeSignal } from '../../signals.js'; export type ScramSha256Data = { salt: string; @@ -42,34 +38,23 @@ export type ScramSha256AuthOptions = { * Creates scram-sha-256 data for password authentication. * @see https://www.postgresql.org/docs/current/sasl-authentication.html */ -export function createScramSha256Data( +export async function createScramSha256Data( password: string, iterations = 4096, -): ScramSha256Data { - const salt = randomBytes(16).toString('base64'); - const saltBuffer = Buffer.from(salt, 'base64'); - const saltedPassword = pbkdf2Sync( - password, - saltBuffer, - iterations, - 32, - 'sha256', - ); - - const clientKey = createHmac('sha256', saltedPassword) - .update('Client Key') - .digest(); - const storedKey = createHash('sha256').update(clientKey).digest(); +): Promise { + const salt = new Uint8Array(16); + crypto.getRandomValues(salt); - const serverKey = createHmac('sha256', saltedPassword) - .update('Server Key') - .digest(); + const saltedPassword = await pbkdf2(password, salt, iterations, 32, 'SHA-256'); + const clientKey = await createHmacKey(saltedPassword, 'Client Key', 'SHA-256'); + const storedKey = await createHashKey(clientKey, 'SHA-256'); + const serverKey = await createHmacKey(saltedPassword, 'Server Key', 'SHA-256'); return { - salt, + salt: encodeBase64(salt), iterations, - storedKey: storedKey.toString('base64'), - serverKey: serverKey.toString('base64'), + storedKey: encodeBase64(storedKey), + serverKey: encodeBase64(serverKey), }; } @@ -77,27 +62,27 @@ export function createScramSha256Data( * Verifies a scram-sha-256 password using the provided parameters. * @see https://www.postgresql.org/docs/current/sasl-authentication.html */ -export function verifyScramSha256Password(params: { +export async function verifyScramSha256Password(params: { authMessage: string; clientProof: string; storedKey: string; }) { const { authMessage, clientProof, storedKey } = params; - const clientProofBuffer = Buffer.from(clientProof, 'base64'); - const storedKeyBuffer = Buffer.from(storedKey, 'base64'); + const clientProofBuffer = decodeBase64(clientProof); + const storedKeyBuffer = decodeBase64(storedKey); + + const clientSignature = await createHmacKey(storedKeyBuffer, authMessage, 'SHA-256'); + const clientSignatureView = new Uint8Array(clientSignature); + const clientKey = new Uint8Array(clientProofBuffer.length); - const clientSignature = createHmac('sha256', storedKeyBuffer) - .update(authMessage) - .digest(); - const clientKey = Buffer.alloc(clientProofBuffer.length); for (let i = 0; i < clientProofBuffer.length; i++) { // biome-ignore lint/style/noNonNullAssertion: - clientKey[i] = clientProofBuffer[i]! ^ clientSignature[i]!; + clientKey[i] = clientProofBuffer[i]! ^ clientSignatureView[i]!; } - const computedStoredKey = createHash('sha256').update(clientKey).digest(); + const computedStoredKey = await createHashKey(clientKey, 'SHA-256'); - return timingSafeEqual(storedKeyBuffer, computedStoredKey); + return await timingSafeEqual(storedKeyBuffer, computedStoredKey); } const ScramSha256Step = { @@ -111,9 +96,7 @@ type ScramSha256Step = (typeof ScramSha256Step)[keyof typeof ScramSha256Step]; export class ScramSha256AuthFlow extends SaslMechanism implements AuthFlow { auth: ScramSha256AuthOptions & { - validateCredentials: NonNullable< - ScramSha256AuthOptions['validateCredentials'] - >; + validateCredentials: NonNullable; }; username: string; clientFirstMessageBare?: string; @@ -127,15 +110,11 @@ export class ScramSha256AuthFlow extends SaslMechanism implements AuthFlow { constructor(params: { auth: ScramSha256AuthOptions; username: string; - socket: Socket; reader: BufferReader; writer: BufferWriter; connectionState: ConnectionState; }) { - super({ - socket: params.socket, - writer: params.writer, - }); + super({ writer: params.writer }); this.username = params.username; this.auth = { ...params.auth, @@ -159,66 +138,60 @@ export class ScramSha256AuthFlow extends SaslMechanism implements AuthFlow { */ async getScramSha256Data(params: { username: string }) { if (!this.scramSha256Data) { - this.socket.pause(); - this.scramSha256Data = await this.auth.getScramSha256Data( - params, - this.connectionState, - ); - this.socket.resume(); + this.scramSha256Data = await this.auth.getScramSha256Data(params, this.connectionState); } return this.scramSha256Data; } - sendInitialAuthMessage() { - this.sendAuthenticationSASL(); + createInitialAuthMessage() { + return this.createAuthenticationSASL(); } - async handleClientMessage(message: Buffer) { + async *handleClientMessage(message: BufferSource) { switch (this.step) { case ScramSha256Step.Initial: - return await this.handleClientFirstMessage(message); + return yield* this.handleClientFirstMessage(message); case ScramSha256Step.ServerFirstMessage: - return await this.handleClientFinalMessage(message); + return yield* this.handleClientFinalMessage(message); default: throw new Error('Unexpected SCRAM-SHA-256 authentication step'); } } - async handleClientFirstMessage(message: Buffer) { + async *handleClientFirstMessage(message: BufferSource) { const length = this.reader.int32(); const saslMechanism = this.reader.cstring(); if (saslMechanism !== 'SCRAM-SHA-256') { - this.sendError({ + yield createBackendErrorMessage({ severity: 'FATAL', code: '28000', message: 'Unsupported SASL authentication mechanism', }); - this.socket.end(); + yield closeSignal; return; } const responseLength = this.reader.int32(); const clientFirstMessage = this.reader.string(responseLength); - const serverFirstMessage = - await this.createServerFirstMessage(clientFirstMessage); + const serverFirstMessage = await this.createServerFirstMessage(clientFirstMessage); this.step = ScramSha256Step.ServerFirstMessage; - this.sendAuthenticationSASLContinue(serverFirstMessage); + yield this.createAuthenticationSASLContinue(serverFirstMessage); } async createServerFirstMessage(clientFirstMessage: string) { const clientFirstMessageParts = clientFirstMessage.split(','); this.clientFirstMessageBare = clientFirstMessageParts.slice(2).join(','); const clientNonce = - clientFirstMessageParts - .find((part) => part.startsWith('r=')) - ?.substring(2) || ''; + clientFirstMessageParts.find((part) => part.startsWith('r='))?.substring(2) || ''; // Generate server nonce by appending random bytes to client nonce - const serverNoncePart = randomBytes(18).toString('base64'); - this.serverNonce = clientNonce + serverNoncePart; + const serverNoncePart = new Uint8Array(18); + crypto.getRandomValues(serverNoncePart); + + this.serverNonce = clientNonce + encodeBase64(serverNoncePart); const { salt, iterations } = await this.getScramSha256Data({ username: this.username, @@ -228,18 +201,19 @@ export class ScramSha256AuthFlow extends SaslMechanism implements AuthFlow { return this.serverFirstMessage; } - async handleClientFinalMessage(message: Buffer) { + async *handleClientFinalMessage(message: BufferSource) { try { const serverFinalMessage = await this.createServerFinalMessage(message); this.step = ScramSha256Step.Completed; - this.sendAuthenticationSASLFinal(serverFinalMessage); + yield this.createAuthenticationSASLFinal(serverFinalMessage); } catch (error) { - this.sendError({ + yield createBackendErrorMessage({ severity: 'FATAL', code: '28000', message: (error as Error).message, }); - this.socket.end(); + yield closeSignal; + return; } } @@ -247,19 +221,16 @@ export class ScramSha256AuthFlow extends SaslMechanism implements AuthFlow { return this.step === ScramSha256Step.Completed; } - async createServerFinalMessage(message: Buffer) { + async createServerFinalMessage(message: BufferSource) { const length = this.reader.int32(); - const clientFinalMessage = this.reader.string(length); + const stringLength = length - 4; // length includes header + const clientFinalMessage = this.reader.string(stringLength); const clientFinalMessageParts = clientFinalMessage.split(','); const channelBinding = clientFinalMessageParts .find((part) => part.startsWith('c=')) ?.substring(2); - const fullNonce = clientFinalMessageParts - .find((part) => part.startsWith('r=')) - ?.substring(2); - const clientProof = clientFinalMessageParts - .find((part) => part.startsWith('p=')) - ?.substring(2); + const fullNonce = clientFinalMessageParts.find((part) => part.startsWith('r='))?.substring(2); + const clientProof = clientFinalMessageParts.find((part) => part.startsWith('p='))?.substring(2); if (!channelBinding || !fullNonce || !clientProof) { throw new Error('Invalid client final message'); @@ -280,7 +251,6 @@ export class ScramSha256AuthFlow extends SaslMechanism implements AuthFlow { username: this.username, }); - this.socket.pause(); const isValid = await this.auth.validateCredentials( { authMessage, @@ -290,21 +260,14 @@ export class ScramSha256AuthFlow extends SaslMechanism implements AuthFlow { }, this.connectionState, ); - this.socket.resume(); if (!isValid) { - throw new Error( - `password authentication failed for user "${this.username}"`, - ); + throw new Error(`password authentication failed for user "${this.username}"`); } - const serverSignature = createHmac( - 'sha256', - Buffer.from(data.serverKey, 'base64'), - ) - .update(authMessage) - .digest(); + const serverKeyBuffer = decodeBase64(data.serverKey); + const serverSignature = await createHmacKey(serverKeyBuffer, authMessage, 'SHA-256'); - return `v=${serverSignature.toString('base64')}`; + return `v=${encodeBase64(serverSignature)}`; } } diff --git a/packages/pg-gateway/src/backend-error.ts b/packages/pg-gateway/src/backend-error.ts index 031efa7..83469ff 100644 --- a/packages/pg-gateway/src/backend-error.ts +++ b/packages/pg-gateway/src/backend-error.ts @@ -25,6 +25,8 @@ export interface BackendError { * Creates a backend error message * * @see https://www.postgresql.org/docs/current/protocol-message-formats.html#PROTOCOL-MESSAGE-FORMATS-ERRORRESPONSE + * + * For error fields, @see https://www.postgresql.org/docs/current/protocol-error-fields.html#PROTOCOL-ERROR-FIELDS */ export function createBackendErrorMessage(error: BackendError) { const writer = new BufferWriter(); diff --git a/packages/pg-gateway/src/buffer-reader.ts b/packages/pg-gateway/src/buffer-reader.ts index 994ae06..eddefc3 100644 --- a/packages/pg-gateway/src/buffer-reader.ts +++ b/packages/pg-gateway/src/buffer-reader.ts @@ -1,61 +1,71 @@ -const emptyBuffer = Buffer.allocUnsafe(0); - /** - * binary data reader tuned for decoding binary specific to the postgres binary protocol + * Binary data reader tuned for decoding the Postgres wire protocol. * * @see https://github.com/brianc/node-postgres/blob/54eb0fa216aaccd727765641e7d1cf5da2bc483d/packages/pg-protocol/src/buffer-reader.ts */ export class BufferReader { - private buffer: Buffer = emptyBuffer; - - // TODO(bmc): support non-utf8 encoding? - private encoding = 'utf-8' as const; + private buffer = new Uint8Array(); + private decoder = new TextDecoder(); constructor(private offset = 0) {} - public setBuffer(offset: number, buffer: Buffer): void { - this.offset = offset; + public setBuffer(buffer: Uint8Array, offset = 0): void { this.buffer = buffer; + this.offset = offset; } public int16(): number { - const result = this.buffer.readInt16BE(this.offset); + const dataView = new DataView( + this.buffer.buffer, + this.buffer.byteOffset, + this.buffer.byteLength, + ); + const result = dataView.getInt16(this.offset); this.offset += 2; return result; } public byte(): number { - // biome-ignore lint/style/noNonNullAssertion: - const result = this.buffer[this.offset]!; + const dataView = new DataView( + this.buffer.buffer, + this.buffer.byteOffset, + this.buffer.byteLength, + ); + const result = dataView.getUint8(this.offset); this.offset++; return result; } public int32(): number { - const result = this.buffer.readInt32BE(this.offset); + const dataView = new DataView( + this.buffer.buffer, + this.buffer.byteOffset, + this.buffer.byteLength, + ); + const result = dataView.getInt32(this.offset); this.offset += 4; return result; } public string(length: number): string { - const result = this.buffer.toString( - this.encoding, - this.offset, - this.offset + length, - ); + const dataView = new DataView(this.buffer.buffer, this.offset, length); this.offset += length; - return result; + return this.decoder.decode(dataView); } public cstring(): string { const start = this.offset; let end = start; - while (this.buffer[end++] !== 0) {} + while ((this.buffer[end++] ?? 0) !== 0) {} + if (this.buffer[end - 1] === undefined) { + throw new Error('Reached end of buffer before null character found for PG String'); + } + const dataView = new DataView(this.buffer.buffer, start, end - start - 1); this.offset = end; - return this.buffer.toString(this.encoding, start, end - 1); + return this.decoder.decode(dataView); } - public bytes(length: number): Buffer { + public bytes(length: number): Uint8Array { const result = this.buffer.slice(this.offset, this.offset + length); this.offset += length; return result; diff --git a/packages/pg-gateway/src/buffer-writer.ts b/packages/pg-gateway/src/buffer-writer.ts index da9f28a..f583d1a 100644 --- a/packages/pg-gateway/src/buffer-writer.ts +++ b/packages/pg-gateway/src/buffer-writer.ts @@ -1,14 +1,18 @@ +import { copy } from '@std/bytes/copy'; + /** * binary data BufferWriter tuned for encoding binary specific to the postgres binary protocol * * @see https://github.com/brianc/node-postgres/blob/54eb0fa216aaccd727765641e7d1cf5da2bc483d/packages/pg-protocol/src/buffer- BufferWriter.ts */ export class BufferWriter { - private buffer: Buffer; + private buffer: Uint8Array; private offset = 5; private headerPosition = 0; + private encoder = new TextEncoder(); + constructor(private size = 256) { - this.buffer = Buffer.allocUnsafe(size); + this.buffer = new Uint8Array(size); } private ensure(size: number): void { @@ -18,8 +22,8 @@ export class BufferWriter { // exponential growth factor of around ~ 1.5 // https://stackoverflow.com/questions/2269063/buffer-growth-strategy const newSize = oldBuffer.length + (oldBuffer.length >> 1) + size; - this.buffer = Buffer.allocUnsafe(newSize); - oldBuffer.copy(this.buffer); + this.buffer = new Uint8Array(newSize); + copy(oldBuffer, this.buffer); } } @@ -43,10 +47,10 @@ export class BufferWriter { if (!string) { this.ensure(1); } else { - const len = Buffer.byteLength(string); - this.ensure(len + 1); // +1 for null terminator - this.buffer.write(string, this.offset, 'utf-8'); - this.offset += len; + const stringBuffer = this.encoder.encode(string); + this.ensure(stringBuffer.byteLength + 1); // +1 for null terminator + this.buffer.set(stringBuffer, this.offset); + this.offset += stringBuffer.byteLength; } this.buffer[this.offset++] = 0; // null terminator @@ -54,35 +58,40 @@ export class BufferWriter { } public addString(string = ''): BufferWriter { - const len = Buffer.byteLength(string); - this.ensure(len); - this.buffer.write(string, this.offset); - this.offset += len; + const stringBuffer = this.encoder.encode(string); + this.ensure(stringBuffer.byteLength); + this.buffer.set(stringBuffer, this.offset); + this.offset += stringBuffer.byteLength; return this; } - public add(otherBuffer: Buffer): BufferWriter { - this.ensure(otherBuffer.length); - otherBuffer.copy(this.buffer, this.offset); + public add(otherBuffer: Uint8Array): BufferWriter { + this.ensure(otherBuffer.byteLength); + copy(otherBuffer, this.buffer, this.offset); this.offset += otherBuffer.length; return this; } - private join(code?: number): Buffer { + private join(code?: number) { if (code) { this.buffer[this.headerPosition] = code; - //length is everything in this packet minus the code + // length is everything in this packet minus the code const length = this.offset - (this.headerPosition + 1); - this.buffer.writeInt32BE(length, this.headerPosition + 1); + const dataView = new DataView( + this.buffer.buffer, + this.buffer.byteOffset, + this.buffer.byteLength, + ); + dataView.setInt32(this.headerPosition + 1, length); } return this.buffer.slice(code ? 0 : 5, this.offset); } - public flush(code?: number): Buffer { + public flush(code?: number) { const result = this.join(code); this.offset = 5; this.headerPosition = 0; - this.buffer = Buffer.allocUnsafe(this.size); + this.buffer = new Uint8Array(this.size); return result; } } diff --git a/packages/pg-gateway/src/connection.ts b/packages/pg-gateway/src/connection.ts index 26a8f17..bc4e217 100644 --- a/packages/pg-gateway/src/connection.ts +++ b/packages/pg-gateway/src/connection.ts @@ -1,42 +1,34 @@ -import type { Socket } from 'node:net'; -import type { TLSSocket } from 'node:tls'; -import { BufferReader } from './buffer-reader.js'; -import { BufferWriter } from './buffer-writer.js'; - import type { AuthFlow } from './auth/base-auth-flow.js'; import { type AuthOptions, createAuthFlow } from './auth/index.js'; +import { createBackendErrorMessage } from './backend-error.js'; +import { BufferReader } from './buffer-reader.js'; +import { BufferWriter } from './buffer-writer.js'; import { - type BackendError, - createBackendErrorMessage, -} from './backend-error.js'; -import { - type ClientInfo, + type ClientParameters, type ConnectionState, ServerStep, type TlsInfo, } from './connection.types.js'; +import { AsyncIterableWithMetadata } from './iterable-util.js'; import { MessageBuffer } from './message-buffer.js'; import { BackendMessageCode, FrontendMessageCode } from './message-codes.js'; -import { upgradeTls } from './tls.js'; +import { tlsUpgradeSignal, closeSignal, type ConnectionSignal } from './signals.js'; +import { type DuplexStream, toAsyncIterator } from './streams.js'; export type TlsOptions = { - key: Buffer; - cert: Buffer; - ca?: Buffer; + key: ArrayBuffer; + cert: ArrayBuffer; + ca?: ArrayBuffer; passphrase?: string; }; -export type TlsOptionsCallback = ( - tlsInfo: TlsInfo, -) => TlsOptions | Promise; +export type TlsOptionsCallback = (serverName?: string) => TlsOptions | Promise; export type PostgresConnectionOptions = { /** * The server version to send to the frontend. */ - serverVersion?: - | string - | ((state: ConnectionState) => string | Promise); + serverVersion?: string | ((state: ConnectionState) => string | Promise); /** * The authentication mode for the server. @@ -95,10 +87,7 @@ export type PostgresConnectionOptions = { * If you wish to hook into messages without bypassing further processing, do not return * any data from this callback. */ - onMessage?( - data: Uint8Array, - state: ConnectionState, - ): MessageResponse | Promise; + onMessage?(data: Uint8Array, state: ConnectionState): MessageResponse | Promise; /** * Callback for every frontend query message. @@ -110,15 +99,34 @@ export type PostgresConnectionOptions = { * TODO: change return signature to be more developer-friendly * and then translate to wire protocol. */ - onQuery?( - query: string, - state: ConnectionState, - ): Uint8Array | Promise; + onQuery?(query: string, state: ConnectionState): Uint8Array | Promise; +}; + +/** + * Platform-specific adapters for handling features like TLS upgrades. + * + * Some platform helpers like `fromNodeSocket()` will implement these + * for you. + */ +export type PostgresConnectionAdapters = { + /** + * Implements the TLS upgrade logic for the stream. + */ + upgradeTls?( + duplex: DuplexStream, + options: TlsOptions | TlsOptionsCallback, + requestCert?: boolean, + ): Promise<{ + duplex: DuplexStream; + tlsInfo: TlsInfo; + }>; }; export type MessageResponse = - // biome-ignore lint/suspicious/noConfusingVoidType: this is a return type - void | Uint8Array | Iterable | AsyncIterable; + | undefined + | Uint8Array + | Iterable + | AsyncIterable; export default class PostgresConnection { private step: ServerStep = ServerStep.AwaitingInitialMessage; @@ -126,124 +134,159 @@ export default class PostgresConnection { auth: NonNullable; }; authFlow?: AuthFlow; - secureSocket?: TLSSocket; hasStarted = false; isAuthenticated = false; detached = false; writer = new BufferWriter(); reader = new BufferReader(); - clientInfo?: ClientInfo; + clientParams?: ClientParameters; tlsInfo?: TlsInfo; messageBuffer = new MessageBuffer(); - boundDataHandler: (data: Buffer) => Promise; constructor( - public socket: Socket, + public duplex: DuplexStream, options: PostgresConnectionOptions = {}, + public adapters: PostgresConnectionAdapters = {}, ) { this.options = { auth: { method: 'trust' }, ...options, }; - this.boundDataHandler = this.handleData.bind(this); - this.createSocketHandlers(socket); + if (this.options.tls && !this.adapters.upgradeTls) { + throw new Error( + 'TLS options are only available when upgradeTls() is implemented. Did you mean to use fromNodeSocket()?', + ); + } + this.init(duplex); } get state(): ConnectionState { return { hasStarted: this.hasStarted, isAuthenticated: this.isAuthenticated, - clientInfo: this.clientInfo, + clientParams: this.clientParams, tlsInfo: this.tlsInfo, step: this.step, }; } - createSocketHandlers(socket: Socket) { - socket.on('data', this.boundDataHandler); - socket.on('error', this.handleSocketError); - } + async init(duplex: DuplexStream) { + try { + const signal = await this.processData(duplex); - handleSocketError = (error: Error) => { - // Ignore EPIPE and ECONNRESET errors as they are normal when the client disconnects - if ( - 'code' in error && - (error.code === 'EPIPE' || error.code === 'ECONNRESET') - ) { - return; - } + if (this.detached) { + return; + } - console.error('Socket error:', error); - }; + if (signal === tlsUpgradeSignal) { + if (!this.options.tls) { + throw new Error('Upgrading TLS but TLS options are not available'); + } + if (!this.adapters.upgradeTls) { + throw new Error('Upgrading TLS but upgradeTls is not implemented'); + } - removeSocketHandlers(socket: Socket) { - socket.off('data', this.boundDataHandler); - socket.off('error', this.handleSocketError); - } + const requestCert = this.options.auth.method === 'cert'; + const { duplex: secureDuplex, tlsInfo } = await this.adapters.upgradeTls( + duplex, + this.options.tls, + requestCert, + ); + + this.duplex = secureDuplex; + this.tlsInfo = tlsInfo; + this.messageBuffer = new MessageBuffer(); + + await this.options.onTlsUpgrade?.(this.state); + if (this.detached) { + return; + } + + await this.init(secureDuplex); + return; + } + + if (signal === closeSignal) { + await this.duplex.writable.close(); + return; + } + } catch (err) { + await this.duplex.writable.abort(); + console.error(err); + } + } /** - * Detaches the `PostgresConnection` from the socket. - * After calling this, no more handlers will be called - * and data will no longer be buffered. + * Detaches the `PostgresConnection` from the stream. + * After calling this, data will no longer be buffered + * and all processing will halt. * - * @returns The underlying socket (which could have been upgraded to a `TLSSocket`) + * Useful when proxying. You can detach at a certain point + * (like TLS upgrade) to prevent further buffering/processing + * when your goal is to pipe future messages downstream. */ - detach() { - this.removeSocketHandlers(this.socket); + async detach() { this.detached = true; - return this.socket; - } - /** - * Ends the `PostgresConnection`. - * The socket will be closed and no more handlers will be called. - */ - end() { - this.removeSocketHandlers(this.socket); - this.socket.end(); + // TODO: inject lingering `messageBuffer` (if any) back + // onto stream to prevent data loss + return this.duplex; } - /** - * Processes incoming data by buffering it and parsing messages. - * - * Inspired by https://github.com/brianc/node-postgres/blob/54eb0fa216aaccd727765641e7d1cf5da2bc483d/packages/pg-protocol/src/parser.ts#L91-L119 - */ - async handleData(data: Buffer) { + async processData(duplex: DuplexStream): Promise { + const writer = duplex.writable.getWriter(); try { - this.messageBuffer.mergeBuffer(data); - await this.messageBuffer.processMessages( - this.handleClientMessage.bind(this), - this.hasStarted, - ); - } catch (err) { - console.error(err); + for await (const data of toAsyncIterator(duplex.readable, { preventCancel: true })) { + this.messageBuffer.mergeBuffer(data); + for await (const clientMessage of this.messageBuffer.processMessages(this.hasStarted)) { + for await (const responseMessage of this.handleClientMessage(clientMessage)) { + if (this.detached) { + return; + } + if (responseMessage === tlsUpgradeSignal) { + return tlsUpgradeSignal; + } + if (responseMessage === closeSignal) { + return closeSignal; + } + await writer.write(responseMessage); + } + } + } + } finally { + writer.releaseLock(); } } - async handleClientMessage(message: Buffer): Promise { - this.reader.setBuffer(0, message); + async *handleClientMessage( + message: Uint8Array, + ): AsyncGenerator { + this.reader.setBuffer(message); - this.socket.pause(); - let skipProcessing = false; const messageResponse = await this.options.onMessage?.(message, this.state); + // Returning any value indicates no further processing + let skipProcessing = messageResponse !== undefined; + // A `Uint8Array` or `Iterator` or `AsyncIterator` // can be returned that contains raw message response data if (messageResponse) { - const iterableResponse = - messageResponse instanceof Uint8Array - ? [messageResponse] - : messageResponse; - - // Asynchronously stream responses back to client - for await (const responseData of iterableResponse) { - // Skip built-in processing if any response data is sent - skipProcessing = true; - this.sendData(responseData); + const iterableResponse = new AsyncIterableWithMetadata( + messageResponse instanceof Uint8Array ? [messageResponse] : messageResponse, + ); + + // Forward yielded responses back to client + yield* iterableResponse; + + // Yield any `Uint8Array` values returned from the iterator + if (iterableResponse.returnValue instanceof Uint8Array) { + yield iterableResponse.returnValue; } - } - this.socket.resume(); + // Yielding or returning any value within the iterator indicates no further processing + skipProcessing = + iterableResponse.iterations > 0 || iterableResponse.returnValue !== undefined; + } // the socket was detached during onMessage, we skip further processing if (this.detached) { @@ -260,33 +303,35 @@ export default class PostgresConnection { switch (this.step) { case ServerStep.AwaitingInitialMessage: if (this.isSslRequest(message)) { - await this.handleSslRequest(); + yield* this.handleSslRequest(); } else if (this.isStartupMessage(message)) { // Guard against SSL connection not being established when `tls` is enabled - if (this.options.tls && !this.secureSocket) { - this.sendError({ + if (this.options.tls && !this.tlsInfo) { + yield createBackendErrorMessage({ severity: 'FATAL', code: '08P01', message: 'SSL connection is required', }); - this.socket.end(); + yield closeSignal; return; } // the next step is determined by handleStartupMessage - this.handleStartupMessage(message); + yield* this.handleStartupMessage(message); } else { throw new Error('Unexpected initial message'); } break; - case ServerStep.PerformingAuthentication: - if ((await this.handleAuthenticationMessage(message)) === true) { - await this.completeAuthentication(); + case ServerStep.PerformingAuthentication: { + const authenticationComplete = yield* this.handleAuthenticationMessage(message); + if (authenticationComplete) { + yield* this.completeAuthentication(); } break; + } case ServerStep.ReadyForQuery: - await this.handleRegularMessage(message); + yield* this.handleRegularMessage(message); break; default: @@ -294,96 +339,89 @@ export default class PostgresConnection { } } - async handleSslRequest() { - if (!this.options.tls) { + async *handleSslRequest() { + if (!this.options.tls || !this.adapters.upgradeTls) { this.writer.addString('N'); - const result = this.writer.flush(); - this.sendData(result); + yield this.writer.flush(); return; } // Otherwise respond with 'S' to indicate it is supported this.writer.addString('S'); - const result = this.writer.flush(); - this.sendData(result); + yield this.writer.flush(); // From now on the frontend will communicate via TLS, so upgrade the connection - await this.upgradeToTls(this.options.tls); + yield tlsUpgradeSignal; } - async handleStartupMessage(message: Buffer) { - const { majorVersion, minorVersion, parameters } = - this.readStartupMessage(); + async *handleStartupMessage(message: BufferSource) { + const { majorVersion, minorVersion, parameters } = this.readStartupMessage(); // user is required if (!parameters.user) { - this.sendError({ + yield createBackendErrorMessage({ severity: 'FATAL', code: '08000', message: 'user is required', }); - this.socket.end(); + yield closeSignal; return; } if (majorVersion !== 3 || minorVersion !== 0) { - this.sendError({ + yield createBackendErrorMessage({ severity: 'FATAL', code: '08000', message: `Unsupported protocol version ${majorVersion.toString()}.${minorVersion.toString()}`, }); - this.socket.end(); + yield closeSignal; return; } - this.clientInfo = { - majorVersion, - minorVersion, - parameters: { - user: parameters.user, - ...parameters, - }, + this.clientParams = { + user: parameters.user, + ...parameters, }; this.hasStarted = true; - this.socket.pause(); await this.options.onStartup?.(this.state); - this.socket.resume(); - // the socket was detached during onStartup, we skip further processing if (this.detached) { return; } if (this.options.auth.method === 'trust') { - await this.completeAuthentication(); + yield* this.completeAuthentication(); return; } this.authFlow = createAuthFlow({ - socket: this.socket, reader: this.reader, writer: this.writer, - username: this.clientInfo.parameters.user, + username: this.clientParams.user, auth: this.options.auth, connectionState: this.state, }); this.step = ServerStep.PerformingAuthentication; - this.authFlow.sendInitialAuthMessage(); + const initialAuthMessage = this.authFlow.createInitialAuthMessage(); + + if (initialAuthMessage) { + yield initialAuthMessage; + } // 'cert' auth flow is an edge case // it doesn't expect a new message from the client so we can directly proceed if (this.options.auth.method === 'cert') { - await this.authFlow.handleClientMessage(message); + yield* this.authFlow.handleClientMessage(message); if (this.authFlow.isCompleted) { - await this.completeAuthentication(); + yield* this.completeAuthentication(); } } } - async handleAuthenticationMessage(message: Buffer) { + async *handleAuthenticationMessage(message: BufferSource) { const code = this.reader.byte(); if (code !== FrontendMessageCode.Password) { @@ -394,42 +432,40 @@ export default class PostgresConnection { throw new Error('AuthFlow not initialized'); } - await this.authFlow.handleClientMessage(message); + yield* this.authFlow.handleClientMessage(message); return this.authFlow.isCompleted; } - private async handleRegularMessage(message: Buffer): Promise { + private async *handleRegularMessage(message: BufferSource) { const code = this.reader.byte(); switch (code) { case FrontendMessageCode.Terminate: - this.handleTerminate(message); - break; + yield closeSignal; + return; default: - this.sendError({ + yield createBackendErrorMessage({ severity: 'ERROR', code: '123', message: 'Message code not yet implemented', }); - this.sendReadyForQuery('idle'); + yield this.createReadyForQuery(); } } - handleTerminate(message: Buffer) { - this.socket.end(); - } - /** * Checks if the given message is a valid SSL request. * * @see https://www.postgresql.org/docs/current/protocol-message-formats.html#PROTOCOL-MESSAGE-FORMATS-SSLREQUEST */ - private isSslRequest(message: Buffer): boolean { - if (message.length !== 8) return false; + private isSslRequest(message: Uint8Array): boolean { + if (message.byteLength !== 8) return false; - const mostSignificantPart = message.readInt16BE(4); - const leastSignificantPart = message.readInt16BE(6); + const dataView = new DataView(message.buffer, message.byteOffset, message.byteLength); + + const mostSignificantPart = dataView.getInt16(4); + const leastSignificantPart = dataView.getInt16(6); return mostSignificantPart === 1234 && leastSignificantPart === 5679; } @@ -439,70 +475,41 @@ export default class PostgresConnection { * * @see https://www.postgresql.org/docs/current/protocol-message-formats.html#PROTOCOL-MESSAGE-FORMATS-STARTUPMESSAGE */ - private isStartupMessage(message: Buffer): boolean { - if (message.length < 8) return false; + private isStartupMessage(message: Uint8Array): boolean { + if (message.byteLength < 8) return false; + + const dataView = new DataView(message.buffer, message.byteOffset, message.byteLength); - const length = message.readInt32BE(0); - const majorVersion = message.readInt16BE(4); - const minorVersion = message.readInt16BE(6); + const length = dataView.getInt32(0); + const majorVersion = dataView.getInt16(4); + const minorVersion = dataView.getInt16(6); - return ( - message.length === length && majorVersion === 3 && minorVersion === 0 - ); + return message.byteLength === length && majorVersion === 3 && minorVersion === 0; } /** * Completes authentication by forwarding the appropriate messages * to the frontend. */ - async completeAuthentication() { + async *completeAuthentication() { this.isAuthenticated = true; - this.sendAuthenticationOk(); + yield this.createAuthenticationOk(); - this.socket.pause(); await this.options.onAuthenticated?.(this.state); - this.socket.resume(); if (this.options.serverVersion) { let serverVersion: string; if (typeof this.options.serverVersion === 'function') { - this.socket.pause(); serverVersion = await this.options.serverVersion(this.state); - this.socket.resume(); } else { serverVersion = this.options.serverVersion; } - this.sendParameterStatus('server_version', serverVersion); + yield this.createParameterStatus('server_version', serverVersion); } this.step = ServerStep.ReadyForQuery; - this.sendReadyForQuery('idle'); - } - - /** - * Upgrades TCP socket connection to TLS. - */ - async upgradeToTls(options: TlsOptions | TlsOptionsCallback) { - const requestCert = this.options.auth.method === 'cert'; - - const { secureSocket, tlsInfo } = await upgradeTls( - this.socket, - options, - {}, - requestCert, - ); - - this.tlsInfo = tlsInfo; - this.secureSocket = secureSocket; - - this.removeSocketHandlers(this.socket); - this.createSocketHandlers(this.secureSocket); - this.socket = this.secureSocket; - - await this.options.onTlsUpgrade?.(this.state); - - this.secureSocket.resume(); + yield this.createReadyForQuery(); } /** @@ -543,47 +550,34 @@ export default class PostgresConnection { } /** - * Sends raw data to the frontend. - */ - sendData(data: Uint8Array) { - this.socket.write(data); - } - - /** - * Sends an "AuthenticationOk" message to the frontend. + * Creates an "AuthenticationOk" message. * * @see https://www.postgresql.org/docs/current/protocol-message-formats.html#PROTOCOL-MESSAGE-FORMATS-AUTHENTICATIONOK */ - sendAuthenticationOk() { + createAuthenticationOk() { this.writer.addInt32(0); - const response = this.writer.flush( - BackendMessageCode.AuthenticationResponse, - ); - this.sendData(response); + return this.writer.flush(BackendMessageCode.AuthenticationResponse); } /** - * Sends an "ParameterStatus" message to the frontend. + * Creates a "ParameterStatus" message. * Informs the frontend about the current setting of backend parameters. * * @see https://www.postgresql.org/docs/current/protocol-message-formats.html#PROTOCOL-MESSAGE-FORMATS-PARAMETERSTATUS * @see https://www.postgresql.org/docs/current/protocol-flow.html#PROTOCOL-ASYNC */ - sendParameterStatus(name: string, value: string) { + createParameterStatus(name: string, value: string) { this.writer.addCString(name); this.writer.addCString(value); - const response = this.writer.flush(BackendMessageCode.ParameterStatus); - this.sendData(response); + return this.writer.flush(BackendMessageCode.ParameterStatus); } /** - * Sends a "ReadyForQuery" message to the frontend. + * Creates a "ReadyForQuery" message. * * @see https://www.postgresql.org/docs/current/protocol-message-formats.html#PROTOCOL-MESSAGE-FORMATS-READYFORQUERY */ - sendReadyForQuery( - transactionStatus: 'idle' | 'transaction' | 'error' = 'idle', - ) { + createReadyForQuery(transactionStatus: 'idle' | 'transaction' | 'error' = 'idle') { switch (transactionStatus) { case 'idle': this.writer.addString('I'); @@ -598,28 +592,15 @@ export default class PostgresConnection { throw new Error(`Unknown transaction status '${transactionStatus}'`); } - const response = this.writer.flush(BackendMessageCode.ReadyForQuery); - this.sendData(response); - } - - /** - * Sends an error message to the frontend. - * - * @see https://www.postgresql.org/docs/current/protocol-message-formats.html#PROTOCOL-MESSAGE-FORMATS-ERRORRESPONSE - * - * For error fields, see https://www.postgresql.org/docs/current/protocol-error-fields.html#PROTOCOL-ERROR-FIELDS - */ - sendError(error: BackendError) { - const errorMessage = createBackendErrorMessage(error); - this.sendData(errorMessage); + return this.writer.flush(BackendMessageCode.ReadyForQuery); } - sendAuthenticationFailedError() { - this.sendError({ + createAuthenticationFailedError() { + return createBackendErrorMessage({ severity: 'FATAL', code: '28P01', - message: this.clientInfo?.parameters.user - ? `password authentication failed for user "${this.clientInfo.parameters.user}"` + message: this.clientParams?.user + ? `password authentication failed for user "${this.clientParams.user}"` : 'password authentication failed', }); } diff --git a/packages/pg-gateway/src/connection.types.ts b/packages/pg-gateway/src/connection.types.ts index da67982..c2df18d 100644 --- a/packages/pg-gateway/src/connection.types.ts +++ b/packages/pg-gateway/src/connection.types.ts @@ -3,14 +3,9 @@ export type ClientParameters = { [key: string]: string; }; -export type ClientInfo = { - majorVersion: number; - minorVersion: number; - parameters: ClientParameters; -}; - export type TlsInfo = { - sniServerName?: string; + serverName?: string; + clientCertificate?: Uint8Array; }; export const ServerStep = { @@ -24,7 +19,7 @@ export type ServerStep = (typeof ServerStep)[keyof typeof ServerStep]; export type ConnectionState = { hasStarted: boolean; isAuthenticated: boolean; - clientInfo?: ClientInfo; + clientParams?: ClientParameters; tlsInfo?: TlsInfo; step: ServerStep; }; diff --git a/packages/pg-gateway/src/crypto.ts b/packages/pg-gateway/src/crypto.ts new file mode 100644 index 0000000..d6fa55a --- /dev/null +++ b/packages/pg-gateway/src/crypto.ts @@ -0,0 +1,120 @@ +/** + * Derives a cryptographic key from a password using the PBKDF2 algorithm. + * + * This function uses Web Crypto's `SubtleCrypto` API to derive a key from + * a given password, salt, iteration count, and hash algorithm. The derived + * key is returned as raw bits (an `ArrayBuffer`) and can be used in subsequent + * cryptographic operations. + * + * PBKDF2 (Password-Based Key Derivation Function 2) is a widely used key + * derivation function that applies a selected HMAC digest algorithm (e.g., SHA-256) + * to derive a key of the requested length. + */ +export async function pbkdf2( + password: string, + salt: ArrayBuffer, + iterations: number, + length: number, + hashAlgorithm: 'SHA-256' | 'SHA-384' | 'SHA-512', +) { + const encoder = new TextEncoder(); + + const keyMaterial = await crypto.subtle.importKey( + 'raw', + encoder.encode(password), + 'PBKDF2', + false, + ['deriveBits'], + ); + + const derivedBits = await crypto.subtle.deriveBits( + { + name: 'PBKDF2', + salt, + iterations: iterations, + hash: hashAlgorithm, // use the specified hash algorithm + }, + keyMaterial, + length * 8, // length in bits (e.g., 32 bytes * 8 = 256 bits) + ); + + return derivedBits; +} + +/** + * Creates an HMAC key and generates a message authentication code (MAC) + * for the given message using the specified hash algorithm. + * + * This function first imports the provided key as a `CryptoKey` using + * the HMAC algorithm and specified hash function. It then generates an + * HMAC for the given message. The HMAC is returned as an `ArrayBuffer`. + * + * HMAC (Hash-based Message Authentication Code) is a mechanism that uses + * a cryptographic hash function along with a secret key to ensure data integrity + * and authenticity. + */ +export async function createHmacKey( + key: ArrayBuffer, + message: string, + algorithm: 'SHA-256' | 'SHA-384' | 'SHA-512', +) { + const encoder = new TextEncoder(); + + const keyMaterial = await crypto.subtle.importKey( + 'raw', + key, + { + name: 'HMAC', + hash: { name: algorithm }, + }, + false, + ['sign'], + ); + + const hmacKey = await crypto.subtle.sign('HMAC', keyMaterial, encoder.encode(message)); + + return hmacKey; +} + +/** + * Computes a cryptographic hash of the given key using the specified hash algorithm. + * + * This function utilizes Web Crypto's `SubtleCrypto.digest` method to compute + * a hash of the provided key material. The resulting hash is returned as an + * `ArrayBuffer`. This can be used to create a hashed version of a key or any + * other binary data. + */ +export async function createHashKey( + key: ArrayBuffer, + algorithm: 'SHA-256' | 'SHA-384' | 'SHA-512', +) { + const hashKey = await crypto.subtle.digest( + { + name: algorithm, + }, + key, + ); + + return hashKey; +} + +/** + * Implements a timing-safe equal function that compares two buffers in + * constant time. + * + * Web Crypto's `SubtleCrypto` API does not provide a built-in timing-safe + * equal function, but this logic can be implemented by leveraging HMACs, + * which inherently require constant-time comparisons for verification. + * + * This function generates an HMAC for the first buffer and uses Web Crypto's + * `verify` method to perform a timing-safe comparison against the second buffer. + * + * @see https://github.com/w3c/webcrypto/issues/270#issuecomment-1899234835 + */ +export async function timingSafeEqual(bufferA: BufferSource, bufferB: BufferSource) { + const algorithm: HmacKeyGenParams = { name: 'HMAC', hash: 'SHA-256' }; + const key = await crypto.subtle.generateKey(algorithm, false, ['sign', 'verify']); + const hmac = await crypto.subtle.sign(algorithm, key, bufferA); + const equal = await crypto.subtle.verify(algorithm, key, hmac, bufferB); + return equal; +} diff --git a/packages/pg-gateway/src/index.ts b/packages/pg-gateway/src/index.ts index 57729a0..fe44ade 100644 --- a/packages/pg-gateway/src/index.ts +++ b/packages/pg-gateway/src/index.ts @@ -1,5 +1,10 @@ export { default as PostgresConnection } from './connection.js'; export * from './connection.js'; +export * from './connection.types.js'; export * from './auth/sasl/scram-sha-256.js'; export * from './auth/md5.js'; export * from './backend-error.js'; +export * from './streams.js'; +export * from './message-codes.js'; +export * from './message-buffer.js'; +export * from './signals.js'; diff --git a/packages/pg-gateway/src/iterable-util.ts b/packages/pg-gateway/src/iterable-util.ts new file mode 100644 index 0000000..8764570 --- /dev/null +++ b/packages/pg-gateway/src/iterable-util.ts @@ -0,0 +1,36 @@ +/** + * An `AsyncIterable` that captures the following metadata during iteration: + * - `iterations`: the number of times the iterable has been iterated over + * - `returnValue`: the final return value (if any) + * + * Useful when iterating using the `for await ... of` syntax while + * still being able to access the return value after. + */ +export class AsyncIterableWithMetadata implements AsyncIterable { + public returnValue: TReturn | undefined = undefined; + public iterations = 0; + + constructor(private iterable: Iterable | AsyncIterable) {} + + [Symbol.asyncIterator](): AsyncIterator { + const asyncIterator = + Symbol.asyncIterator in this.iterable + ? this.iterable[Symbol.asyncIterator]() + : this.iterable[Symbol.iterator](); + + this.returnValue = undefined; + this.iterations = 0; + + return { + next: async () => { + const result = await asyncIterator.next(); + if (result.done) { + this.returnValue = result.value; + } else { + this.iterations++; + } + return result; + }, + }; + } +} diff --git a/packages/pg-gateway/src/message-buffer.ts b/packages/pg-gateway/src/message-buffer.ts index 40866c3..75dad2a 100644 --- a/packages/pg-gateway/src/message-buffer.ts +++ b/packages/pg-gateway/src/message-buffer.ts @@ -1,8 +1,10 @@ +import { copy } from '@std/bytes/copy'; + /** * Handles buffering of messages for a connection */ export class MessageBuffer { - private buffer: Buffer = Buffer.alloc(0); + private buffer = new Uint8Array(); private bufferLength = 0; private bufferOffset = 0; @@ -11,35 +13,31 @@ export class MessageBuffer { * * @see https://github.com/brianc/node-postgres/blob/54eb0fa216aaccd727765641e7d1cf5da2bc483d/packages/pg-protocol/src/parser.ts#L121-L152 */ - mergeBuffer(newData: Buffer): void { + mergeBuffer(newData: Uint8Array): void { if (this.bufferLength > 0) { const newLength = this.bufferLength + newData.byteLength; const newFullLength = newLength + this.bufferOffset; if (newFullLength > this.buffer.byteLength) { - let newBuffer: Buffer; - if ( - newLength <= this.buffer.byteLength && - this.bufferOffset >= this.bufferLength - ) { + let newBuffer: Uint8Array; + if (newLength <= this.buffer.byteLength && this.bufferOffset >= this.bufferLength) { newBuffer = this.buffer; } else { let newBufferLength = this.buffer.byteLength * 2; while (newLength >= newBufferLength) { newBufferLength *= 2; } - newBuffer = Buffer.allocUnsafe(newBufferLength); + newBuffer = new Uint8Array(newBufferLength); } - this.buffer.copy( - newBuffer, - 0, + const bufferView = this.buffer.subarray( this.bufferOffset, this.bufferOffset + this.bufferLength, ); + copy(bufferView, newBuffer, 0); this.buffer = newBuffer; this.bufferOffset = 0; } - newData.copy(this.buffer, this.bufferOffset + this.bufferLength); + copy(newData, this.buffer, this.bufferOffset + this.bufferLength); this.bufferLength = newLength; } else { this.buffer = newData; @@ -53,10 +51,7 @@ export class MessageBuffer { * * @see https://github.com/brianc/node-postgres/blob/54eb0fa216aaccd727765641e7d1cf5da2bc483d/packages/pg-protocol/src/parser.ts#L91-L119 */ - async processMessages( - messageHandler: (message: Buffer) => Promise, - hasStarted: boolean, - ): Promise { + async *processMessages(hasStarted: boolean) { const bufferFullLength = this.bufferOffset + this.bufferLength; let offset = this.bufferOffset; @@ -68,18 +63,15 @@ export class MessageBuffer { while (offset + headerLength <= bufferFullLength) { // The length passed in the message header - const length = this.buffer.readUInt32BE(offset + codeLength); + const dataView = new DataView(this.buffer.buffer); + const length = dataView.getUint32(offset + codeLength); // The length passed in the message header does not include the first single // byte code, so we account for it here const fullMessageLength = codeLength + length; if (offset + fullMessageLength <= bufferFullLength) { - const messageData = this.buffer.subarray( - offset, - offset + fullMessageLength, - ); - await messageHandler(messageData); + yield this.buffer.subarray(offset, offset + fullMessageLength); offset += fullMessageLength; } else { break; @@ -87,7 +79,7 @@ export class MessageBuffer { } if (offset === bufferFullLength) { - this.buffer = Buffer.alloc(0); + this.buffer = new Uint8Array(); this.bufferLength = 0; this.bufferOffset = 0; } else { @@ -96,3 +88,18 @@ export class MessageBuffer { } } } + +export function* getMessages(data: Uint8Array) { + const dataView = new DataView(data.buffer, data.byteOffset, data.byteLength); + let offset = 0; + + if (dataView.byteLength === 0) { + return; + } + + while (offset < dataView.byteLength) { + const length = dataView.getUint32(offset + 1); + yield data.subarray(offset, offset + length + 1); + offset += length + 1; + } +} diff --git a/packages/pg-gateway/src/message-codes.ts b/packages/pg-gateway/src/message-codes.ts index 31e2660..61672ea 100644 --- a/packages/pg-gateway/src/message-codes.ts +++ b/packages/pg-gateway/src/message-codes.ts @@ -48,3 +48,11 @@ export const BackendMessageCode = { CopyDone: 0x63, // c CopyData: 0x64, // d } as const; + +export function getFrontendMessageName(code: number) { + return Object.entries(FrontendMessageCode).find(([_, value]) => value === code)?.[0]; +} + +export function getBackendMessageName(code: number) { + return Object.entries(BackendMessageCode).find(([_, value]) => value === code)?.[0]; +} diff --git a/packages/pg-gateway/src/platforms/deno/index.ts b/packages/pg-gateway/src/platforms/deno/index.ts new file mode 100644 index 0000000..56887a7 --- /dev/null +++ b/packages/pg-gateway/src/platforms/deno/index.ts @@ -0,0 +1,14 @@ +import PostgresConnection, { type PostgresConnectionOptions } from '../../connection.js'; + +/** + * Creates a `PostgresConnection` from a Deno TCP/Unix `Conn`. + * + * Note Postgres `SSLRequest` upgrades are not yet supported in Deno. + * This feature depends on: + * - https://github.com/denoland/deno/issues/18451 + * - https://github.com/denoland/deno/issues/23233 + */ +// deno-lint-ignore require-await +export async function fromDenoConn(conn: Deno.Conn, options?: PostgresConnectionOptions) { + return new PostgresConnection(conn, options); +} diff --git a/packages/pg-gateway/src/platforms/node/index.ts b/packages/pg-gateway/src/platforms/node/index.ts new file mode 100644 index 0000000..7b4a62e --- /dev/null +++ b/packages/pg-gateway/src/platforms/node/index.ts @@ -0,0 +1,47 @@ +import type { Socket } from 'node:net'; +import { Duplex } from 'node:stream'; +import PostgresConnection, { + type PostgresConnectionAdapters, + type PostgresConnectionOptions, +} from '../../connection.js'; +import { upgradeTls, validateCredentials } from './tls.js'; +import type { DuplexStream } from '../../streams.js'; + +/** + * Creates a `PostgresConnection` from a Node.js TCP/Unix `Socket`. + * + * `PostgresConnection` operates on web streams, so this helper + * converts a `Socket` to/from the respective web streams. + * + * Also implements `upgradeTls()`, which makes Postgres `SSLRequest` + * upgrades available in Node.js environments. + */ +export async function fromNodeSocket(socket: Socket, options?: PostgresConnectionOptions) { + return fromDuplexStream(Duplex.toWeb(socket), options); +} + +/** + * Creates a `PostgresConnection` from a `DuplexStream` with + * Node.js adapters like `upgradeTls()` included. + * + * Useful in Node.js environments when you start from a + * non-Socket stream but want Node.js TLS adapters. + */ +export async function fromDuplexStream( + duplex: DuplexStream, + options?: PostgresConnectionOptions, +) { + const opts: PostgresConnectionOptions = { + ...options, + }; + + if (opts?.auth?.method === 'cert') { + opts.auth.validateCredentials = validateCredentials; + } + + const adapters: PostgresConnectionAdapters = { + upgradeTls, + }; + + return new PostgresConnection(duplex, options, adapters); +} diff --git a/packages/pg-gateway/src/platforms/node/tls.ts b/packages/pg-gateway/src/platforms/node/tls.ts new file mode 100644 index 0000000..529f155 --- /dev/null +++ b/packages/pg-gateway/src/platforms/node/tls.ts @@ -0,0 +1,96 @@ +import { X509Certificate } from 'node:crypto'; +import { Duplex, once } from 'node:stream'; +import { TLSSocket, type TLSSocketOptions, createSecureContext } from 'node:tls'; +import type { TlsOptions, TlsOptionsCallback } from '../../connection.js'; +import type { TlsInfo } from '../../connection.types.js'; +import type { DuplexStream } from '../../streams.js'; + +export async function validateCredentials(credentials: { + username: string; + certificate: Uint8Array; +}) { + const cert = new X509Certificate(Buffer.from(credentials.certificate)); + + const subjectKeyValues: Record = Object.fromEntries( + cert.subject.split(/, ?/).map((entry) => entry.split('=')), + ); + + return 'CN' in subjectKeyValues && subjectKeyValues.CN === credentials.username; +} + +export async function upgradeTls( + duplex: DuplexStream, + options: TlsOptions | TlsOptionsCallback, + requestCert = false, +): Promise<{ + duplex: DuplexStream; + tlsInfo: TlsInfo; +}> { + const tlsInfo: TlsInfo = {}; + const tlsSocketOptions = await createTlsSocketOptions(options, requestCert); + + const secureSocket = new TLSSocket(Duplex.fromWeb(duplex), { + ...tlsSocketOptions, + isServer: true, + SNICallback: async (serverName, callback) => { + tlsInfo.serverName = serverName; + const updatedTlsSocketOptions = await createTlsSocketOptions( + options, + requestCert, + serverName, + ); + callback(null, createSecureContext(updatedTlsSocketOptions)); + }, + }); + + await once(secureSocket, 'secure'); + onServerSocketSecure(secureSocket); + + const peerCertificate = secureSocket.getPeerCertificate(); + + if (peerCertificate && 'raw' in peerCertificate) { + tlsInfo.clientCertificate = new Uint8Array(peerCertificate.raw); + } + + return { + duplex: Duplex.toWeb(secureSocket), + tlsInfo, + }; +} + +async function createTlsSocketOptions( + optionsOrCallback: TlsOptions | TlsOptionsCallback, + requestCert: boolean, + serverName?: string, +): Promise { + const { key, cert, ca, passphrase } = + typeof optionsOrCallback === 'function' + ? await optionsOrCallback(serverName) + : optionsOrCallback; + + return { + key: Buffer.from(key), + cert: Buffer.from(cert), + ca: ca ? Buffer.from(ca) : undefined, + passphrase, + requestCert, + }; +} + +/** + * Internal Node.js handler copied and modified from source to validate client certs. + * https://github.com/nodejs/node/blob/aeaffbb385c9fc756247e6deaa70be8eb8f59496/lib/_tls_wrap.js#L1185-L1203 + * + * Without this, `authorized` is always `false` on the TLSSocket and we never know if the client cert is valid. + */ +// biome-ignore lint/suspicious/noExplicitAny: +function onServerSocketSecure(secureSocket: TLSSocket & any) { + if (secureSocket._requestCert) { + const verifyError = secureSocket._handle.verifyError(); + if (verifyError) { + secureSocket.authorizationError = verifyError.code; + } else { + secureSocket.authorized = true; + } + } +} diff --git a/packages/pg-gateway/src/platforms/web/index.ts b/packages/pg-gateway/src/platforms/web/index.ts new file mode 100644 index 0000000..7f05795 --- /dev/null +++ b/packages/pg-gateway/src/platforms/web/index.ts @@ -0,0 +1,51 @@ +import PostgresConnection, { type PostgresConnectionOptions } from '../../connection.js'; +import type { DuplexStream } from '../../streams.js'; + +/** + * Creates a `PostgresConnection` from a `WebSocketStream`. + * + * Note Postgres `SSLRequest` upgrades are not supported in a `WebSocketStream`. + */ +export async function fromWebSocketStream( + wss: WebSocketStream, + options?: PostgresConnectionOptions, +) { + const duplex = await duplexFromWebSocketStream(wss); + return new PostgresConnection(duplex, options); +} + +/** + * Creates a `DuplexStream` from a `WebSocketStream`. + */ +export async function duplexFromWebSocketStream( + wss: WebSocketStream, +): Promise> { + const { readable, writable } = await wss.opened; + + return { + readable: ensureBinaryStream( + readable, + new Error('WebSocketStream must contain binary data for a PostgresConnection, found text'), + ), + writable, + }; +} + +/** + * Ensures that a `ReadableStream` contains binary data and not text. + */ +function ensureBinaryStream(readable: ReadableStream, error: Error) { + return ReadableStream.from(ensureBinaryIterable(readable, error)); +} + +/** + * Ensures that an `AsyncIterable` contains binary data and not text. + */ +async function* ensureBinaryIterable(iterable: AsyncIterable, error: Error) { + for await (const chunk of iterable) { + if (typeof chunk === 'string') { + throw error; + } + yield chunk; + } +} diff --git a/packages/pg-gateway/src/signals.ts b/packages/pg-gateway/src/signals.ts new file mode 100644 index 0000000..6724da0 --- /dev/null +++ b/packages/pg-gateway/src/signals.ts @@ -0,0 +1,7 @@ +export const tlsUpgradeSignal = Symbol('tls-upgrade'); +export const closeSignal = Symbol('close'); + +export type TlsUpgradeSignal = typeof tlsUpgradeSignal; +export type CloseSignal = typeof closeSignal; + +export type ConnectionSignal = TlsUpgradeSignal | CloseSignal; diff --git a/packages/pg-gateway/src/streams.test.ts b/packages/pg-gateway/src/streams.test.ts new file mode 100644 index 0000000..180a960 --- /dev/null +++ b/packages/pg-gateway/src/streams.test.ts @@ -0,0 +1,69 @@ +import { describe, expect, it } from 'vitest'; +import { createDuplexPair, createVirtualServer } from './streams'; + +describe('createDuplexPair', () => { + it('should transfer data between duplex streams using Uint8Array', async () => { + const [duplexA, duplexB] = createDuplexPair(); + + const writerA = duplexA.writable.getWriter(); + const writerB = duplexB.writable.getWriter(); + + const messageFromA = new Uint8Array([72, 101, 108, 108, 111]); // "Hello" in ASCII + const messageFromB = new Uint8Array([87, 111, 114, 108, 100]); // "World" in ASCII + + await writerA.write(messageFromA); + await writerA.close(); + await writerB.write(messageFromB); + await writerB.close(); + + const decoder = new TextDecoder(); + + for await (const chunk of duplexB.readable) { + const value = decoder.decode(chunk); + expect(value).toBe('Hello'); + break; + } + + for await (const chunk of duplexA.readable) { + const value = decoder.decode(chunk); + expect(value).toBe('World'); + break; + } + }); +}); + +describe('createVirtualServer', () => { + it('should allow a client to connect and exchange data with the server', async () => { + const { listen, connect } = createVirtualServer(); + const decoder = new TextDecoder(); + + const messageFromClient = new Uint8Array([72, 101, 108, 108, 111]); // "Hello" in ASCII + const messageFromServer = new Uint8Array([87, 111, 114, 108, 100]); // "World" in ASCII + + const clientConn = await connect(); + + const clientWriter = clientConn.writable.getWriter(); + await clientWriter.write(messageFromClient); + await clientWriter.close(); + + for await (const conn of listen()) { + for await (const chunk of conn.readable) { + const value = decoder.decode(chunk); + expect(value).toBe('Hello'); + break; + } + + const serverWriter = conn.writable.getWriter(); + await serverWriter.write(messageFromServer); + await serverWriter.close(); + + break; + } + + for await (const chunk of clientConn.readable) { + const value = decoder.decode(chunk); + expect(value).toBe('World'); + break; + } + }); +}); diff --git a/packages/pg-gateway/src/streams.ts b/packages/pg-gateway/src/streams.ts new file mode 100644 index 0000000..383716c --- /dev/null +++ b/packages/pg-gateway/src/streams.ts @@ -0,0 +1,149 @@ +export interface DuplexStream { + readable: ReadableStream; + writable: WritableStream; +} + +/** + * A passthrough `DuplexStream` that buffers data to support + * asynchronous reads and writes. + */ +export class BufferedStream implements DuplexStream { + public readable: ReadableStream; + public writable: WritableStream; + + constructor() { + const buffer: T[] = []; + + this.readable = new ReadableStream({ + async pull(controller) { + while (buffer.length === 0) { + // Yield to the event loop + await new Promise((resolve) => setTimeout(resolve)); + } + // biome-ignore lint/style/noNonNullAssertion: + const chunk = buffer.shift()!; + controller.enqueue(chunk); + }, + }); + + this.writable = new WritableStream({ + async write(chunk) { + buffer.push(chunk); + + // Yield to the event loop + await new Promise((resolve) => setTimeout(resolve)); + }, + }); + } +} + +/** + * Creates a pair of linked duplex streams. + * + * The returned duplex streams are interconnected such that writing to the + * writable stream of one duplex will result in the data appearing on the + * readable stream of the other duplex, and vice versa. This can be useful + * for simulating a bidirectional communication channel or virtual socket. + */ +export function createDuplexPair(): [DuplexStream, DuplexStream] { + // Intermediate streams that forward writable to readable + const aToB = new BufferedStream(); + const bToA = new BufferedStream(); + + // Swap readable and writable to link duplex connections + const duplexA: DuplexStream = { + readable: bToA.readable, + writable: aToB.writable, + }; + const duplexB: DuplexStream = { + readable: aToB.readable, + writable: bToA.writable, + }; + + return [duplexA, duplexB]; +} + +/** + * Creates a virtual server that can accept multiple duplex stream connections. + * + * The server allows clients to connect via a `connect()` method, returning a + * `DuplexStream` representing the client side of the connection. The server + * side of each connection can be accessed by reading from the stream returned + * by the `listen()` method. + * + * This is useful for simulating network servers, testing bidirectional + * communication channels, or creating virtual sockets where data flow + * can be controlled and observed. + * + * @returns An object containing `connect()` to initiate a connection and + * `listen()` to retrieve the server side of the connections. + */ +export function createVirtualServer() { + const listener = new BufferedStream>(); + const connWriter = listener.writable.getWriter(); + + function listen() { + return listener.readable; + } + + async function connect() { + const [clientConn, serverConn] = createDuplexPair(); + await connWriter.write(serverConn); + return clientConn; + } + + return { listen, connect }; +} + +/** + * Converts a `ReadableStream` to an `AsyncIterator`. + * + * Note that `ReadableStream` is supposed to implement `AsyncIterable` + * already, but this isn't true for all environments today (eg. Safari). + * + * Use this method as a ponyfill. + */ +export function toAsyncIterator( + readable: ReadableStream, + options?: { preventCancel?: boolean }, +): AsyncIterableIterator { + // If the `ReadableStream` implements `[Symbol.asyncIterator]`, use it + if (Symbol.asyncIterator in readable) { + return readable[Symbol.asyncIterator](options); + } + + // Otherwise fallback to a ponyfill + const reader = (readable as ReadableStream).getReader(); + const iterator: AsyncIterableIterator = { + async next() { + try { + const { done, value } = await reader.read(); + if (done) { + reader.releaseLock(); + } + return { + done, + // biome-ignore lint/style/noNonNullAssertion: + value: value!, + }; + } catch (e) { + reader.releaseLock(); + throw e; + } + }, + async return(value: unknown) { + if (!options?.preventCancel) { + const cancelPromise = reader.cancel(value); + reader.releaseLock(); + await cancelPromise; + } else { + reader.releaseLock(); + } + return { done: true, value }; + }, + [Symbol.asyncIterator]() { + return iterator; + }, + }; + return iterator; +} diff --git a/packages/pg-gateway/src/tls.ts b/packages/pg-gateway/src/tls.ts deleted file mode 100644 index b79d30f..0000000 --- a/packages/pg-gateway/src/tls.ts +++ /dev/null @@ -1,88 +0,0 @@ -import type { Socket } from 'node:net'; -import { - TLSSocket, - type TLSSocketOptions, - createSecureContext, -} from 'node:tls'; -import type { TlsOptions, TlsOptionsCallback } from './connection'; -import type { TlsInfo } from './connection.types.js'; - -export async function upgradeTls( - socket: Socket, - options: TlsOptions | TlsOptionsCallback, - tlsInfo: TlsInfo = {}, - requestCert = false, -): Promise<{ secureSocket: TLSSocket; tlsInfo: TlsInfo }> { - const originalSocket = socket; - originalSocket.pause(); - - const tlsSocketOptions = await createTlsSocketOptions( - options, - tlsInfo, - requestCert, - ); - - const secureSocket = new TLSSocket(originalSocket, { - ...tlsSocketOptions, - isServer: true, - SNICallback: async (sniServerName, callback) => { - tlsInfo.sniServerName = sniServerName; - const updatedTlsSocketOptions = await createTlsSocketOptions( - options, - tlsInfo, - requestCert, - ); - callback(null, createSecureContext(updatedTlsSocketOptions)); - }, - }); - - secureSocket.pause(); - - await new Promise((resolve) => { - secureSocket.on('secure', () => { - onServerSocketSecure(secureSocket); - resolve(); - }); - }); - - originalSocket.resume(); - - return { secureSocket, tlsInfo }; -} - -async function createTlsSocketOptions( - optionsOrCallback: TlsOptions | TlsOptionsCallback, - tlsInfo: TlsInfo, - requestCert: boolean, -): Promise { - const { key, cert, ca, passphrase } = - typeof optionsOrCallback === 'function' - ? await optionsOrCallback(tlsInfo) - : optionsOrCallback; - - return { - key, - cert, - ca, - passphrase, - requestCert, - }; -} - -/** - * Internal Node.js handler copied and modified from source to validate client certs. - * https://github.com/nodejs/node/blob/aeaffbb385c9fc756247e6deaa70be8eb8f59496/lib/_tls_wrap.js#L1185-L1203 - * - * Without this, `authorized` is always `false` on the TLSSocket and we never know if the client cert is valid. - */ -// biome-ignore lint/suspicious/noExplicitAny: -function onServerSocketSecure(secureSocket: TLSSocket & any) { - if (secureSocket._requestCert) { - const verifyError = secureSocket._handle.verifyError(); - if (verifyError) { - secureSocket.authorizationError = verifyError.code; - } else { - secureSocket.authorized = true; - } - } -} diff --git a/packages/pg-gateway/src/types/lib.deno.d.ts b/packages/pg-gateway/src/types/lib.deno.d.ts new file mode 100644 index 0000000..4a3e06a --- /dev/null +++ b/packages/pg-gateway/src/types/lib.deno.d.ts @@ -0,0 +1,16478 @@ +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +/// +/// +/// + +/** Deno provides extra properties on `import.meta`. These are included here + * to ensure that these are still available when using the Deno namespace in + * conjunction with other type libs, like `dom`. + * + * @category ES Modules + */ +declare interface ImportMeta { + /** A string representation of the fully qualified module URL. When the + * module is loaded locally, the value will be a file URL (e.g. + * `file:///path/module.ts`). + * + * You can also parse the string as a URL to determine more information about + * how the current module was loaded. For example to determine if a module was + * local or not: + * + * ```ts + * const url = new URL(import.meta.url); + * if (url.protocol === "file:") { + * console.log("this module was loaded locally"); + * } + * ``` + */ + url: string; + + /** The absolute path of the current module. + * + * This property is only provided for local modules (ie. using `file://` URLs). + * + * Example: + * ``` + * // Unix + * console.log(import.meta.filename); // /home/alice/my_module.ts + * + * // Windows + * console.log(import.meta.filename); // C:\alice\my_module.ts + * ``` + */ + filename?: string; + + /** The absolute path of the dirrectory containing the current module. + * + * This property is only provided for local modules (ie. using `file://` URLs). + * + * * Example: + * ``` + * // Unix + * console.log(import.meta.dirname); // /home/alice/ + * + * // Windows + * console.log(import.meta.dirname); // C:\alice\ + * ``` + */ + dirname?: string; + + /** A flag that indicates if the current module is the main module that was + * called when starting the program under Deno. + * + * ```ts + * if (import.meta.main) { + * // this was loaded as the main module, maybe do some bootstrapping + * } + * ``` + */ + main: boolean; + + /** A function that returns resolved specifier as if it would be imported + * using `import(specifier)`. + * + * ```ts + * console.log(import.meta.resolve("./foo.js")); + * // file:///dev/foo.js + * ``` + */ + resolve(specifier: string): string; +} + +/** Deno supports [User Timing Level 3](https://w3c.github.io/user-timing) + * which is not widely supported yet in other runtimes. + * + * Check out the + * [Performance API](https://developer.mozilla.org/en-US/docs/Web/API/Performance) + * documentation on MDN for further information about how to use the API. + * + * @category Performance + */ +declare interface Performance { + /** Stores a timestamp with the associated name (a "mark"). */ + mark(markName: string, options?: PerformanceMarkOptions): PerformanceMark; + + /** Stores the `DOMHighResTimeStamp` duration between two marks along with the + * associated name (a "measure"). */ + measure( + measureName: string, + options?: PerformanceMeasureOptions, + ): PerformanceMeasure; +} + +/** + * Options which are used in conjunction with `performance.mark`. Check out the + * MDN + * [`performance.mark()`](https://developer.mozilla.org/en-US/docs/Web/API/Performance/mark#markoptions) + * documentation for more details. + * + * @category Performance + */ +declare interface PerformanceMarkOptions { + /** Metadata to be included in the mark. */ + // deno-lint-ignore no-explicit-any + detail?: any; + + /** Timestamp to be used as the mark time. */ + startTime?: number; +} + +/** + * Options which are used in conjunction with `performance.measure`. Check out the + * MDN + * [`performance.mark()`](https://developer.mozilla.org/en-US/docs/Web/API/Performance/measure#measureoptions) + * documentation for more details. + * + * @category Performance + */ +declare interface PerformanceMeasureOptions { + /** Metadata to be included in the measure. */ + // deno-lint-ignore no-explicit-any + detail?: any; + + /** Timestamp to be used as the start time or string to be used as start + * mark. */ + start?: string | number; + + /** Duration between the start and end times. */ + duration?: number; + + /** Timestamp to be used as the end time or string to be used as end mark. */ + end?: string | number; +} + +/** The global namespace where Deno specific, non-standard APIs are located. */ +declare namespace Deno { + /** A set of error constructors that are raised by Deno APIs. + * + * Can be used to provide more specific handling of failures within code + * which is using Deno APIs. For example, handling attempting to open a file + * which does not exist: + * + * ```ts + * try { + * const file = await Deno.open("./some/file.txt"); + * } catch (error) { + * if (error instanceof Deno.errors.NotFound) { + * console.error("the file was not found"); + * } else { + * // otherwise re-throw + * throw error; + * } + * } + * ``` + * + * @category Errors + */ + export namespace errors { + /** + * Raised when the underlying operating system indicates that the file + * was not found. + * + * @category Errors */ + export class NotFound extends Error {} + /** + * Raised when the underlying operating system indicates the current user + * which the Deno process is running under does not have the appropriate + * permissions to a file or resource, or the user _did not_ provide required + * `--allow-*` flag. + * + * @category Errors */ + export class PermissionDenied extends Error {} + /** + * Raised when the underlying operating system reports that a connection to + * a resource is refused. + * + * @category Errors */ + export class ConnectionRefused extends Error {} + /** + * Raised when the underlying operating system reports that a connection has + * been reset. With network servers, it can be a _normal_ occurrence where a + * client will abort a connection instead of properly shutting it down. + * + * @category Errors */ + export class ConnectionReset extends Error {} + /** + * Raised when the underlying operating system reports an `ECONNABORTED` + * error. + * + * @category Errors */ + export class ConnectionAborted extends Error {} + /** + * Raised when the underlying operating system reports an `ENOTCONN` error. + * + * @category Errors */ + export class NotConnected extends Error {} + /** + * Raised when attempting to open a server listener on an address and port + * that already has a listener. + * + * @category Errors */ + export class AddrInUse extends Error {} + /** + * Raised when the underlying operating system reports an `EADDRNOTAVAIL` + * error. + * + * @category Errors */ + export class AddrNotAvailable extends Error {} + /** + * Raised when trying to write to a resource and a broken pipe error occurs. + * This can happen when trying to write directly to `stdout` or `stderr` + * and the operating system is unable to pipe the output for a reason + * external to the Deno runtime. + * + * @category Errors */ + export class BrokenPipe extends Error {} + /** + * Raised when trying to create a resource, like a file, that already + * exits. + * + * @category Errors */ + export class AlreadyExists extends Error {} + /** + * Raised when an operation to returns data that is invalid for the + * operation being performed. + * + * @category Errors */ + export class InvalidData extends Error {} + /** + * Raised when the underlying operating system reports that an I/O operation + * has timed out (`ETIMEDOUT`). + * + * @category Errors */ + export class TimedOut extends Error {} + /** + * Raised when the underlying operating system reports an `EINTR` error. In + * many cases, this underlying IO error will be handled internally within + * Deno, or result in an @{link BadResource} error instead. + * + * @category Errors */ + export class Interrupted extends Error {} + /** + * Raised when the underlying operating system would need to block to + * complete but an asynchronous (non-blocking) API is used. + * + * @category Errors */ + export class WouldBlock extends Error {} + /** + * Raised when expecting to write to a IO buffer resulted in zero bytes + * being written. + * + * @category Errors */ + export class WriteZero extends Error {} + /** + * Raised when attempting to read bytes from a resource, but the EOF was + * unexpectedly encountered. + * + * @category Errors */ + export class UnexpectedEof extends Error {} + /** + * The underlying IO resource is invalid or closed, and so the operation + * could not be performed. + * + * @category Errors */ + export class BadResource extends Error {} + /** + * Raised in situations where when attempting to load a dynamic import, + * too many redirects were encountered. + * + * @category Errors */ + export class Http extends Error {} + /** + * Raised when the underlying IO resource is not available because it is + * being awaited on in another block of code. + * + * @category Errors */ + export class Busy extends Error {} + /** + * Raised when the underlying Deno API is asked to perform a function that + * is not currently supported. + * + * @category Errors */ + export class NotSupported extends Error {} + /** + * Raised when too many symbolic links were encountered when resolving the + * filename. + * + * @category Errors */ + export class FilesystemLoop extends Error {} + /** + * Raised when trying to open, create or write to a directory. + * + * @category Errors */ + export class IsADirectory extends Error {} + /** + * Raised when performing a socket operation but the remote host is + * not reachable. + * + * @category Errors */ + export class NetworkUnreachable extends Error {} + /** + * Raised when trying to perform an operation on a path that is not a + * directory, when directory is required. + * + * @category Errors */ + export class NotADirectory extends Error {} + } + + /** The current process ID of this instance of the Deno CLI. + * + * ```ts + * console.log(Deno.pid); + * ``` + * + * @category Runtime Environment + */ + export const pid: number; + + /** + * The process ID of parent process of this instance of the Deno CLI. + * + * ```ts + * console.log(Deno.ppid); + * ``` + * + * @category Runtime Environment + */ + export const ppid: number; + + /** @category Runtime Environment */ + export interface MemoryUsage { + /** The number of bytes of the current Deno's process resident set size, + * which is the amount of memory occupied in main memory (RAM). */ + rss: number; + /** The total size of the heap for V8, in bytes. */ + heapTotal: number; + /** The amount of the heap used for V8, in bytes. */ + heapUsed: number; + /** Memory, in bytes, associated with JavaScript objects outside of the + * JavaScript isolate. */ + external: number; + } + + /** + * Returns an object describing the memory usage of the Deno process and the + * V8 subsystem measured in bytes. + * + * @category Runtime Environment + */ + export function memoryUsage(): MemoryUsage; + + /** + * Get the `hostname` of the machine the Deno process is running on. + * + * ```ts + * console.log(Deno.hostname()); + * ``` + * + * Requires `allow-sys` permission. + * + * @tags allow-sys + * @category Runtime Environment + */ + export function hostname(): string; + + /** + * Returns an array containing the 1, 5, and 15 minute load averages. The + * load average is a measure of CPU and IO utilization of the last one, five, + * and 15 minute periods expressed as a fractional number. Zero means there + * is no load. On Windows, the three values are always the same and represent + * the current load, not the 1, 5 and 15 minute load averages. + * + * ```ts + * console.log(Deno.loadavg()); // e.g. [ 0.71, 0.44, 0.44 ] + * ``` + * + * Requires `allow-sys` permission. + * + * On Windows there is no API available to retrieve this information and this method returns `[ 0, 0, 0 ]`. + * + * @tags allow-sys + * @category Observability + */ + export function loadavg(): number[]; + + /** + * The information for a network interface returned from a call to + * {@linkcode Deno.networkInterfaces}. + * + * @category Network + */ + export interface NetworkInterfaceInfo { + /** The network interface name. */ + name: string; + /** The IP protocol version. */ + family: "IPv4" | "IPv6"; + /** The IP address bound to the interface. */ + address: string; + /** The netmask applied to the interface. */ + netmask: string; + /** The IPv6 scope id or `null`. */ + scopeid: number | null; + /** The CIDR range. */ + cidr: string; + /** The MAC address. */ + mac: string; + } + + /** + * Returns an array of the network interface information. + * + * ```ts + * console.log(Deno.networkInterfaces()); + * ``` + * + * Requires `allow-sys` permission. + * + * @tags allow-sys + * @category Network + */ + export function networkInterfaces(): NetworkInterfaceInfo[]; + + /** + * Displays the total amount of free and used physical and swap memory in the + * system, as well as the buffers and caches used by the kernel. + * + * This is similar to the `free` command in Linux + * + * ```ts + * console.log(Deno.systemMemoryInfo()); + * ``` + * + * Requires `allow-sys` permission. + * + * @tags allow-sys + * @category Runtime Environment + */ + export function systemMemoryInfo(): SystemMemoryInfo; + + /** + * Information returned from a call to {@linkcode Deno.systemMemoryInfo}. + * + * @category Runtime Environment + */ + export interface SystemMemoryInfo { + /** Total installed memory in bytes. */ + total: number; + /** Unused memory in bytes. */ + free: number; + /** Estimation of how much memory, in bytes, is available for starting new + * applications, without swapping. Unlike the data provided by the cache or + * free fields, this field takes into account page cache and also that not + * all reclaimable memory will be reclaimed due to items being in use. + */ + available: number; + /** Memory used by kernel buffers. */ + buffers: number; + /** Memory used by the page cache and slabs. */ + cached: number; + /** Total swap memory. */ + swapTotal: number; + /** Unused swap memory. */ + swapFree: number; + } + + /** Reflects the `NO_COLOR` environment variable at program start. + * + * When the value is `true`, the Deno CLI will attempt to not send color codes + * to `stderr` or `stdout` and other command line programs should also attempt + * to respect this value. + * + * See: https://no-color.org/ + * + * @category Runtime Environment + */ + export const noColor: boolean; + + /** + * Returns the release version of the Operating System. + * + * ```ts + * console.log(Deno.osRelease()); + * ``` + * + * Requires `allow-sys` permission. + * Under consideration to possibly move to Deno.build or Deno.versions and if + * it should depend sys-info, which may not be desirable. + * + * @tags allow-sys + * @category Runtime Environment + */ + export function osRelease(): string; + + /** + * Returns the Operating System uptime in number of seconds. + * + * ```ts + * console.log(Deno.osUptime()); + * ``` + * + * Requires `allow-sys` permission. + * + * @tags allow-sys + * @category Runtime Environment + */ + export function osUptime(): number; + + /** + * Options which define the permissions within a test or worker context. + * + * `"inherit"` ensures that all permissions of the parent process will be + * applied to the test context. `"none"` ensures the test context has no + * permissions. A `PermissionOptionsObject` provides a more specific + * set of permissions to the test context. + * + * @category Permissions */ + export type PermissionOptions = + | "inherit" + | "none" + | PermissionOptionsObject; + + /** + * A set of options which can define the permissions within a test or worker + * context at a highly specific level. + * + * @category Permissions */ + export interface PermissionOptionsObject { + /** Specifies if the `env` permission should be requested or revoked. + * If set to `"inherit"`, the current `env` permission will be inherited. + * If set to `true`, the global `env` permission will be requested. + * If set to `false`, the global `env` permission will be revoked. + * + * @default {false} + */ + env?: "inherit" | boolean | string[]; + + /** Specifies if the `sys` permission should be requested or revoked. + * If set to `"inherit"`, the current `sys` permission will be inherited. + * If set to `true`, the global `sys` permission will be requested. + * If set to `false`, the global `sys` permission will be revoked. + * + * @default {false} + */ + sys?: "inherit" | boolean | string[]; + + /** Specifies if the `hrtime` permission should be requested or revoked. + * If set to `"inherit"`, the current `hrtime` permission will be inherited. + * If set to `true`, the global `hrtime` permission will be requested. + * If set to `false`, the global `hrtime` permission will be revoked. + * + * @default {false} + */ + hrtime?: "inherit" | boolean; + + /** Specifies if the `net` permission should be requested or revoked. + * if set to `"inherit"`, the current `net` permission will be inherited. + * if set to `true`, the global `net` permission will be requested. + * if set to `false`, the global `net` permission will be revoked. + * if set to `string[]`, the `net` permission will be requested with the + * specified host strings with the format `"[:]`. + * + * @default {false} + * + * Examples: + * + * ```ts + * import { assertEquals } from "https://deno.land/std/assert/mod.ts"; + * + * Deno.test({ + * name: "inherit", + * permissions: { + * net: "inherit", + * }, + * async fn() { + * const status = await Deno.permissions.query({ name: "net" }) + * assertEquals(status.state, "granted"); + * }, + * }); + * ``` + * + * ```ts + * import { assertEquals } from "https://deno.land/std/assert/mod.ts"; + * + * Deno.test({ + * name: "true", + * permissions: { + * net: true, + * }, + * async fn() { + * const status = await Deno.permissions.query({ name: "net" }); + * assertEquals(status.state, "granted"); + * }, + * }); + * ``` + * + * ```ts + * import { assertEquals } from "https://deno.land/std/assert/mod.ts"; + * + * Deno.test({ + * name: "false", + * permissions: { + * net: false, + * }, + * async fn() { + * const status = await Deno.permissions.query({ name: "net" }); + * assertEquals(status.state, "denied"); + * }, + * }); + * ``` + * + * ```ts + * import { assertEquals } from "https://deno.land/std/assert/mod.ts"; + * + * Deno.test({ + * name: "localhost:8080", + * permissions: { + * net: ["localhost:8080"], + * }, + * async fn() { + * const status = await Deno.permissions.query({ name: "net", host: "localhost:8080" }); + * assertEquals(status.state, "granted"); + * }, + * }); + * ``` + */ + net?: "inherit" | boolean | string[]; + + /** Specifies if the `ffi` permission should be requested or revoked. + * If set to `"inherit"`, the current `ffi` permission will be inherited. + * If set to `true`, the global `ffi` permission will be requested. + * If set to `false`, the global `ffi` permission will be revoked. + * + * @default {false} + */ + ffi?: "inherit" | boolean | Array; + + /** Specifies if the `read` permission should be requested or revoked. + * If set to `"inherit"`, the current `read` permission will be inherited. + * If set to `true`, the global `read` permission will be requested. + * If set to `false`, the global `read` permission will be revoked. + * If set to `Array`, the `read` permission will be requested with the + * specified file paths. + * + * @default {false} + */ + read?: "inherit" | boolean | Array; + + /** Specifies if the `run` permission should be requested or revoked. + * If set to `"inherit"`, the current `run` permission will be inherited. + * If set to `true`, the global `run` permission will be requested. + * If set to `false`, the global `run` permission will be revoked. + * + * @default {false} + */ + run?: "inherit" | boolean | Array; + + /** Specifies if the `write` permission should be requested or revoked. + * If set to `"inherit"`, the current `write` permission will be inherited. + * If set to `true`, the global `write` permission will be requested. + * If set to `false`, the global `write` permission will be revoked. + * If set to `Array`, the `write` permission will be requested with the + * specified file paths. + * + * @default {false} + */ + write?: "inherit" | boolean | Array; + } + + /** + * Context that is passed to a testing function, which can be used to either + * gain information about the current test, or register additional test + * steps within the current test. + * + * @category Testing */ + export interface TestContext { + /** The current test name. */ + name: string; + /** The string URL of the current test. */ + origin: string; + /** If the current test is a step of another test, the parent test context + * will be set here. */ + parent?: TestContext; + + /** Run a sub step of the parent test or step. Returns a promise + * that resolves to a boolean signifying if the step completed successfully. + * + * The returned promise never rejects unless the arguments are invalid. + * + * If the test was ignored the promise returns `false`. + * + * ```ts + * Deno.test({ + * name: "a parent test", + * async fn(t) { + * console.log("before the step"); + * await t.step({ + * name: "step 1", + * fn(t) { + * console.log("current step:", t.name); + * } + * }); + * console.log("after the step"); + * } + * }); + * ``` + */ + step(definition: TestStepDefinition): Promise; + + /** Run a sub step of the parent test or step. Returns a promise + * that resolves to a boolean signifying if the step completed successfully. + * + * The returned promise never rejects unless the arguments are invalid. + * + * If the test was ignored the promise returns `false`. + * + * ```ts + * Deno.test( + * "a parent test", + * async (t) => { + * console.log("before the step"); + * await t.step( + * "step 1", + * (t) => { + * console.log("current step:", t.name); + * } + * ); + * console.log("after the step"); + * } + * ); + * ``` + */ + step( + name: string, + fn: (t: TestContext) => void | Promise, + ): Promise; + + /** Run a sub step of the parent test or step. Returns a promise + * that resolves to a boolean signifying if the step completed successfully. + * + * The returned promise never rejects unless the arguments are invalid. + * + * If the test was ignored the promise returns `false`. + * + * ```ts + * Deno.test(async function aParentTest(t) { + * console.log("before the step"); + * await t.step(function step1(t) { + * console.log("current step:", t.name); + * }); + * console.log("after the step"); + * }); + * ``` + */ + step(fn: (t: TestContext) => void | Promise): Promise; + } + + /** @category Testing */ + export interface TestStepDefinition { + /** The test function that will be tested when this step is executed. The + * function can take an argument which will provide information about the + * current step's context. */ + fn: (t: TestContext) => void | Promise; + /** The name of the step. */ + name: string; + /** If truthy the current test step will be ignored. + * + * This is a quick way to skip over a step, but also can be used for + * conditional logic, like determining if an environment feature is present. + */ + ignore?: boolean; + /** Check that the number of async completed operations after the test step + * is the same as number of dispatched operations. This ensures that the + * code tested does not start async operations which it then does + * not await. This helps in preventing logic errors and memory leaks + * in the application code. + * + * Defaults to the parent test or step's value. */ + sanitizeOps?: boolean; + /** Ensure the test step does not "leak" resources - like open files or + * network connections - by ensuring the open resources at the start of the + * step match the open resources at the end of the step. + * + * Defaults to the parent test or step's value. */ + sanitizeResources?: boolean; + /** Ensure the test step does not prematurely cause the process to exit, + * for example via a call to {@linkcode Deno.exit}. + * + * Defaults to the parent test or step's value. */ + sanitizeExit?: boolean; + } + + /** @category Testing */ + export interface TestDefinition { + fn: (t: TestContext) => void | Promise; + /** The name of the test. */ + name: string; + /** If truthy the current test step will be ignored. + * + * It is a quick way to skip over a step, but also can be used for + * conditional logic, like determining if an environment feature is present. + */ + ignore?: boolean; + /** If at least one test has `only` set to `true`, only run tests that have + * `only` set to `true` and fail the test suite. */ + only?: boolean; + /** Check that the number of async completed operations after the test step + * is the same as number of dispatched operations. This ensures that the + * code tested does not start async operations which it then does + * not await. This helps in preventing logic errors and memory leaks + * in the application code. + * + * @default {true} */ + sanitizeOps?: boolean; + /** Ensure the test step does not "leak" resources - like open files or + * network connections - by ensuring the open resources at the start of the + * test match the open resources at the end of the test. + * + * @default {true} */ + sanitizeResources?: boolean; + /** Ensure the test case does not prematurely cause the process to exit, + * for example via a call to {@linkcode Deno.exit}. + * + * @default {true} */ + sanitizeExit?: boolean; + /** Specifies the permissions that should be used to run the test. + * + * Set this to "inherit" to keep the calling runtime permissions, set this + * to "none" to revoke all permissions, or set a more specific set of + * permissions using a {@linkcode PermissionOptionsObject}. + * + * @default {"inherit"} */ + permissions?: PermissionOptions; + } + + /** Register a test which will be run when `deno test` is used on the command + * line and the containing module looks like a test module. + * + * `fn` can be async if required. + * + * ```ts + * import { assertEquals } from "https://deno.land/std/assert/mod.ts"; + * + * Deno.test({ + * name: "example test", + * fn() { + * assertEquals("world", "world"); + * }, + * }); + * + * Deno.test({ + * name: "example ignored test", + * ignore: Deno.build.os === "windows", + * fn() { + * // This test is ignored only on Windows machines + * }, + * }); + * + * Deno.test({ + * name: "example async test", + * async fn() { + * const decoder = new TextDecoder("utf-8"); + * const data = await Deno.readFile("hello_world.txt"); + * assertEquals(decoder.decode(data), "Hello world"); + * } + * }); + * ``` + * + * @category Testing + */ + export const test: DenoTest; + + /** + * @category Testing + */ + interface DenoTest { + /** Register a test which will be run when `deno test` is used on the command + * line and the containing module looks like a test module. + * + * `fn` can be async if required. + * + * ```ts + * import { assertEquals } from "https://deno.land/std/assert/mod.ts"; + * + * Deno.test({ + * name: "example test", + * fn() { + * assertEquals("world", "world"); + * }, + * }); + * + * Deno.test({ + * name: "example ignored test", + * ignore: Deno.build.os === "windows", + * fn() { + * // This test is ignored only on Windows machines + * }, + * }); + * + * Deno.test({ + * name: "example async test", + * async fn() { + * const decoder = new TextDecoder("utf-8"); + * const data = await Deno.readFile("hello_world.txt"); + * assertEquals(decoder.decode(data), "Hello world"); + * } + * }); + * ``` + * + * @category Testing + */ + (t: TestDefinition): void; + + /** Register a test which will be run when `deno test` is used on the command + * line and the containing module looks like a test module. + * + * `fn` can be async if required. + * + * ```ts + * import { assertEquals } from "https://deno.land/std/assert/mod.ts"; + * + * Deno.test("My test description", () => { + * assertEquals("hello", "hello"); + * }); + * + * Deno.test("My async test description", async () => { + * const decoder = new TextDecoder("utf-8"); + * const data = await Deno.readFile("hello_world.txt"); + * assertEquals(decoder.decode(data), "Hello world"); + * }); + * ``` + * + * @category Testing + */ + ( + name: string, + fn: (t: TestContext) => void | Promise, + ): void; + + /** Register a test which will be run when `deno test` is used on the command + * line and the containing module looks like a test module. + * + * `fn` can be async if required. Declared function must have a name. + * + * ```ts + * import { assertEquals } from "https://deno.land/std/assert/mod.ts"; + * + * Deno.test(function myTestName() { + * assertEquals("hello", "hello"); + * }); + * + * Deno.test(async function myOtherTestName() { + * const decoder = new TextDecoder("utf-8"); + * const data = await Deno.readFile("hello_world.txt"); + * assertEquals(decoder.decode(data), "Hello world"); + * }); + * ``` + * + * @category Testing + */ + (fn: (t: TestContext) => void | Promise): void; + + /** Register a test which will be run when `deno test` is used on the command + * line and the containing module looks like a test module. + * + * `fn` can be async if required. + * + * ```ts + * import {assert, fail, assertEquals} from "https://deno.land/std/assert/mod.ts"; + * + * Deno.test("My test description", { permissions: { read: true } }, (): void => { + * assertEquals("hello", "hello"); + * }); + * + * Deno.test("My async test description", { permissions: { read: false } }, async (): Promise => { + * const decoder = new TextDecoder("utf-8"); + * const data = await Deno.readFile("hello_world.txt"); + * assertEquals(decoder.decode(data), "Hello world"); + * }); + * ``` + * + * @category Testing + */ + ( + name: string, + options: Omit, + fn: (t: TestContext) => void | Promise, + ): void; + + /** Register a test which will be run when `deno test` is used on the command + * line and the containing module looks like a test module. + * + * `fn` can be async if required. + * + * ```ts + * import { assertEquals } from "https://deno.land/std/assert/mod.ts"; + * + * Deno.test( + * { + * name: "My test description", + * permissions: { read: true }, + * }, + * () => { + * assertEquals("hello", "hello"); + * }, + * ); + * + * Deno.test( + * { + * name: "My async test description", + * permissions: { read: false }, + * }, + * async () => { + * const decoder = new TextDecoder("utf-8"); + * const data = await Deno.readFile("hello_world.txt"); + * assertEquals(decoder.decode(data), "Hello world"); + * }, + * ); + * ``` + * + * @category Testing + */ + ( + options: Omit, + fn: (t: TestContext) => void | Promise, + ): void; + + /** Register a test which will be run when `deno test` is used on the command + * line and the containing module looks like a test module. + * + * `fn` can be async if required. Declared function must have a name. + * + * ```ts + * import { assertEquals } from "https://deno.land/std/assert/mod.ts"; + * + * Deno.test( + * { permissions: { read: true } }, + * function myTestName() { + * assertEquals("hello", "hello"); + * }, + * ); + * + * Deno.test( + * { permissions: { read: false } }, + * async function myOtherTestName() { + * const decoder = new TextDecoder("utf-8"); + * const data = await Deno.readFile("hello_world.txt"); + * assertEquals(decoder.decode(data), "Hello world"); + * }, + * ); + * ``` + * + * @category Testing + */ + ( + options: Omit, + fn: (t: TestContext) => void | Promise, + ): void; + + /** Shorthand property for ignoring a particular test case. + * + * @category Testing + */ + ignore(t: Omit): void; + + /** Shorthand property for ignoring a particular test case. + * + * @category Testing + */ + ignore( + name: string, + fn: (t: TestContext) => void | Promise, + ): void; + + /** Shorthand property for ignoring a particular test case. + * + * @category Testing + */ + ignore(fn: (t: TestContext) => void | Promise): void; + + /** Shorthand property for ignoring a particular test case. + * + * @category Testing + */ + ignore( + name: string, + options: Omit, + fn: (t: TestContext) => void | Promise, + ): void; + + /** Shorthand property for ignoring a particular test case. + * + * @category Testing + */ + ignore( + options: Omit, + fn: (t: TestContext) => void | Promise, + ): void; + + /** Shorthand property for ignoring a particular test case. + * + * @category Testing + */ + ignore( + options: Omit, + fn: (t: TestContext) => void | Promise, + ): void; + + /** Shorthand property for focusing a particular test case. + * + * @category Testing + */ + only(t: Omit): void; + + /** Shorthand property for focusing a particular test case. + * + * @category Testing + */ + only( + name: string, + fn: (t: TestContext) => void | Promise, + ): void; + + /** Shorthand property for focusing a particular test case. + * + * @category Testing + */ + only(fn: (t: TestContext) => void | Promise): void; + + /** Shorthand property for focusing a particular test case. + * + * @category Testing + */ + only( + name: string, + options: Omit, + fn: (t: TestContext) => void | Promise, + ): void; + + /** Shorthand property for focusing a particular test case. + * + * @category Testing + */ + only( + options: Omit, + fn: (t: TestContext) => void | Promise, + ): void; + + /** Shorthand property for focusing a particular test case. + * + * @category Testing + */ + only( + options: Omit, + fn: (t: TestContext) => void | Promise, + ): void; + } + + /** + * Context that is passed to a benchmarked function. The instance is shared + * between iterations of the benchmark. Its methods can be used for example + * to override of the measured portion of the function. + * + * @category Testing + */ + export interface BenchContext { + /** The current benchmark name. */ + name: string; + /** The string URL of the current benchmark. */ + origin: string; + + /** Restarts the timer for the bench measurement. This should be called + * after doing setup work which should not be measured. + * + * Warning: This method should not be used for benchmarks averaging less + * than 10μs per iteration. In such cases it will be disabled but the call + * will still have noticeable overhead, resulting in a warning. + * + * ```ts + * Deno.bench("foo", async (t) => { + * const data = await Deno.readFile("data.txt"); + * t.start(); + * // some operation on `data`... + * }); + * ``` + */ + start(): void; + + /** End the timer early for the bench measurement. This should be called + * before doing teardown work which should not be measured. + * + * Warning: This method should not be used for benchmarks averaging less + * than 10μs per iteration. In such cases it will be disabled but the call + * will still have noticeable overhead, resulting in a warning. + * + * ```ts + * Deno.bench("foo", async (t) => { + * using file = await Deno.open("data.txt"); + * t.start(); + * // some operation on `file`... + * t.end(); + * }); + * ``` + */ + end(): void; + } + + /** + * The interface for defining a benchmark test using {@linkcode Deno.bench}. + * + * @category Testing + */ + export interface BenchDefinition { + /** The test function which will be benchmarked. */ + fn: (b: BenchContext) => void | Promise; + /** The name of the test, which will be used in displaying the results. */ + name: string; + /** If truthy, the benchmark test will be ignored/skipped. */ + ignore?: boolean; + /** Group name for the benchmark. + * + * Grouped benchmarks produce a group time summary, where the difference + * in performance between each test of the group is compared. */ + group?: string; + /** Benchmark should be used as the baseline for other benchmarks. + * + * If there are multiple baselines in a group, the first one is used as the + * baseline. */ + baseline?: boolean; + /** If at least one bench has `only` set to true, only run benches that have + * `only` set to `true` and fail the bench suite. */ + only?: boolean; + /** Ensure the bench case does not prematurely cause the process to exit, + * for example via a call to {@linkcode Deno.exit}. + * + * @default {true} */ + sanitizeExit?: boolean; + /** Specifies the permissions that should be used to run the bench. + * + * Set this to `"inherit"` to keep the calling thread's permissions. + * + * Set this to `"none"` to revoke all permissions. + * + * @default {"inherit"} + */ + permissions?: PermissionOptions; + } + + /** + * Register a benchmark test which will be run when `deno bench` is used on + * the command line and the containing module looks like a bench module. + * + * If the test function (`fn`) returns a promise or is async, the test runner + * will await resolution to consider the test complete. + * + * ```ts + * import { assertEquals } from "https://deno.land/std/assert/mod.ts"; + * + * Deno.bench({ + * name: "example test", + * fn() { + * assertEquals("world", "world"); + * }, + * }); + * + * Deno.bench({ + * name: "example ignored test", + * ignore: Deno.build.os === "windows", + * fn() { + * // This test is ignored only on Windows machines + * }, + * }); + * + * Deno.bench({ + * name: "example async test", + * async fn() { + * const decoder = new TextDecoder("utf-8"); + * const data = await Deno.readFile("hello_world.txt"); + * assertEquals(decoder.decode(data), "Hello world"); + * } + * }); + * ``` + * + * @category Testing + */ + export function bench(b: BenchDefinition): void; + + /** + * Register a benchmark test which will be run when `deno bench` is used on + * the command line and the containing module looks like a bench module. + * + * If the test function (`fn`) returns a promise or is async, the test runner + * will await resolution to consider the test complete. + * + * ```ts + * import { assertEquals } from "https://deno.land/std/assert/mod.ts"; + * + * Deno.bench("My test description", () => { + * assertEquals("hello", "hello"); + * }); + * + * Deno.bench("My async test description", async () => { + * const decoder = new TextDecoder("utf-8"); + * const data = await Deno.readFile("hello_world.txt"); + * assertEquals(decoder.decode(data), "Hello world"); + * }); + * ``` + * + * @category Testing + */ + export function bench( + name: string, + fn: (b: BenchContext) => void | Promise, + ): void; + + /** + * Register a benchmark test which will be run when `deno bench` is used on + * the command line and the containing module looks like a bench module. + * + * If the test function (`fn`) returns a promise or is async, the test runner + * will await resolution to consider the test complete. + * + * ```ts + * import { assertEquals } from "https://deno.land/std/assert/mod.ts"; + * + * Deno.bench(function myTestName() { + * assertEquals("hello", "hello"); + * }); + * + * Deno.bench(async function myOtherTestName() { + * const decoder = new TextDecoder("utf-8"); + * const data = await Deno.readFile("hello_world.txt"); + * assertEquals(decoder.decode(data), "Hello world"); + * }); + * ``` + * + * @category Testing + */ + export function bench(fn: (b: BenchContext) => void | Promise): void; + + /** + * Register a benchmark test which will be run when `deno bench` is used on + * the command line and the containing module looks like a bench module. + * + * If the test function (`fn`) returns a promise or is async, the test runner + * will await resolution to consider the test complete. + * + * ```ts + * import { assertEquals } from "https://deno.land/std/assert/mod.ts"; + * + * Deno.bench( + * "My test description", + * { permissions: { read: true } }, + * () => { + * assertEquals("hello", "hello"); + * } + * ); + * + * Deno.bench( + * "My async test description", + * { permissions: { read: false } }, + * async () => { + * const decoder = new TextDecoder("utf-8"); + * const data = await Deno.readFile("hello_world.txt"); + * assertEquals(decoder.decode(data), "Hello world"); + * } + * ); + * ``` + * + * @category Testing + */ + export function bench( + name: string, + options: Omit, + fn: (b: BenchContext) => void | Promise, + ): void; + + /** + * Register a benchmark test which will be run when `deno bench` is used on + * the command line and the containing module looks like a bench module. + * + * If the test function (`fn`) returns a promise or is async, the test runner + * will await resolution to consider the test complete. + * + * ```ts + * import { assertEquals } from "https://deno.land/std/assert/mod.ts"; + * + * Deno.bench( + * { name: "My test description", permissions: { read: true } }, + * () => { + * assertEquals("hello", "hello"); + * } + * ); + * + * Deno.bench( + * { name: "My async test description", permissions: { read: false } }, + * async () => { + * const decoder = new TextDecoder("utf-8"); + * const data = await Deno.readFile("hello_world.txt"); + * assertEquals(decoder.decode(data), "Hello world"); + * } + * ); + * ``` + * + * @category Testing + */ + export function bench( + options: Omit, + fn: (b: BenchContext) => void | Promise, + ): void; + + /** + * Register a benchmark test which will be run when `deno bench` is used on + * the command line and the containing module looks like a bench module. + * + * If the test function (`fn`) returns a promise or is async, the test runner + * will await resolution to consider the test complete. + * + * ```ts + * import { assertEquals } from "https://deno.land/std/assert/mod.ts"; + * + * Deno.bench( + * { permissions: { read: true } }, + * function myTestName() { + * assertEquals("hello", "hello"); + * } + * ); + * + * Deno.bench( + * { permissions: { read: false } }, + * async function myOtherTestName() { + * const decoder = new TextDecoder("utf-8"); + * const data = await Deno.readFile("hello_world.txt"); + * assertEquals(decoder.decode(data), "Hello world"); + * } + * ); + * ``` + * + * @category Testing + */ + export function bench( + options: Omit, + fn: (b: BenchContext) => void | Promise, + ): void; + + /** Exit the Deno process with optional exit code. + * + * If no exit code is supplied then Deno will exit with return code of `0`. + * + * In worker contexts this is an alias to `self.close();`. + * + * ```ts + * Deno.exit(5); + * ``` + * + * @category Runtime Environment + */ + export function exit(code?: number): never; + + /** An interface containing methods to interact with the process environment + * variables. + * + * @tags allow-env + * @category Runtime Environment + */ + export interface Env { + /** Retrieve the value of an environment variable. + * + * Returns `undefined` if the supplied environment variable is not defined. + * + * ```ts + * console.log(Deno.env.get("HOME")); // e.g. outputs "/home/alice" + * console.log(Deno.env.get("MADE_UP_VAR")); // outputs "undefined" + * ``` + * + * Requires `allow-env` permission. + * + * @tags allow-env + */ + get(key: string): string | undefined; + + /** Set the value of an environment variable. + * + * ```ts + * Deno.env.set("SOME_VAR", "Value"); + * Deno.env.get("SOME_VAR"); // outputs "Value" + * ``` + * + * Requires `allow-env` permission. + * + * @tags allow-env + */ + set(key: string, value: string): void; + + /** Delete the value of an environment variable. + * + * ```ts + * Deno.env.set("SOME_VAR", "Value"); + * Deno.env.delete("SOME_VAR"); // outputs "undefined" + * ``` + * + * Requires `allow-env` permission. + * + * @tags allow-env + */ + delete(key: string): void; + + /** Check whether an environment variable is present or not. + * + * ```ts + * Deno.env.set("SOME_VAR", "Value"); + * Deno.env.has("SOME_VAR"); // outputs true + * ``` + * + * Requires `allow-env` permission. + * + * @tags allow-env + */ + has(key: string): boolean; + + /** Returns a snapshot of the environment variables at invocation as a + * simple object of keys and values. + * + * ```ts + * Deno.env.set("TEST_VAR", "A"); + * const myEnv = Deno.env.toObject(); + * console.log(myEnv.SHELL); + * Deno.env.set("TEST_VAR", "B"); + * console.log(myEnv.TEST_VAR); // outputs "A" + * ``` + * + * Requires `allow-env` permission. + * + * @tags allow-env + */ + toObject(): { [index: string]: string }; + } + + /** An interface containing methods to interact with the process environment + * variables. + * + * @tags allow-env + * @category Runtime Environment + */ + export const env: Env; + + /** + * Returns the path to the current deno executable. + * + * ```ts + * console.log(Deno.execPath()); // e.g. "/home/alice/.local/bin/deno" + * ``` + * + * Requires `allow-read` permission. + * + * @tags allow-read + * @category Runtime Environment + */ + export function execPath(): string; + + /** + * Change the current working directory to the specified path. + * + * ```ts + * Deno.chdir("/home/userA"); + * Deno.chdir("../userB"); + * Deno.chdir("C:\\Program Files (x86)\\Java"); + * ``` + * + * Throws {@linkcode Deno.errors.NotFound} if directory not found. + * + * Throws {@linkcode Deno.errors.PermissionDenied} if the user does not have + * operating system file access rights. + * + * Requires `allow-read` permission. + * + * @tags allow-read + * @category Runtime Environment + */ + export function chdir(directory: string | URL): void; + + /** + * Return a string representing the current working directory. + * + * If the current directory can be reached via multiple paths (due to symbolic + * links), `cwd()` may return any one of them. + * + * ```ts + * const currentWorkingDirectory = Deno.cwd(); + * ``` + * + * Throws {@linkcode Deno.errors.NotFound} if directory not available. + * + * Requires `allow-read` permission. + * + * @tags allow-read + * @category Runtime Environment + */ + export function cwd(): string; + + /** + * Creates `newpath` as a hard link to `oldpath`. + * + * ```ts + * await Deno.link("old/name", "new/name"); + * ``` + * + * Requires `allow-read` and `allow-write` permissions. + * + * @tags allow-read, allow-write + * @category File System + */ + export function link(oldpath: string, newpath: string): Promise; + + /** + * Synchronously creates `newpath` as a hard link to `oldpath`. + * + * ```ts + * Deno.linkSync("old/name", "new/name"); + * ``` + * + * Requires `allow-read` and `allow-write` permissions. + * + * @tags allow-read, allow-write + * @category File System + */ + export function linkSync(oldpath: string, newpath: string): void; + + /** + * A enum which defines the seek mode for IO related APIs that support + * seeking. + * + * @category I/O */ + export enum SeekMode { + /* Seek from the start of the file/resource. */ + Start = 0, + /* Seek from the current position within the file/resource. */ + Current = 1, + /* Seek from the end of the current file/resource. */ + End = 2, + } + + /** + * An abstract interface which when implemented provides an interface to read + * bytes into an array buffer asynchronously. + * + * @deprecated Use {@linkcode ReadableStream} instead. {@linkcode Reader} + * will be removed in v2.0.0. + * + * @category I/O */ + export interface Reader { + /** Reads up to `p.byteLength` bytes into `p`. It resolves to the number of + * bytes read (`0` < `n` <= `p.byteLength`) and rejects if any error + * encountered. Even if `read()` resolves to `n` < `p.byteLength`, it may + * use all of `p` as scratch space during the call. If some data is + * available but not `p.byteLength` bytes, `read()` conventionally resolves + * to what is available instead of waiting for more. + * + * When `read()` encounters end-of-file condition, it resolves to EOF + * (`null`). + * + * When `read()` encounters an error, it rejects with an error. + * + * Callers should always process the `n` > `0` bytes returned before + * considering the EOF (`null`). Doing so correctly handles I/O errors that + * happen after reading some bytes and also both of the allowed EOF + * behaviors. + * + * Implementations should not retain a reference to `p`. + * + * Use + * [`itereateReader`](https://deno.land/std/streams/iterate_reader.ts?s=iterateReader) + * from + * [`std/streams/iterate_reader.ts`](https://deno.land/std/streams/iterate_reader.ts) + * to turn a `Reader` into an {@linkcode AsyncIterator}. + */ + read(p: Uint8Array): Promise; + } + + /** + * An abstract interface which when implemented provides an interface to read + * bytes into an array buffer synchronously. + * + * @deprecated Use {@linkcode ReadableStream} instead. {@linkcode ReaderSync} + * will be removed in v2.0.0. + * + * @category I/O */ + export interface ReaderSync { + /** Reads up to `p.byteLength` bytes into `p`. It resolves to the number + * of bytes read (`0` < `n` <= `p.byteLength`) and rejects if any error + * encountered. Even if `readSync()` returns `n` < `p.byteLength`, it may use + * all of `p` as scratch space during the call. If some data is available + * but not `p.byteLength` bytes, `readSync()` conventionally returns what is + * available instead of waiting for more. + * + * When `readSync()` encounters end-of-file condition, it returns EOF + * (`null`). + * + * When `readSync()` encounters an error, it throws with an error. + * + * Callers should always process the `n` > `0` bytes returned before + * considering the EOF (`null`). Doing so correctly handles I/O errors that + * happen after reading some bytes and also both of the allowed EOF + * behaviors. + * + * Implementations should not retain a reference to `p`. + * + * Use + * [`itereateReaderSync`](https://deno.land/std/streams/iterate_reader.ts?s=iterateReaderSync) + * from from + * [`std/streams/iterate_reader.ts`](https://deno.land/std/streams/iterate_reader.ts) + * to turn a `ReaderSync` into an {@linkcode Iterator}. + */ + readSync(p: Uint8Array): number | null; + } + + /** + * An abstract interface which when implemented provides an interface to write + * bytes from an array buffer to a file/resource asynchronously. + * + * @deprecated Use {@linkcode WritableStream} instead. {@linkcode Writer} + * will be removed in v2.0.0. + * + * @category I/O */ + export interface Writer { + /** Writes `p.byteLength` bytes from `p` to the underlying data stream. It + * resolves to the number of bytes written from `p` (`0` <= `n` <= + * `p.byteLength`) or reject with the error encountered that caused the + * write to stop early. `write()` must reject with a non-null error if + * would resolve to `n` < `p.byteLength`. `write()` must not modify the + * slice data, even temporarily. + * + * This function is one of the lowest + * level APIs and most users should not work with this directly, but rather use + * [`writeAll()`](https://deno.land/std/streams/write_all.ts?s=writeAll) from + * [`std/streams/write_all.ts`](https://deno.land/std/streams/write_all.ts) + * instead. + * + * Implementations should not retain a reference to `p`. + */ + write(p: Uint8Array): Promise; + } + + /** + * An abstract interface which when implemented provides an interface to write + * bytes from an array buffer to a file/resource synchronously. + * + * @deprecated Use {@linkcode WritableStream} instead. {@linkcode WriterSync} + * will be removed in v2.0.0. + * + * @category I/O */ + export interface WriterSync { + /** Writes `p.byteLength` bytes from `p` to the underlying data + * stream. It returns the number of bytes written from `p` (`0` <= `n` + * <= `p.byteLength`) and any error encountered that caused the write to + * stop early. `writeSync()` must throw a non-null error if it returns `n` < + * `p.byteLength`. `writeSync()` must not modify the slice data, even + * temporarily. + * + * Implementations should not retain a reference to `p`. + */ + writeSync(p: Uint8Array): number; + } + + /** + * An abstract interface which when implemented provides an interface to close + * files/resources that were previously opened. + * + * @deprecated Use {@linkcode ReadableStream} and {@linkcode WritableStream} + * instead. {@linkcode Closer} will be removed in v2.0.0. + * + * @category I/O */ + export interface Closer { + /** Closes the resource, "freeing" the backing file/resource. */ + close(): void; + } + + /** + * An abstract interface which when implemented provides an interface to seek + * within an open file/resource asynchronously. + * + * @category I/O */ + export interface Seeker { + /** Seek sets the offset for the next `read()` or `write()` to offset, + * interpreted according to `whence`: `Start` means relative to the + * start of the file, `Current` means relative to the current offset, + * and `End` means relative to the end. Seek resolves to the new offset + * relative to the start of the file. + * + * Seeking to an offset before the start of the file is an error. Seeking to + * any positive offset is legal, but the behavior of subsequent I/O + * operations on the underlying object is implementation-dependent. + * + * It resolves with the updated offset. + */ + seek(offset: number | bigint, whence: SeekMode): Promise; + } + + /** + * An abstract interface which when implemented provides an interface to seek + * within an open file/resource synchronously. + * + * @category I/O */ + export interface SeekerSync { + /** Seek sets the offset for the next `readSync()` or `writeSync()` to + * offset, interpreted according to `whence`: `Start` means relative + * to the start of the file, `Current` means relative to the current + * offset, and `End` means relative to the end. + * + * Seeking to an offset before the start of the file is an error. Seeking to + * any positive offset is legal, but the behavior of subsequent I/O + * operations on the underlying object is implementation-dependent. + * + * It returns the updated offset. + */ + seekSync(offset: number | bigint, whence: SeekMode): number; + } + + /** + * Copies from `src` to `dst` until either EOF (`null`) is read from `src` or + * an error occurs. It resolves to the number of bytes copied or rejects with + * the first error encountered while copying. + * + * @deprecated Use {@linkcode https://deno.land/std/io/copy.ts?s=copy | copy} + * instead. {@linkcode Deno.copy} will be removed in v2.0.0. + * + * @category I/O + * + * @param src The source to copy from + * @param dst The destination to copy to + * @param options Can be used to tune size of the buffer. Default size is 32kB + */ + export function copy( + src: Reader, + dst: Writer, + options?: { bufSize?: number }, + ): Promise; + + /** + * Turns a Reader, `r`, into an async iterator. + * + * @deprecated Use {@linkcode ReadableStream} instead. + * {@linkcode Deno.iter} will be removed in v2.0.0. + * + * @category I/O + */ + export function iter( + r: Reader, + options?: { bufSize?: number }, + ): AsyncIterableIterator; + + /** + * Turns a ReaderSync, `r`, into an iterator. + * + * @deprecated Use {@linkcode ReadableStream} instead. + * {@linkcode Deno.iterSync} will be removed in v2.0.0. + * + * @category I/O + */ + export function iterSync( + r: ReaderSync, + options?: { + bufSize?: number; + }, + ): IterableIterator; + + /** Open a file and resolve to an instance of {@linkcode Deno.FsFile}. The + * file does not need to previously exist if using the `create` or `createNew` + * open options. The caller may have the resulting file automatically closed + * by the runtime once it's out of scope by declaring the file variable with + * the `using` keyword. + * + * ```ts + * using file = await Deno.open("/foo/bar.txt", { read: true, write: true }); + * // Do work with file + * ``` + * + * Alternatively, the caller may manually close the resource when finished with + * it. + * + * ```ts + * const file = await Deno.open("/foo/bar.txt", { read: true, write: true }); + * // Do work with file + * file.close(); + * ``` + * + * Requires `allow-read` and/or `allow-write` permissions depending on + * options. + * + * @tags allow-read, allow-write + * @category File System + */ + export function open( + path: string | URL, + options?: OpenOptions, + ): Promise; + + /** Synchronously open a file and return an instance of + * {@linkcode Deno.FsFile}. The file does not need to previously exist if + * using the `create` or `createNew` open options. The caller may have the + * resulting file automatically closed by the runtime once it's out of scope + * by declaring the file variable with the `using` keyword. + * + * ```ts + * using file = Deno.openSync("/foo/bar.txt", { read: true, write: true }); + * // Do work with file + * ``` + * + * Alternatively, the caller may manually close the resource when finished with + * it. + * + * ```ts + * const file = Deno.openSync("/foo/bar.txt", { read: true, write: true }); + * // Do work with file + * file.close(); + * ``` + * + * Requires `allow-read` and/or `allow-write` permissions depending on + * options. + * + * @tags allow-read, allow-write + * @category File System + */ + export function openSync(path: string | URL, options?: OpenOptions): FsFile; + + /** Creates a file if none exists or truncates an existing file and resolves to + * an instance of {@linkcode Deno.FsFile}. + * + * ```ts + * const file = await Deno.create("/foo/bar.txt"); + * ``` + * + * Requires `allow-read` and `allow-write` permissions. + * + * @tags allow-read, allow-write + * @category File System + */ + export function create(path: string | URL): Promise; + + /** Creates a file if none exists or truncates an existing file and returns + * an instance of {@linkcode Deno.FsFile}. + * + * ```ts + * const file = Deno.createSync("/foo/bar.txt"); + * ``` + * + * Requires `allow-read` and `allow-write` permissions. + * + * @tags allow-read, allow-write + * @category File System + */ + export function createSync(path: string | URL): FsFile; + + /** Read from a resource ID (`rid`) into an array buffer (`buffer`). + * + * Resolves to either the number of bytes read during the operation or EOF + * (`null`) if there was nothing more to read. + * + * It is possible for a read to successfully return with `0` bytes. This does + * not indicate EOF. + * + * This function is one of the lowest level APIs and most users should not + * work with this directly, but rather use {@linkcode ReadableStream} and + * {@linkcode https://deno.land/std/streams/mod.ts?s=toArrayBuffer|toArrayBuffer} + * instead. + * + * **It is not guaranteed that the full buffer will be read in a single call.** + * + * ```ts + * // if "/foo/bar.txt" contains the text "hello world": + * using file = await Deno.open("/foo/bar.txt"); + * const buf = new Uint8Array(100); + * const numberOfBytesRead = await Deno.read(file.rid, buf); // 11 bytes + * const text = new TextDecoder().decode(buf); // "hello world" + * ``` + * + * @deprecated Use `reader.read()` instead. {@linkcode Deno.read} will be + * removed in Deno 2.0. + * + * @category I/O + */ + export function read(rid: number, buffer: Uint8Array): Promise; + + /** Synchronously read from a resource ID (`rid`) into an array buffer + * (`buffer`). + * + * Returns either the number of bytes read during the operation or EOF + * (`null`) if there was nothing more to read. + * + * It is possible for a read to successfully return with `0` bytes. This does + * not indicate EOF. + * + * This function is one of the lowest level APIs and most users should not + * work with this directly, but rather use {@linkcode ReadableStream} and + * {@linkcode https://deno.land/std/streams/mod.ts?s=toArrayBuffer|toArrayBuffer} + * instead. + * + * **It is not guaranteed that the full buffer will be read in a single + * call.** + * + * ```ts + * // if "/foo/bar.txt" contains the text "hello world": + * using file = Deno.openSync("/foo/bar.txt"); + * const buf = new Uint8Array(100); + * const numberOfBytesRead = Deno.readSync(file.rid, buf); // 11 bytes + * const text = new TextDecoder().decode(buf); // "hello world" + * ``` + * + * @deprecated Use `reader.readSync()` instead. {@linkcode Deno.readSync} + * will be removed in Deno 2.0. + * + * @category I/O + */ + export function readSync(rid: number, buffer: Uint8Array): number | null; + + /** Write to the resource ID (`rid`) the contents of the array buffer (`data`). + * + * Resolves to the number of bytes written. This function is one of the lowest + * level APIs and most users should not work with this directly, but rather + * use {@linkcode WritableStream}, {@linkcode ReadableStream.from} and + * {@linkcode ReadableStream.pipeTo}. + * + * **It is not guaranteed that the full buffer will be written in a single + * call.** + * + * ```ts + * const encoder = new TextEncoder(); + * const data = encoder.encode("Hello world"); + * using file = await Deno.open("/foo/bar.txt", { write: true }); + * const bytesWritten = await Deno.write(file.rid, data); // 11 + * ``` + * + * @deprecated Use `writer.write()` instead. {@linkcode Deno.write} will be + * removed in Deno 2.0. + * + * @category I/O + */ + export function write(rid: number, data: Uint8Array): Promise; + + /** Synchronously write to the resource ID (`rid`) the contents of the array + * buffer (`data`). + * + * Returns the number of bytes written. This function is one of the lowest + * level APIs and most users should not work with this directly, but rather + * use {@linkcode WritableStream}, {@linkcode ReadableStream.from} and + * {@linkcode ReadableStream.pipeTo}. + * + * **It is not guaranteed that the full buffer will be written in a single + * call.** + * + * ```ts + * const encoder = new TextEncoder(); + * const data = encoder.encode("Hello world"); + * using file = Deno.openSync("/foo/bar.txt", { write: true }); + * const bytesWritten = Deno.writeSync(file.rid, data); // 11 + * ``` + * + * @deprecated Use `writer.writeSync()` instead. {@linkcode Deno.writeSync} + * will be removed in Deno 2.0. + * + * @category I/O + */ + export function writeSync(rid: number, data: Uint8Array): number; + + /** Seek a resource ID (`rid`) to the given `offset` under mode given by `whence`. + * The call resolves to the new position within the resource (bytes from the start). + * + * ```ts + * // Given file.rid pointing to file with "Hello world", which is 11 bytes long: + * using file = await Deno.open( + * "hello.txt", + * { read: true, write: true, truncate: true, create: true }, + * ); + * await file.write(new TextEncoder().encode("Hello world")); + * + * // advance cursor 6 bytes + * const cursorPosition = await Deno.seek(file.rid, 6, Deno.SeekMode.Start); + * console.log(cursorPosition); // 6 + * const buf = new Uint8Array(100); + * await file.read(buf); + * console.log(new TextDecoder().decode(buf)); // "world" + * ``` + * + * The seek modes work as follows: + * + * ```ts + * // Given file.rid pointing to file with "Hello world", which is 11 bytes long: + * using file = await Deno.open( + * "hello.txt", + * { read: true, write: true, truncate: true, create: true }, + * ); + * await file.write(new TextEncoder().encode("Hello world")); + * + * // Seek 6 bytes from the start of the file + * console.log(await Deno.seek(file.rid, 6, Deno.SeekMode.Start)); // "6" + * // Seek 2 more bytes from the current position + * console.log(await Deno.seek(file.rid, 2, Deno.SeekMode.Current)); // "8" + * // Seek backwards 2 bytes from the end of the file + * console.log(await Deno.seek(file.rid, -2, Deno.SeekMode.End)); // "9" (i.e. 11-2) + * ``` + * + * @deprecated Use `file.seek()` instead. {@linkcode Deno.seek} will be + * removed in Deno 2.0. + * + * @category I/O + */ + export function seek( + rid: number, + offset: number | bigint, + whence: SeekMode, + ): Promise; + + /** Synchronously seek a resource ID (`rid`) to the given `offset` under mode + * given by `whence`. The new position within the resource (bytes from the + * start) is returned. + * + * ```ts + * using file = Deno.openSync( + * "hello.txt", + * { read: true, write: true, truncate: true, create: true }, + * ); + * Deno.writeSync(file.rid, new TextEncoder().encode("Hello world")); + * + * // advance cursor 6 bytes + * const cursorPosition = Deno.seekSync(file.rid, 6, Deno.SeekMode.Start); + * console.log(cursorPosition); // 6 + * const buf = new Uint8Array(100); + * file.readSync(buf); + * console.log(new TextDecoder().decode(buf)); // "world" + * ``` + * + * The seek modes work as follows: + * + * ```ts + * // Given file.rid pointing to file with "Hello world", which is 11 bytes long: + * using file = Deno.openSync( + * "hello.txt", + * { read: true, write: true, truncate: true, create: true }, + * ); + * Deno.writeSync(file.rid, new TextEncoder().encode("Hello world")); + * + * // Seek 6 bytes from the start of the file + * console.log(Deno.seekSync(file.rid, 6, Deno.SeekMode.Start)); // "6" + * // Seek 2 more bytes from the current position + * console.log(Deno.seekSync(file.rid, 2, Deno.SeekMode.Current)); // "8" + * // Seek backwards 2 bytes from the end of the file + * console.log(Deno.seekSync(file.rid, -2, Deno.SeekMode.End)); // "9" (i.e. 11-2) + * ``` + * + * @deprecated Use `file.seekSync()` instead. {@linkcode Deno.seekSync} + * will be removed in Deno 2.0. + * + * @category I/O + */ + export function seekSync( + rid: number, + offset: number | bigint, + whence: SeekMode, + ): number; + + /** + * Flushes any pending data and metadata operations of the given file stream + * to disk. + * + * ```ts + * const file = await Deno.open( + * "my_file.txt", + * { read: true, write: true, create: true }, + * ); + * await file.write(new TextEncoder().encode("Hello World")); + * await file.truncate(1); + * await Deno.fsync(file.rid); + * console.log(await Deno.readTextFile("my_file.txt")); // H + * ``` + * + * @category I/O + */ + export function fsync(rid: number): Promise; + + /** + * Synchronously flushes any pending data and metadata operations of the given + * file stream to disk. + * + * ```ts + * const file = Deno.openSync( + * "my_file.txt", + * { read: true, write: true, create: true }, + * ); + * Deno.writeSync(file.rid, new TextEncoder().encode("Hello World")); + * file.truncateSync(1); + * Deno.fsyncSync(file.rid); + * console.log(Deno.readTextFileSync("my_file.txt")); // H + * ``` + * + * @category I/O + */ + export function fsyncSync(rid: number): void; + + /** + * Flushes any pending data operations of the given file stream to disk. + * ```ts + * const file = await Deno.open( + * "my_file.txt", + * { read: true, write: true, create: true }, + * ); + * await file.write(new TextEncoder().encode("Hello World")); + * await Deno.fdatasync(file.rid); + * console.log(await Deno.readTextFile("my_file.txt")); // Hello World + * ``` + * + * @category I/O + */ + export function fdatasync(rid: number): Promise; + + /** + * Synchronously flushes any pending data operations of the given file stream + * to disk. + * + * ```ts + * const file = Deno.openSync( + * "my_file.txt", + * { read: true, write: true, create: true }, + * ); + * Deno.writeSync(file.rid, new TextEncoder().encode("Hello World")); + * Deno.fdatasyncSync(file.rid); + * console.log(Deno.readTextFileSync("my_file.txt")); // Hello World + * ``` + * + * @category I/O + */ + export function fdatasyncSync(rid: number): void; + + /** Close the given resource ID (`rid`) which has been previously opened, such + * as via opening or creating a file. Closing a file when you are finished + * with it is important to avoid leaking resources. + * + * ```ts + * const file = await Deno.open("my_file.txt"); + * // do work with "file" object + * Deno.close(file.rid); + * ``` + * + * It is recommended to define the variable with the `using` keyword so the + * runtime will automatically close the resource when it goes out of scope. + * Doing so negates the need to manually close the resource. + * + * ```ts + * using file = await Deno.open("my_file.txt"); + * // do work with "file" object + * ``` + * + * @deprecated Use `.close()` method on the resource instead. + * {@linkcode Deno.close} will be removed in Deno 2.0. + * + * @category I/O + */ + export function close(rid: number): void; + + /** The Deno abstraction for reading and writing files. + * + * This is the most straight forward way of handling files within Deno and is + * recommended over using the discreet functions within the `Deno` namespace. + * + * ```ts + * using file = await Deno.open("/foo/bar.txt", { read: true }); + * const fileInfo = await file.stat(); + * if (fileInfo.isFile) { + * const buf = new Uint8Array(100); + * const numberOfBytesRead = await file.read(buf); // 11 bytes + * const text = new TextDecoder().decode(buf); // "hello world" + * } + * ``` + * + * @category File System + */ + export class FsFile + implements + Reader, + ReaderSync, + Writer, + WriterSync, + Seeker, + SeekerSync, + Closer, + Disposable { + /** + * The resource ID associated with the file instance. The resource ID + * should be considered an opaque reference to resource. + * + * @deprecated Use {@linkcode Deno.FsFile} instance methods instead. + * {@linkcode Deno.FsFile.rid} will be removed in Deno 2.0. + */ + readonly rid: number; + /** A {@linkcode ReadableStream} instance representing to the byte contents + * of the file. This makes it easy to interoperate with other web streams + * based APIs. + * + * ```ts + * using file = await Deno.open("my_file.txt", { read: true }); + * const decoder = new TextDecoder(); + * for await (const chunk of file.readable) { + * console.log(decoder.decode(chunk)); + * } + * ``` + */ + readonly readable: ReadableStream; + /** A {@linkcode WritableStream} instance to write the contents of the + * file. This makes it easy to interoperate with other web streams based + * APIs. + * + * ```ts + * const items = ["hello", "world"]; + * using file = await Deno.open("my_file.txt", { write: true }); + * const encoder = new TextEncoder(); + * const writer = file.writable.getWriter(); + * for (const item of items) { + * await writer.write(encoder.encode(item)); + * } + * ``` + */ + readonly writable: WritableStream; + /** + * The constructor which takes a resource ID. Generally `FsFile` should + * not be constructed directly. Instead use {@linkcode Deno.open} or + * {@linkcode Deno.openSync} to create a new instance of `FsFile`. + * + * @deprecated Use {@linkcode Deno.open} or {@linkcode Deno.openSync} + * instead. {@linkcode Deno.FsFile.constructor} will be removed in Deno + * 2.0. + */ + constructor(rid: number); + /** Write the contents of the array buffer (`p`) to the file. + * + * Resolves to the number of bytes written. + * + * **It is not guaranteed that the full buffer will be written in a single + * call.** + * + * ```ts + * const encoder = new TextEncoder(); + * const data = encoder.encode("Hello world"); + * using file = await Deno.open("/foo/bar.txt", { write: true }); + * const bytesWritten = await file.write(data); // 11 + * ``` + * + * @category I/O + */ + write(p: Uint8Array): Promise; + /** Synchronously write the contents of the array buffer (`p`) to the file. + * + * Returns the number of bytes written. + * + * **It is not guaranteed that the full buffer will be written in a single + * call.** + * + * ```ts + * const encoder = new TextEncoder(); + * const data = encoder.encode("Hello world"); + * using file = Deno.openSync("/foo/bar.txt", { write: true }); + * const bytesWritten = file.writeSync(data); // 11 + * ``` + */ + writeSync(p: Uint8Array): number; + /** Truncates (or extends) the file to reach the specified `len`. If `len` + * is not specified, then the entire file contents are truncated. + * + * ### Truncate the entire file + * + * ```ts + * using file = await Deno.open("my_file.txt", { write: true }); + * await file.truncate(); + * ``` + * + * ### Truncate part of the file + * + * ```ts + * // if "my_file.txt" contains the text "hello world": + * using file = await Deno.open("my_file.txt", { write: true }); + * await file.truncate(7); + * const buf = new Uint8Array(100); + * await file.read(buf); + * const text = new TextDecoder().decode(buf); // "hello w" + * ``` + */ + truncate(len?: number): Promise; + /** Synchronously truncates (or extends) the file to reach the specified + * `len`. If `len` is not specified, then the entire file contents are + * truncated. + * + * ### Truncate the entire file + * + * ```ts + * using file = Deno.openSync("my_file.txt", { write: true }); + * file.truncateSync(); + * ``` + * + * ### Truncate part of the file + * + * ```ts + * // if "my_file.txt" contains the text "hello world": + * using file = Deno.openSync("my_file.txt", { write: true }); + * file.truncateSync(7); + * const buf = new Uint8Array(100); + * file.readSync(buf); + * const text = new TextDecoder().decode(buf); // "hello w" + * ``` + */ + truncateSync(len?: number): void; + /** Read the file into an array buffer (`p`). + * + * Resolves to either the number of bytes read during the operation or EOF + * (`null`) if there was nothing more to read. + * + * It is possible for a read to successfully return with `0` bytes. This + * does not indicate EOF. + * + * **It is not guaranteed that the full buffer will be read in a single + * call.** + * + * ```ts + * // if "/foo/bar.txt" contains the text "hello world": + * using file = await Deno.open("/foo/bar.txt"); + * const buf = new Uint8Array(100); + * const numberOfBytesRead = await file.read(buf); // 11 bytes + * const text = new TextDecoder().decode(buf); // "hello world" + * ``` + */ + read(p: Uint8Array): Promise; + /** Synchronously read from the file into an array buffer (`p`). + * + * Returns either the number of bytes read during the operation or EOF + * (`null`) if there was nothing more to read. + * + * It is possible for a read to successfully return with `0` bytes. This + * does not indicate EOF. + * + * **It is not guaranteed that the full buffer will be read in a single + * call.** + * + * ```ts + * // if "/foo/bar.txt" contains the text "hello world": + * using file = Deno.openSync("/foo/bar.txt"); + * const buf = new Uint8Array(100); + * const numberOfBytesRead = file.readSync(buf); // 11 bytes + * const text = new TextDecoder().decode(buf); // "hello world" + * ``` + */ + readSync(p: Uint8Array): number | null; + /** Seek to the given `offset` under mode given by `whence`. The call + * resolves to the new position within the resource (bytes from the start). + * + * ```ts + * // Given file pointing to file with "Hello world", which is 11 bytes long: + * using file = await Deno.open( + * "hello.txt", + * { read: true, write: true, truncate: true, create: true }, + * ); + * await file.write(new TextEncoder().encode("Hello world")); + * + * // advance cursor 6 bytes + * const cursorPosition = await file.seek(6, Deno.SeekMode.Start); + * console.log(cursorPosition); // 6 + * const buf = new Uint8Array(100); + * await file.read(buf); + * console.log(new TextDecoder().decode(buf)); // "world" + * ``` + * + * The seek modes work as follows: + * + * ```ts + * // Given file.rid pointing to file with "Hello world", which is 11 bytes long: + * const file = await Deno.open( + * "hello.txt", + * { read: true, write: true, truncate: true, create: true }, + * ); + * await file.write(new TextEncoder().encode("Hello world")); + * + * // Seek 6 bytes from the start of the file + * console.log(await file.seek(6, Deno.SeekMode.Start)); // "6" + * // Seek 2 more bytes from the current position + * console.log(await file.seek(2, Deno.SeekMode.Current)); // "8" + * // Seek backwards 2 bytes from the end of the file + * console.log(await file.seek(-2, Deno.SeekMode.End)); // "9" (i.e. 11-2) + * ``` + */ + seek(offset: number | bigint, whence: SeekMode): Promise; + /** Synchronously seek to the given `offset` under mode given by `whence`. + * The new position within the resource (bytes from the start) is returned. + * + * ```ts + * using file = Deno.openSync( + * "hello.txt", + * { read: true, write: true, truncate: true, create: true }, + * ); + * file.writeSync(new TextEncoder().encode("Hello world")); + * + * // advance cursor 6 bytes + * const cursorPosition = file.seekSync(6, Deno.SeekMode.Start); + * console.log(cursorPosition); // 6 + * const buf = new Uint8Array(100); + * file.readSync(buf); + * console.log(new TextDecoder().decode(buf)); // "world" + * ``` + * + * The seek modes work as follows: + * + * ```ts + * // Given file.rid pointing to file with "Hello world", which is 11 bytes long: + * using file = Deno.openSync( + * "hello.txt", + * { read: true, write: true, truncate: true, create: true }, + * ); + * file.writeSync(new TextEncoder().encode("Hello world")); + * + * // Seek 6 bytes from the start of the file + * console.log(file.seekSync(6, Deno.SeekMode.Start)); // "6" + * // Seek 2 more bytes from the current position + * console.log(file.seekSync(2, Deno.SeekMode.Current)); // "8" + * // Seek backwards 2 bytes from the end of the file + * console.log(file.seekSync(-2, Deno.SeekMode.End)); // "9" (i.e. 11-2) + * ``` + */ + seekSync(offset: number | bigint, whence: SeekMode): number; + /** Resolves to a {@linkcode Deno.FileInfo} for the file. + * + * ```ts + * import { assert } from "https://deno.land/std/assert/mod.ts"; + * + * using file = await Deno.open("hello.txt"); + * const fileInfo = await file.stat(); + * assert(fileInfo.isFile); + * ``` + */ + stat(): Promise; + /** Synchronously returns a {@linkcode Deno.FileInfo} for the file. + * + * ```ts + * import { assert } from "https://deno.land/std/assert/mod.ts"; + * + * using file = Deno.openSync("hello.txt") + * const fileInfo = file.statSync(); + * assert(fileInfo.isFile); + * ``` + */ + statSync(): FileInfo; + /** **UNSTABLE**: New API, yet to be vetted. + * + * Flushes any pending data and metadata operations of the given file + * stream to disk. + * + * ```ts + * const file = await Deno.open( + * "my_file.txt", + * { read: true, write: true, create: true }, + * ); + * await file.write(new TextEncoder().encode("Hello World")); + * await file.truncate(1); + * await file.sync(); + * console.log(await Deno.readTextFile("my_file.txt")); // H + * ``` + * + * @category I/O + */ + sync(): Promise; + /** **UNSTABLE**: New API, yet to be vetted. + * + * Synchronously flushes any pending data and metadata operations of the given + * file stream to disk. + * + * ```ts + * const file = Deno.openSync( + * "my_file.txt", + * { read: true, write: true, create: true }, + * ); + * file.writeSync(new TextEncoder().encode("Hello World")); + * file.truncateSync(1); + * file.syncSync(); + * console.log(Deno.readTextFileSync("my_file.txt")); // H + * ``` + * + * @category I/O + */ + syncSync(): void; + /** **UNSTABLE**: New API, yet to be vetted. + * + * Flushes any pending data operations of the given file stream to disk. + * ```ts + * using file = await Deno.open( + * "my_file.txt", + * { read: true, write: true, create: true }, + * ); + * await file.write(new TextEncoder().encode("Hello World")); + * await file.syncData(); + * console.log(await Deno.readTextFile("my_file.txt")); // Hello World + * ``` + * + * @category I/O + */ + syncData(): Promise; + /** **UNSTABLE**: New API, yet to be vetted. + * + * Synchronously flushes any pending data operations of the given file stream + * to disk. + * + * ```ts + * using file = Deno.openSync( + * "my_file.txt", + * { read: true, write: true, create: true }, + * ); + * file.writeSync(new TextEncoder().encode("Hello World")); + * file.syncDataSync(); + * console.log(Deno.readTextFileSync("my_file.txt")); // Hello World + * ``` + * + * @category I/O + */ + syncDataSync(): void; + /** + * Changes the access (`atime`) and modification (`mtime`) times of the + * file stream resource. Given times are either in seconds (UNIX epoch + * time) or as `Date` objects. + * + * ```ts + * using file = await Deno.open("file.txt", { create: true, write: true }); + * await file.utime(1556495550, new Date()); + * ``` + * + * @category File System + */ + utime(atime: number | Date, mtime: number | Date): Promise; + /** + * Synchronously changes the access (`atime`) and modification (`mtime`) + * times of the file stream resource. Given times are either in seconds + * (UNIX epoch time) or as `Date` objects. + * + * ```ts + * using file = Deno.openSync("file.txt", { create: true, write: true }); + * file.utime(1556495550, new Date()); + * ``` + * + * @category File System + */ + utimeSync(atime: number | Date, mtime: number | Date): void; + /** Close the file. Closing a file when you are finished with it is + * important to avoid leaking resources. + * + * ```ts + * using file = await Deno.open("my_file.txt"); + * // do work with "file" object + * ``` + */ + close(): void; + + [Symbol.dispose](): void; + } + + /** + * The Deno abstraction for reading and writing files. + * + * @deprecated Use {@linkcode Deno.FsFile} instead. {@linkcode Deno.File} + * will be removed in v2.0.0. + * + * @category File System + */ + export const File: typeof FsFile; + + /** Gets the size of the console as columns/rows. + * + * ```ts + * const { columns, rows } = Deno.consoleSize(); + * ``` + * + * This returns the size of the console window as reported by the operating + * system. It's not a reflection of how many characters will fit within the + * console window, but can be used as part of that calculation. + * + * @category I/O + */ + export function consoleSize(): { + columns: number; + rows: number; + }; + + /** @category I/O */ + export interface SetRawOptions { + /** + * The `cbreak` option can be used to indicate that characters that + * correspond to a signal should still be generated. When disabling raw + * mode, this option is ignored. This functionality currently only works on + * Linux and Mac OS. + */ + cbreak: boolean; + } + + /** A reference to `stdin` which can be used to read directly from `stdin`. + * It implements the Deno specific {@linkcode Reader}, {@linkcode ReaderSync}, + * and {@linkcode Closer} interfaces as well as provides a + * {@linkcode ReadableStream} interface. + * + * ### Reading chunks from the readable stream + * + * ```ts + * const decoder = new TextDecoder(); + * for await (const chunk of Deno.stdin.readable) { + * const text = decoder.decode(chunk); + * // do something with the text + * } + * ``` + * + * @category I/O + */ + export const stdin: Reader & ReaderSync & Closer & { + /** + * The resource ID assigned to `stdin`. This can be used with the discreet + * I/O functions in the `Deno` namespace. + * + * @deprecated Use {@linkcode Deno.stdin} instance methods instead. + * {@linkcode Deno.stdin.rid} will be removed in Deno 2.0. + */ + readonly rid: number; + /** A readable stream interface to `stdin`. */ + readonly readable: ReadableStream; + /** + * Set TTY to be under raw mode or not. In raw mode, characters are read and + * returned as is, without being processed. All special processing of + * characters by the terminal is disabled, including echoing input + * characters. Reading from a TTY device in raw mode is faster than reading + * from a TTY device in canonical mode. + * + * ```ts + * Deno.stdin.setRaw(true, { cbreak: true }); + * ``` + * + * @category I/O + */ + setRaw(mode: boolean, options?: SetRawOptions): void; + /** + * Checks if `stdin` is a TTY (terminal). + * + * ```ts + * // This example is system and context specific + * Deno.stdin.isTerminal(); // true + * ``` + * + * @category I/O + */ + isTerminal(): boolean; + }; + /** A reference to `stdout` which can be used to write directly to `stdout`. + * It implements the Deno specific {@linkcode Writer}, {@linkcode WriterSync}, + * and {@linkcode Closer} interfaces as well as provides a + * {@linkcode WritableStream} interface. + * + * These are low level constructs, and the {@linkcode console} interface is a + * more straight forward way to interact with `stdout` and `stderr`. + * + * @category I/O + */ + export const stdout: Writer & WriterSync & Closer & { + /** + * The resource ID assigned to `stdout`. This can be used with the discreet + * I/O functions in the `Deno` namespace. + * + * @deprecated Use {@linkcode Deno.stdout} instance methods instead. + * {@linkcode Deno.stdout.rid} will be removed in Deno 2.0. + */ + readonly rid: number; + /** A writable stream interface to `stdout`. */ + readonly writable: WritableStream; + /** + * Checks if `stdout` is a TTY (terminal). + * + * ```ts + * // This example is system and context specific + * Deno.stdout.isTerminal(); // true + * ``` + * + * @category I/O + */ + isTerminal(): boolean; + }; + /** A reference to `stderr` which can be used to write directly to `stderr`. + * It implements the Deno specific {@linkcode Writer}, {@linkcode WriterSync}, + * and {@linkcode Closer} interfaces as well as provides a + * {@linkcode WritableStream} interface. + * + * These are low level constructs, and the {@linkcode console} interface is a + * more straight forward way to interact with `stdout` and `stderr`. + * + * @category I/O + */ + export const stderr: Writer & WriterSync & Closer & { + /** + * The resource ID assigned to `stderr`. This can be used with the discreet + * I/O functions in the `Deno` namespace. + * + * @deprecated Use {@linkcode Deno.stderr} instance methods instead. + * {@linkcode Deno.stderr.rid} will be removed in Deno 2.0. + */ + readonly rid: number; + /** A writable stream interface to `stderr`. */ + readonly writable: WritableStream; + /** + * Checks if `stderr` is a TTY (terminal). + * + * ```ts + * // This example is system and context specific + * Deno.stderr.isTerminal(); // true + * ``` + * + * @category I/O + */ + isTerminal(): boolean; + }; + + /** + * Options which can be set when doing {@linkcode Deno.open} and + * {@linkcode Deno.openSync}. + * + * @category File System */ + export interface OpenOptions { + /** Sets the option for read access. This option, when `true`, means that + * the file should be read-able if opened. + * + * @default {true} */ + read?: boolean; + /** Sets the option for write access. This option, when `true`, means that + * the file should be write-able if opened. If the file already exists, + * any write calls on it will overwrite its contents, by default without + * truncating it. + * + * @default {false} */ + write?: boolean; + /** Sets the option for the append mode. This option, when `true`, means + * that writes will append to a file instead of overwriting previous + * contents. + * + * Note that setting `{ write: true, append: true }` has the same effect as + * setting only `{ append: true }`. + * + * @default {false} */ + append?: boolean; + /** Sets the option for truncating a previous file. If a file is + * successfully opened with this option set it will truncate the file to `0` + * size if it already exists. The file must be opened with write access + * for truncate to work. + * + * @default {false} */ + truncate?: boolean; + /** Sets the option to allow creating a new file, if one doesn't already + * exist at the specified path. Requires write or append access to be + * used. + * + * @default {false} */ + create?: boolean; + /** If set to `true`, no file, directory, or symlink is allowed to exist at + * the target location. Requires write or append access to be used. When + * createNew is set to `true`, create and truncate are ignored. + * + * @default {false} */ + createNew?: boolean; + /** Permissions to use if creating the file (defaults to `0o666`, before + * the process's umask). + * + * Ignored on Windows. */ + mode?: number; + } + + /** + * Options which can be set when using {@linkcode Deno.readFile} or + * {@linkcode Deno.readFileSync}. + * + * @category File System */ + export interface ReadFileOptions { + /** + * An abort signal to allow cancellation of the file read operation. + * If the signal becomes aborted the readFile operation will be stopped + * and the promise returned will be rejected with an AbortError. + */ + signal?: AbortSignal; + } + + /** + * Check if a given resource id (`rid`) is a TTY (a terminal). + * + * ```ts + * // This example is system and context specific + * const nonTTYRid = Deno.openSync("my_file.txt").rid; + * const ttyRid = Deno.openSync("/dev/tty6").rid; + * console.log(Deno.isatty(nonTTYRid)); // false + * console.log(Deno.isatty(ttyRid)); // true + * ``` + * + * @deprecated Use `Deno.stdin.isTerminal()`, `Deno.stdout.isTerminal()` or + * `Deno.stderr.isTerminal()` instead. + * {@linkcode Deno.isatty} will be removed in v2.0.0. + * + * @category I/O + */ + export function isatty(rid: number): boolean; + + /** + * A variable-sized buffer of bytes with `read()` and `write()` methods. + * + * @deprecated Use + * {@linkcode https://deno.land/std/io/buffer.ts?s=Buffer | Buffer} instead. + * {@linkcode Deno.Buffer} will be removed in v2.0.0. + * + * @category I/O + */ + export class Buffer implements Reader, ReaderSync, Writer, WriterSync { + constructor(ab?: ArrayBuffer); + /** Returns a slice holding the unread portion of the buffer. + * + * The slice is valid for use only until the next buffer modification (that + * is, only until the next call to a method like `read()`, `write()`, + * `reset()`, or `truncate()`). If `options.copy` is false the slice aliases the buffer content at + * least until the next buffer modification, so immediate changes to the + * slice will affect the result of future reads. + * @param options Defaults to `{ copy: true }` + */ + bytes(options?: { copy?: boolean }): Uint8Array; + /** Returns whether the unread portion of the buffer is empty. */ + empty(): boolean; + /** A read only number of bytes of the unread portion of the buffer. */ + readonly length: number; + /** The read only capacity of the buffer's underlying byte slice, that is, + * the total space allocated for the buffer's data. */ + readonly capacity: number; + /** Discards all but the first `n` unread bytes from the buffer but + * continues to use the same allocated storage. It throws if `n` is + * negative or greater than the length of the buffer. */ + truncate(n: number): void; + /** Resets the buffer to be empty, but it retains the underlying storage for + * use by future writes. `.reset()` is the same as `.truncate(0)`. */ + reset(): void; + /** Reads the next `p.length` bytes from the buffer or until the buffer is + * drained. Returns the number of bytes read. If the buffer has no data to + * return, the return is EOF (`null`). */ + readSync(p: Uint8Array): number | null; + /** Reads the next `p.length` bytes from the buffer or until the buffer is + * drained. Resolves to the number of bytes read. If the buffer has no + * data to return, resolves to EOF (`null`). + * + * NOTE: This methods reads bytes synchronously; it's provided for + * compatibility with `Reader` interfaces. + */ + read(p: Uint8Array): Promise; + writeSync(p: Uint8Array): number; + /** NOTE: This methods writes bytes synchronously; it's provided for + * compatibility with `Writer` interface. */ + write(p: Uint8Array): Promise; + /** Grows the buffer's capacity, if necessary, to guarantee space for + * another `n` bytes. After `.grow(n)`, at least `n` bytes can be written to + * the buffer without another allocation. If `n` is negative, `.grow()` will + * throw. If the buffer can't grow it will throw an error. + * + * Based on Go Lang's + * [Buffer.Grow](https://golang.org/pkg/bytes/#Buffer.Grow). */ + grow(n: number): void; + /** Reads data from `r` until EOF (`null`) and appends it to the buffer, + * growing the buffer as needed. It resolves to the number of bytes read. + * If the buffer becomes too large, `.readFrom()` will reject with an error. + * + * Based on Go Lang's + * [Buffer.ReadFrom](https://golang.org/pkg/bytes/#Buffer.ReadFrom). */ + readFrom(r: Reader): Promise; + /** Reads data from `r` until EOF (`null`) and appends it to the buffer, + * growing the buffer as needed. It returns the number of bytes read. If the + * buffer becomes too large, `.readFromSync()` will throw an error. + * + * Based on Go Lang's + * [Buffer.ReadFrom](https://golang.org/pkg/bytes/#Buffer.ReadFrom). */ + readFromSync(r: ReaderSync): number; + } + + /** + * Read Reader `r` until EOF (`null`) and resolve to the content as + * Uint8Array`. + * + * @deprecated Use + * {@linkcode https://deno.land/std/io/read_all.ts?s=readAll | readAll} + * instead. {@linkcode Deno.readAll} will be removed in v2.0.0. + * + * @category I/O + */ + export function readAll(r: Reader): Promise; + + /** + * Synchronously reads Reader `r` until EOF (`null`) and returns the content + * as `Uint8Array`. + * + * @deprecated Use + * {@linkcode https://deno.land/std/io/read_all.ts?s=readAllSync | readAllSync} + * instead. {@linkcode Deno.readAllSync} will be removed in v2.0.0. + * + * @category I/O + */ + export function readAllSync(r: ReaderSync): Uint8Array; + + /** + * Write all the content of the array buffer (`arr`) to the writer (`w`). + * + * @deprecated Use + * {@linkcode https://deno.land/std/io/write_all.ts?s=writeAll | writeAll} + * instead. {@linkcode Deno.writeAll} will be removed in v2.0.0. + * + * @category I/O + */ + export function writeAll(w: Writer, arr: Uint8Array): Promise; + + /** + * Synchronously write all the content of the array buffer (`arr`) to the + * writer (`w`). + * + * @deprecated Use + * {@linkcode https://deno.land/std/io/write_all.ts?s=writeAllSync | writeAllSync} + * instead. {@linkcode Deno.writeAllSync} will be removed in v2.0.0. + * + * @category I/O + */ + export function writeAllSync(w: WriterSync, arr: Uint8Array): void; + + /** + * Options which can be set when using {@linkcode Deno.mkdir} and + * {@linkcode Deno.mkdirSync}. + * + * @category File System */ + export interface MkdirOptions { + /** If set to `true`, means that any intermediate directories will also be + * created (as with the shell command `mkdir -p`). + * + * Intermediate directories are created with the same permissions. + * + * When recursive is set to `true`, succeeds silently (without changing any + * permissions) if a directory already exists at the path, or if the path + * is a symlink to an existing directory. + * + * @default {false} */ + recursive?: boolean; + /** Permissions to use when creating the directory (defaults to `0o777`, + * before the process's umask). + * + * Ignored on Windows. */ + mode?: number; + } + + /** Creates a new directory with the specified path. + * + * ```ts + * await Deno.mkdir("new_dir"); + * await Deno.mkdir("nested/directories", { recursive: true }); + * await Deno.mkdir("restricted_access_dir", { mode: 0o700 }); + * ``` + * + * Defaults to throwing error if the directory already exists. + * + * Requires `allow-write` permission. + * + * @tags allow-write + * @category File System + */ + export function mkdir( + path: string | URL, + options?: MkdirOptions, + ): Promise; + + /** Synchronously creates a new directory with the specified path. + * + * ```ts + * Deno.mkdirSync("new_dir"); + * Deno.mkdirSync("nested/directories", { recursive: true }); + * Deno.mkdirSync("restricted_access_dir", { mode: 0o700 }); + * ``` + * + * Defaults to throwing error if the directory already exists. + * + * Requires `allow-write` permission. + * + * @tags allow-write + * @category File System + */ + export function mkdirSync(path: string | URL, options?: MkdirOptions): void; + + /** + * Options which can be set when using {@linkcode Deno.makeTempDir}, + * {@linkcode Deno.makeTempDirSync}, {@linkcode Deno.makeTempFile}, and + * {@linkcode Deno.makeTempFileSync}. + * + * @category File System */ + export interface MakeTempOptions { + /** Directory where the temporary directory should be created (defaults to + * the env variable `TMPDIR`, or the system's default, usually `/tmp`). + * + * Note that if the passed `dir` is relative, the path returned by + * `makeTempFile()` and `makeTempDir()` will also be relative. Be mindful of + * this when changing working directory. */ + dir?: string; + /** String that should precede the random portion of the temporary + * directory's name. */ + prefix?: string; + /** String that should follow the random portion of the temporary + * directory's name. */ + suffix?: string; + } + + /** Creates a new temporary directory in the default directory for temporary + * files, unless `dir` is specified. Other optional options include + * prefixing and suffixing the directory name with `prefix` and `suffix` + * respectively. + * + * This call resolves to the full path to the newly created directory. + * + * Multiple programs calling this function simultaneously will create different + * directories. It is the caller's responsibility to remove the directory when + * no longer needed. + * + * ```ts + * const tempDirName0 = await Deno.makeTempDir(); // e.g. /tmp/2894ea76 + * const tempDirName1 = await Deno.makeTempDir({ prefix: 'my_temp' }); // e.g. /tmp/my_temp339c944d + * ``` + * + * Requires `allow-write` permission. + * + * @tags allow-write + * @category File System + */ + // TODO(ry) Doesn't check permissions. + export function makeTempDir(options?: MakeTempOptions): Promise; + + /** Synchronously creates a new temporary directory in the default directory + * for temporary files, unless `dir` is specified. Other optional options + * include prefixing and suffixing the directory name with `prefix` and + * `suffix` respectively. + * + * The full path to the newly created directory is returned. + * + * Multiple programs calling this function simultaneously will create different + * directories. It is the caller's responsibility to remove the directory when + * no longer needed. + * + * ```ts + * const tempDirName0 = Deno.makeTempDirSync(); // e.g. /tmp/2894ea76 + * const tempDirName1 = Deno.makeTempDirSync({ prefix: 'my_temp' }); // e.g. /tmp/my_temp339c944d + * ``` + * + * Requires `allow-write` permission. + * + * @tags allow-write + * @category File System + */ + // TODO(ry) Doesn't check permissions. + export function makeTempDirSync(options?: MakeTempOptions): string; + + /** Creates a new temporary file in the default directory for temporary + * files, unless `dir` is specified. + * + * Other options include prefixing and suffixing the directory name with + * `prefix` and `suffix` respectively. + * + * This call resolves to the full path to the newly created file. + * + * Multiple programs calling this function simultaneously will create + * different files. It is the caller's responsibility to remove the file when + * no longer needed. + * + * ```ts + * const tmpFileName0 = await Deno.makeTempFile(); // e.g. /tmp/419e0bf2 + * const tmpFileName1 = await Deno.makeTempFile({ prefix: 'my_temp' }); // e.g. /tmp/my_temp754d3098 + * ``` + * + * Requires `allow-write` permission. + * + * @tags allow-write + * @category File System + */ + export function makeTempFile(options?: MakeTempOptions): Promise; + + /** Synchronously creates a new temporary file in the default directory for + * temporary files, unless `dir` is specified. + * + * Other options include prefixing and suffixing the directory name with + * `prefix` and `suffix` respectively. + * + * The full path to the newly created file is returned. + * + * Multiple programs calling this function simultaneously will create + * different files. It is the caller's responsibility to remove the file when + * no longer needed. + * + * ```ts + * const tempFileName0 = Deno.makeTempFileSync(); // e.g. /tmp/419e0bf2 + * const tempFileName1 = Deno.makeTempFileSync({ prefix: 'my_temp' }); // e.g. /tmp/my_temp754d3098 + * ``` + * + * Requires `allow-write` permission. + * + * @tags allow-write + * @category File System + */ + export function makeTempFileSync(options?: MakeTempOptions): string; + + /** Changes the permission of a specific file/directory of specified path. + * Ignores the process's umask. + * + * ```ts + * await Deno.chmod("/path/to/file", 0o666); + * ``` + * + * The mode is a sequence of 3 octal numbers. The first/left-most number + * specifies the permissions for the owner. The second number specifies the + * permissions for the group. The last/right-most number specifies the + * permissions for others. For example, with a mode of 0o764, the owner (7) + * can read/write/execute, the group (6) can read/write and everyone else (4) + * can read only. + * + * | Number | Description | + * | ------ | ----------- | + * | 7 | read, write, and execute | + * | 6 | read and write | + * | 5 | read and execute | + * | 4 | read only | + * | 3 | write and execute | + * | 2 | write only | + * | 1 | execute only | + * | 0 | no permission | + * + * NOTE: This API currently throws on Windows + * + * Requires `allow-write` permission. + * + * @tags allow-write + * @category File System + */ + export function chmod(path: string | URL, mode: number): Promise; + + /** Synchronously changes the permission of a specific file/directory of + * specified path. Ignores the process's umask. + * + * ```ts + * Deno.chmodSync("/path/to/file", 0o666); + * ``` + * + * For a full description, see {@linkcode Deno.chmod}. + * + * NOTE: This API currently throws on Windows + * + * Requires `allow-write` permission. + * + * @tags allow-write + * @category File System + */ + export function chmodSync(path: string | URL, mode: number): void; + + /** Change owner of a regular file or directory. + * + * This functionality is not available on Windows. + * + * ```ts + * await Deno.chown("myFile.txt", 1000, 1002); + * ``` + * + * Requires `allow-write` permission. + * + * Throws Error (not implemented) if executed on Windows. + * + * @tags allow-write + * @category File System + * + * @param path path to the file + * @param uid user id (UID) of the new owner, or `null` for no change + * @param gid group id (GID) of the new owner, or `null` for no change + */ + export function chown( + path: string | URL, + uid: number | null, + gid: number | null, + ): Promise; + + /** Synchronously change owner of a regular file or directory. + * + * This functionality is not available on Windows. + * + * ```ts + * Deno.chownSync("myFile.txt", 1000, 1002); + * ``` + * + * Requires `allow-write` permission. + * + * Throws Error (not implemented) if executed on Windows. + * + * @tags allow-write + * @category File System + * + * @param path path to the file + * @param uid user id (UID) of the new owner, or `null` for no change + * @param gid group id (GID) of the new owner, or `null` for no change + */ + export function chownSync( + path: string | URL, + uid: number | null, + gid: number | null, + ): void; + + /** + * Options which can be set when using {@linkcode Deno.remove} and + * {@linkcode Deno.removeSync}. + * + * @category File System */ + export interface RemoveOptions { + /** If set to `true`, path will be removed even if it's a non-empty directory. + * + * @default {false} */ + recursive?: boolean; + } + + /** Removes the named file or directory. + * + * ```ts + * await Deno.remove("/path/to/empty_dir/or/file"); + * await Deno.remove("/path/to/populated_dir/or/file", { recursive: true }); + * ``` + * + * Throws error if permission denied, path not found, or path is a non-empty + * directory and the `recursive` option isn't set to `true`. + * + * Requires `allow-write` permission. + * + * @tags allow-write + * @category File System + */ + export function remove( + path: string | URL, + options?: RemoveOptions, + ): Promise; + + /** Synchronously removes the named file or directory. + * + * ```ts + * Deno.removeSync("/path/to/empty_dir/or/file"); + * Deno.removeSync("/path/to/populated_dir/or/file", { recursive: true }); + * ``` + * + * Throws error if permission denied, path not found, or path is a non-empty + * directory and the `recursive` option isn't set to `true`. + * + * Requires `allow-write` permission. + * + * @tags allow-write + * @category File System + */ + export function removeSync(path: string | URL, options?: RemoveOptions): void; + + /** Synchronously renames (moves) `oldpath` to `newpath`. Paths may be files or + * directories. If `newpath` already exists and is not a directory, + * `renameSync()` replaces it. OS-specific restrictions may apply when + * `oldpath` and `newpath` are in different directories. + * + * ```ts + * Deno.renameSync("old/path", "new/path"); + * ``` + * + * On Unix-like OSes, this operation does not follow symlinks at either path. + * + * It varies between platforms when the operation throws errors, and if so what + * they are. It's always an error to rename anything to a non-empty directory. + * + * Requires `allow-read` and `allow-write` permissions. + * + * @tags allow-read, allow-write + * @category File System + */ + export function renameSync( + oldpath: string | URL, + newpath: string | URL, + ): void; + + /** Renames (moves) `oldpath` to `newpath`. Paths may be files or directories. + * If `newpath` already exists and is not a directory, `rename()` replaces it. + * OS-specific restrictions may apply when `oldpath` and `newpath` are in + * different directories. + * + * ```ts + * await Deno.rename("old/path", "new/path"); + * ``` + * + * On Unix-like OSes, this operation does not follow symlinks at either path. + * + * It varies between platforms when the operation throws errors, and if so + * what they are. It's always an error to rename anything to a non-empty + * directory. + * + * Requires `allow-read` and `allow-write` permissions. + * + * @tags allow-read, allow-write + * @category File System + */ + export function rename( + oldpath: string | URL, + newpath: string | URL, + ): Promise; + + /** Asynchronously reads and returns the entire contents of a file as an UTF-8 + * decoded string. Reading a directory throws an error. + * + * ```ts + * const data = await Deno.readTextFile("hello.txt"); + * console.log(data); + * ``` + * + * Requires `allow-read` permission. + * + * @tags allow-read + * @category File System + */ + export function readTextFile( + path: string | URL, + options?: ReadFileOptions, + ): Promise; + + /** Synchronously reads and returns the entire contents of a file as an UTF-8 + * decoded string. Reading a directory throws an error. + * + * ```ts + * const data = Deno.readTextFileSync("hello.txt"); + * console.log(data); + * ``` + * + * Requires `allow-read` permission. + * + * @tags allow-read + * @category File System + */ + export function readTextFileSync(path: string | URL): string; + + /** Reads and resolves to the entire contents of a file as an array of bytes. + * `TextDecoder` can be used to transform the bytes to string if required. + * Reading a directory returns an empty data array. + * + * ```ts + * const decoder = new TextDecoder("utf-8"); + * const data = await Deno.readFile("hello.txt"); + * console.log(decoder.decode(data)); + * ``` + * + * Requires `allow-read` permission. + * + * @tags allow-read + * @category File System + */ + export function readFile( + path: string | URL, + options?: ReadFileOptions, + ): Promise; + + /** Synchronously reads and returns the entire contents of a file as an array + * of bytes. `TextDecoder` can be used to transform the bytes to string if + * required. Reading a directory returns an empty data array. + * + * ```ts + * const decoder = new TextDecoder("utf-8"); + * const data = Deno.readFileSync("hello.txt"); + * console.log(decoder.decode(data)); + * ``` + * + * Requires `allow-read` permission. + * + * @tags allow-read + * @category File System + */ + export function readFileSync(path: string | URL): Uint8Array; + + /** Provides information about a file and is returned by + * {@linkcode Deno.stat}, {@linkcode Deno.lstat}, {@linkcode Deno.statSync}, + * and {@linkcode Deno.lstatSync} or from calling `stat()` and `statSync()` + * on an {@linkcode Deno.FsFile} instance. + * + * @category File System + */ + export interface FileInfo { + /** True if this is info for a regular file. Mutually exclusive to + * `FileInfo.isDirectory` and `FileInfo.isSymlink`. */ + isFile: boolean; + /** True if this is info for a regular directory. Mutually exclusive to + * `FileInfo.isFile` and `FileInfo.isSymlink`. */ + isDirectory: boolean; + /** True if this is info for a symlink. Mutually exclusive to + * `FileInfo.isFile` and `FileInfo.isDirectory`. */ + isSymlink: boolean; + /** The size of the file, in bytes. */ + size: number; + /** The last modification time of the file. This corresponds to the `mtime` + * field from `stat` on Linux/Mac OS and `ftLastWriteTime` on Windows. This + * may not be available on all platforms. */ + mtime: Date | null; + /** The last access time of the file. This corresponds to the `atime` + * field from `stat` on Unix and `ftLastAccessTime` on Windows. This may not + * be available on all platforms. */ + atime: Date | null; + /** The creation time of the file. This corresponds to the `birthtime` + * field from `stat` on Mac/BSD and `ftCreationTime` on Windows. This may + * not be available on all platforms. */ + birthtime: Date | null; + /** ID of the device containing the file. */ + dev: number; + /** Inode number. + * + * _Linux/Mac OS only._ */ + ino: number | null; + /** The underlying raw `st_mode` bits that contain the standard Unix + * permissions for this file/directory. + * + * _Linux/Mac OS only._ */ + mode: number | null; + /** Number of hard links pointing to this file. + * + * _Linux/Mac OS only._ */ + nlink: number | null; + /** User ID of the owner of this file. + * + * _Linux/Mac OS only._ */ + uid: number | null; + /** Group ID of the owner of this file. + * + * _Linux/Mac OS only._ */ + gid: number | null; + /** Device ID of this file. + * + * _Linux/Mac OS only._ */ + rdev: number | null; + /** Blocksize for filesystem I/O. + * + * _Linux/Mac OS only._ */ + blksize: number | null; + /** Number of blocks allocated to the file, in 512-byte units. + * + * _Linux/Mac OS only._ */ + blocks: number | null; + /** True if this is info for a block device. + * + * _Linux/Mac OS only._ */ + isBlockDevice: boolean | null; + /** True if this is info for a char device. + * + * _Linux/Mac OS only._ */ + isCharDevice: boolean | null; + /** True if this is info for a fifo. + * + * _Linux/Mac OS only._ */ + isFifo: boolean | null; + /** True if this is info for a socket. + * + * _Linux/Mac OS only._ */ + isSocket: boolean | null; + } + + /** Resolves to the absolute normalized path, with symbolic links resolved. + * + * ```ts + * // e.g. given /home/alice/file.txt and current directory /home/alice + * await Deno.symlink("file.txt", "symlink_file.txt"); + * const realPath = await Deno.realPath("./file.txt"); + * const realSymLinkPath = await Deno.realPath("./symlink_file.txt"); + * console.log(realPath); // outputs "/home/alice/file.txt" + * console.log(realSymLinkPath); // outputs "/home/alice/file.txt" + * ``` + * + * Requires `allow-read` permission for the target path. + * + * Also requires `allow-read` permission for the `CWD` if the target path is + * relative. + * + * @tags allow-read + * @category File System + */ + export function realPath(path: string | URL): Promise; + + /** Synchronously returns absolute normalized path, with symbolic links + * resolved. + * + * ```ts + * // e.g. given /home/alice/file.txt and current directory /home/alice + * Deno.symlinkSync("file.txt", "symlink_file.txt"); + * const realPath = Deno.realPathSync("./file.txt"); + * const realSymLinkPath = Deno.realPathSync("./symlink_file.txt"); + * console.log(realPath); // outputs "/home/alice/file.txt" + * console.log(realSymLinkPath); // outputs "/home/alice/file.txt" + * ``` + * + * Requires `allow-read` permission for the target path. + * + * Also requires `allow-read` permission for the `CWD` if the target path is + * relative. + * + * @tags allow-read + * @category File System + */ + export function realPathSync(path: string | URL): string; + + /** + * Information about a directory entry returned from {@linkcode Deno.readDir} + * and {@linkcode Deno.readDirSync}. + * + * @category File System */ + export interface DirEntry { + /** The file name of the entry. It is just the entity name and does not + * include the full path. */ + name: string; + /** True if this is info for a regular file. Mutually exclusive to + * `DirEntry.isDirectory` and `DirEntry.isSymlink`. */ + isFile: boolean; + /** True if this is info for a regular directory. Mutually exclusive to + * `DirEntry.isFile` and `DirEntry.isSymlink`. */ + isDirectory: boolean; + /** True if this is info for a symlink. Mutually exclusive to + * `DirEntry.isFile` and `DirEntry.isDirectory`. */ + isSymlink: boolean; + } + + /** Reads the directory given by `path` and returns an async iterable of + * {@linkcode Deno.DirEntry}. The order of entries is not guaranteed. + * + * ```ts + * for await (const dirEntry of Deno.readDir("/")) { + * console.log(dirEntry.name); + * } + * ``` + * + * Throws error if `path` is not a directory. + * + * Requires `allow-read` permission. + * + * @tags allow-read + * @category File System + */ + export function readDir(path: string | URL): AsyncIterable; + + /** Synchronously reads the directory given by `path` and returns an iterable + * of {@linkcode Deno.DirEntry}. The order of entries is not guaranteed. + * + * ```ts + * for (const dirEntry of Deno.readDirSync("/")) { + * console.log(dirEntry.name); + * } + * ``` + * + * Throws error if `path` is not a directory. + * + * Requires `allow-read` permission. + * + * @tags allow-read + * @category File System + */ + export function readDirSync(path: string | URL): Iterable; + + /** Copies the contents and permissions of one file to another specified path, + * by default creating a new file if needed, else overwriting. Fails if target + * path is a directory or is unwritable. + * + * ```ts + * await Deno.copyFile("from.txt", "to.txt"); + * ``` + * + * Requires `allow-read` permission on `fromPath`. + * + * Requires `allow-write` permission on `toPath`. + * + * @tags allow-read, allow-write + * @category File System + */ + export function copyFile( + fromPath: string | URL, + toPath: string | URL, + ): Promise; + + /** Synchronously copies the contents and permissions of one file to another + * specified path, by default creating a new file if needed, else overwriting. + * Fails if target path is a directory or is unwritable. + * + * ```ts + * Deno.copyFileSync("from.txt", "to.txt"); + * ``` + * + * Requires `allow-read` permission on `fromPath`. + * + * Requires `allow-write` permission on `toPath`. + * + * @tags allow-read, allow-write + * @category File System + */ + export function copyFileSync( + fromPath: string | URL, + toPath: string | URL, + ): void; + + /** Resolves to the full path destination of the named symbolic link. + * + * ```ts + * await Deno.symlink("./test.txt", "./test_link.txt"); + * const target = await Deno.readLink("./test_link.txt"); // full path of ./test.txt + * ``` + * + * Throws TypeError if called with a hard link. + * + * Requires `allow-read` permission. + * + * @tags allow-read + * @category File System + */ + export function readLink(path: string | URL): Promise; + + /** Synchronously returns the full path destination of the named symbolic + * link. + * + * ```ts + * Deno.symlinkSync("./test.txt", "./test_link.txt"); + * const target = Deno.readLinkSync("./test_link.txt"); // full path of ./test.txt + * ``` + * + * Throws TypeError if called with a hard link. + * + * Requires `allow-read` permission. + * + * @tags allow-read + * @category File System + */ + export function readLinkSync(path: string | URL): string; + + /** Resolves to a {@linkcode Deno.FileInfo} for the specified `path`. If + * `path` is a symlink, information for the symlink will be returned instead + * of what it points to. + * + * ```ts + * import { assert } from "https://deno.land/std/assert/mod.ts"; + * const fileInfo = await Deno.lstat("hello.txt"); + * assert(fileInfo.isFile); + * ``` + * + * Requires `allow-read` permission. + * + * @tags allow-read + * @category File System + */ + export function lstat(path: string | URL): Promise; + + /** Synchronously returns a {@linkcode Deno.FileInfo} for the specified + * `path`. If `path` is a symlink, information for the symlink will be + * returned instead of what it points to. + * + * ```ts + * import { assert } from "https://deno.land/std/assert/mod.ts"; + * const fileInfo = Deno.lstatSync("hello.txt"); + * assert(fileInfo.isFile); + * ``` + * + * Requires `allow-read` permission. + * + * @tags allow-read + * @category File System + */ + export function lstatSync(path: string | URL): FileInfo; + + /** Resolves to a {@linkcode Deno.FileInfo} for the specified `path`. Will + * always follow symlinks. + * + * ```ts + * import { assert } from "https://deno.land/std/assert/mod.ts"; + * const fileInfo = await Deno.stat("hello.txt"); + * assert(fileInfo.isFile); + * ``` + * + * Requires `allow-read` permission. + * + * @tags allow-read + * @category File System + */ + export function stat(path: string | URL): Promise; + + /** Synchronously returns a {@linkcode Deno.FileInfo} for the specified + * `path`. Will always follow symlinks. + * + * ```ts + * import { assert } from "https://deno.land/std/assert/mod.ts"; + * const fileInfo = Deno.statSync("hello.txt"); + * assert(fileInfo.isFile); + * ``` + * + * Requires `allow-read` permission. + * + * @tags allow-read + * @category File System + */ + export function statSync(path: string | URL): FileInfo; + + /** Options for writing to a file. + * + * @category File System + */ + export interface WriteFileOptions { + /** If set to `true`, will append to a file instead of overwriting previous + * contents. + * + * @default {false} */ + append?: boolean; + /** Sets the option to allow creating a new file, if one doesn't already + * exist at the specified path. + * + * @default {true} */ + create?: boolean; + /** If set to `true`, no file, directory, or symlink is allowed to exist at + * the target location. When createNew is set to `true`, `create` is ignored. + * + * @default {false} */ + createNew?: boolean; + /** Permissions always applied to file. */ + mode?: number; + /** An abort signal to allow cancellation of the file write operation. + * + * If the signal becomes aborted the write file operation will be stopped + * and the promise returned will be rejected with an {@linkcode AbortError}. + */ + signal?: AbortSignal; + } + + /** Write `data` to the given `path`, by default creating a new file if + * needed, else overwriting. + * + * ```ts + * const encoder = new TextEncoder(); + * const data = encoder.encode("Hello world\n"); + * await Deno.writeFile("hello1.txt", data); // overwrite "hello1.txt" or create it + * await Deno.writeFile("hello2.txt", data, { create: false }); // only works if "hello2.txt" exists + * await Deno.writeFile("hello3.txt", data, { mode: 0o777 }); // set permissions on new file + * await Deno.writeFile("hello4.txt", data, { append: true }); // add data to the end of the file + * ``` + * + * Requires `allow-write` permission, and `allow-read` if `options.create` is + * `false`. + * + * @tags allow-read, allow-write + * @category File System + */ + export function writeFile( + path: string | URL, + data: Uint8Array | ReadableStream, + options?: WriteFileOptions, + ): Promise; + + /** Synchronously write `data` to the given `path`, by default creating a new + * file if needed, else overwriting. + * + * ```ts + * const encoder = new TextEncoder(); + * const data = encoder.encode("Hello world\n"); + * Deno.writeFileSync("hello1.txt", data); // overwrite "hello1.txt" or create it + * Deno.writeFileSync("hello2.txt", data, { create: false }); // only works if "hello2.txt" exists + * Deno.writeFileSync("hello3.txt", data, { mode: 0o777 }); // set permissions on new file + * Deno.writeFileSync("hello4.txt", data, { append: true }); // add data to the end of the file + * ``` + * + * Requires `allow-write` permission, and `allow-read` if `options.create` is + * `false`. + * + * @tags allow-read, allow-write + * @category File System + */ + export function writeFileSync( + path: string | URL, + data: Uint8Array, + options?: WriteFileOptions, + ): void; + + /** Write string `data` to the given `path`, by default creating a new file if + * needed, else overwriting. + * + * ```ts + * await Deno.writeTextFile("hello1.txt", "Hello world\n"); // overwrite "hello1.txt" or create it + * ``` + * + * Requires `allow-write` permission, and `allow-read` if `options.create` is + * `false`. + * + * @tags allow-read, allow-write + * @category File System + */ + export function writeTextFile( + path: string | URL, + data: string | ReadableStream, + options?: WriteFileOptions, + ): Promise; + + /** Synchronously write string `data` to the given `path`, by default creating + * a new file if needed, else overwriting. + * + * ```ts + * Deno.writeTextFileSync("hello1.txt", "Hello world\n"); // overwrite "hello1.txt" or create it + * ``` + * + * Requires `allow-write` permission, and `allow-read` if `options.create` is + * `false`. + * + * @tags allow-read, allow-write + * @category File System + */ + export function writeTextFileSync( + path: string | URL, + data: string, + options?: WriteFileOptions, + ): void; + + /** Truncates (or extends) the specified file, to reach the specified `len`. + * If `len` is not specified then the entire file contents are truncated. + * + * ### Truncate the entire file + * ```ts + * await Deno.truncate("my_file.txt"); + * ``` + * + * ### Truncate part of the file + * + * ```ts + * const file = await Deno.makeTempFile(); + * await Deno.writeTextFile(file, "Hello World"); + * await Deno.truncate(file, 7); + * const data = await Deno.readFile(file); + * console.log(new TextDecoder().decode(data)); // "Hello W" + * ``` + * + * Requires `allow-write` permission. + * + * @tags allow-write + * @category File System + */ + export function truncate(name: string, len?: number): Promise; + + /** Synchronously truncates (or extends) the specified file, to reach the + * specified `len`. If `len` is not specified then the entire file contents + * are truncated. + * + * ### Truncate the entire file + * + * ```ts + * Deno.truncateSync("my_file.txt"); + * ``` + * + * ### Truncate part of the file + * + * ```ts + * const file = Deno.makeTempFileSync(); + * Deno.writeFileSync(file, new TextEncoder().encode("Hello World")); + * Deno.truncateSync(file, 7); + * const data = Deno.readFileSync(file); + * console.log(new TextDecoder().decode(data)); + * ``` + * + * Requires `allow-write` permission. + * + * @tags allow-write + * @category File System + */ + export function truncateSync(name: string, len?: number): void; + + /** @category Observability + * + * @deprecated {@linkcode Deno.metrics} will be removed in v2.0.0. + */ + export interface OpMetrics { + opsDispatched: number; + opsDispatchedSync: number; + opsDispatchedAsync: number; + opsDispatchedAsyncUnref: number; + opsCompleted: number; + opsCompletedSync: number; + opsCompletedAsync: number; + opsCompletedAsyncUnref: number; + bytesSentControl: number; + bytesSentData: number; + bytesReceived: number; + } + + /** @category Observability + * + * @deprecated {@linkcode Deno.metrics} will be removed in v2.0.0. + */ + export interface Metrics extends OpMetrics { + ops: Record; + } + + /** Receive metrics from the privileged side of Deno. This is primarily used + * in the development of Deno. _Ops_, also called _bindings_, are the + * go-between between Deno JavaScript sandbox and the rest of Deno. + * + * ```shell + * > console.table(Deno.metrics()) + * ┌─────────────────────────┬────────┐ + * │ (index) │ Values │ + * ├─────────────────────────┼────────┤ + * │ opsDispatched │ 3 │ + * │ opsDispatchedSync │ 2 │ + * │ opsDispatchedAsync │ 1 │ + * │ opsDispatchedAsyncUnref │ 0 │ + * │ opsCompleted │ 3 │ + * │ opsCompletedSync │ 2 │ + * │ opsCompletedAsync │ 1 │ + * │ opsCompletedAsyncUnref │ 0 │ + * │ bytesSentControl │ 73 │ + * │ bytesSentData │ 0 │ + * │ bytesReceived │ 375 │ + * └─────────────────────────┴────────┘ + * ``` + * + * @category Observability + * + * @deprecated {@linkcode Deno.metrics} will be removed in v2.0.0. + */ + export function metrics(): Metrics; + + /** + * A map of open resources that Deno is tracking. The key is the resource ID + * (_rid_) and the value is its representation. + * + * @deprecated {@linkcode Deno.resources} will be removed in Deno 2.0. + * + * @category Observability */ + interface ResourceMap { + [rid: number]: unknown; + } + + /** Returns a map of open resource IDs (_rid_) along with their string + * representations. This is an internal API and as such resource + * representation has `unknown` type; that means it can change any time and + * should not be depended upon. + * + * ```ts + * console.log(Deno.resources()); + * // { 0: "stdin", 1: "stdout", 2: "stderr" } + * Deno.openSync('../test.file'); + * console.log(Deno.resources()); + * // { 0: "stdin", 1: "stdout", 2: "stderr", 3: "fsFile" } + * ``` + * + * @deprecated {@linkcode Deno.resources} will be removed in Deno 2.0. + * + * @category Observability + */ + export function resources(): ResourceMap; + + /** + * Additional information for FsEvent objects with the "other" kind. + * + * - `"rescan"`: rescan notices indicate either a lapse in the events or a + * change in the filesystem such that events received so far can no longer + * be relied on to represent the state of the filesystem now. An + * application that simply reacts to file changes may not care about this. + * An application that keeps an in-memory representation of the filesystem + * will need to care, and will need to refresh that representation directly + * from the filesystem. + * + * @category File System + */ + export type FsEventFlag = "rescan"; + + /** + * Represents a unique file system event yielded by a + * {@linkcode Deno.FsWatcher}. + * + * @category File System */ + export interface FsEvent { + /** The kind/type of the file system event. */ + kind: "any" | "access" | "create" | "modify" | "remove" | "other"; + /** An array of paths that are associated with the file system event. */ + paths: string[]; + /** Any additional flags associated with the event. */ + flag?: FsEventFlag; + } + + /** + * Returned by {@linkcode Deno.watchFs}. It is an async iterator yielding up + * system events. To stop watching the file system by calling `.close()` + * method. + * + * @category File System + */ + export interface FsWatcher extends AsyncIterable, Disposable { + /** + * The resource id. + * + * @deprecated Use {@linkcode Deno.FsWatcher} instance methods instead. + * {@linkcode Deno.FsWatcher.rid} will be removed in Deno 2.0. + */ + readonly rid: number; + /** Stops watching the file system and closes the watcher resource. */ + close(): void; + /** + * Stops watching the file system and closes the watcher resource. + * + * @deprecated {@linkcode Deno.FsWatcher.return} will be removed in Deno 2.0. + */ + return?(value?: any): Promise>; + [Symbol.asyncIterator](): AsyncIterableIterator; + } + + /** Watch for file system events against one or more `paths`, which can be + * files or directories. These paths must exist already. One user action (e.g. + * `touch test.file`) can generate multiple file system events. Likewise, + * one user action can result in multiple file paths in one event (e.g. `mv + * old_name.txt new_name.txt`). + * + * The recursive option is `true` by default and, for directories, will watch + * the specified directory and all sub directories. + * + * Note that the exact ordering of the events can vary between operating + * systems. + * + * ```ts + * const watcher = Deno.watchFs("/"); + * for await (const event of watcher) { + * console.log(">>>> event", event); + * // { kind: "create", paths: [ "/foo.txt" ] } + * } + * ``` + * + * Call `watcher.close()` to stop watching. + * + * ```ts + * const watcher = Deno.watchFs("/"); + * + * setTimeout(() => { + * watcher.close(); + * }, 5000); + * + * for await (const event of watcher) { + * console.log(">>>> event", event); + * } + * ``` + * + * Requires `allow-read` permission. + * + * @tags allow-read + * @category File System + */ + export function watchFs( + paths: string | string[], + options?: { recursive: boolean }, + ): FsWatcher; + + /** + * Options which can be used with {@linkcode Deno.run}. + * + * @deprecated Use {@linkcode Deno.Command} instead. {@linkcode Deno.run} + * will be removed in v2.0.0. + * + * @category Sub Process */ + export interface RunOptions { + /** Arguments to pass. + * + * _Note_: the first element needs to be a path to the executable that is + * being run. */ + cmd: readonly string[] | [string | URL, ...string[]]; + /** The current working directory that should be used when running the + * sub-process. */ + cwd?: string; + /** Any environment variables to be set when running the sub-process. */ + env?: Record; + /** By default subprocess inherits `stdout` of parent process. To change + * this this option can be set to a resource ID (_rid_) of an open file, + * `"inherit"`, `"piped"`, or `"null"`: + * + * - _number_: the resource ID of an open file/resource. This allows you to + * write to a file. + * - `"inherit"`: The default if unspecified. The subprocess inherits from the + * parent. + * - `"piped"`: A new pipe should be arranged to connect the parent and child + * sub-process. + * - `"null"`: This stream will be ignored. This is the equivalent of attaching + * the stream to `/dev/null`. + */ + stdout?: "inherit" | "piped" | "null" | number; + /** By default subprocess inherits `stderr` of parent process. To change + * this this option can be set to a resource ID (_rid_) of an open file, + * `"inherit"`, `"piped"`, or `"null"`: + * + * - _number_: the resource ID of an open file/resource. This allows you to + * write to a file. + * - `"inherit"`: The default if unspecified. The subprocess inherits from the + * parent. + * - `"piped"`: A new pipe should be arranged to connect the parent and child + * sub-process. + * - `"null"`: This stream will be ignored. This is the equivalent of attaching + * the stream to `/dev/null`. + */ + stderr?: "inherit" | "piped" | "null" | number; + /** By default subprocess inherits `stdin` of parent process. To change + * this this option can be set to a resource ID (_rid_) of an open file, + * `"inherit"`, `"piped"`, or `"null"`: + * + * - _number_: the resource ID of an open file/resource. This allows you to + * read from a file. + * - `"inherit"`: The default if unspecified. The subprocess inherits from the + * parent. + * - `"piped"`: A new pipe should be arranged to connect the parent and child + * sub-process. + * - `"null"`: This stream will be ignored. This is the equivalent of attaching + * the stream to `/dev/null`. + */ + stdin?: "inherit" | "piped" | "null" | number; + } + + /** + * The status resolved from the `.status()` method of a + * {@linkcode Deno.Process} instance. + * + * If `success` is `true`, then `code` will be `0`, but if `success` is + * `false`, the sub-process exit code will be set in `code`. + * + * @deprecated Use {@linkcode Deno.Command} instead. {@linkcode Deno.run} + * will be removed in v2.0.0. + * + * @category Sub Process */ + export type ProcessStatus = + | { + success: true; + code: 0; + signal?: undefined; + } + | { + success: false; + code: number; + signal?: number; + }; + + /** + * Represents an instance of a sub process that is returned from + * {@linkcode Deno.run} which can be used to manage the sub-process. + * + * @deprecated Use {@linkcode Deno.Command} instead. {@linkcode Deno.run} + * will be removed in v2.0.0. + * + * @category Sub Process */ + export class Process { + /** The resource ID of the sub-process. */ + readonly rid: number; + /** The operating system's process ID for the sub-process. */ + readonly pid: number; + /** A reference to the sub-processes `stdin`, which allows interacting with + * the sub-process at a low level. */ + readonly stdin: T["stdin"] extends "piped" ? Writer & Closer & { + writable: WritableStream; + } + : (Writer & Closer & { writable: WritableStream }) | null; + /** A reference to the sub-processes `stdout`, which allows interacting with + * the sub-process at a low level. */ + readonly stdout: T["stdout"] extends "piped" ? Reader & Closer & { + readable: ReadableStream; + } + : (Reader & Closer & { readable: ReadableStream }) | null; + /** A reference to the sub-processes `stderr`, which allows interacting with + * the sub-process at a low level. */ + readonly stderr: T["stderr"] extends "piped" ? Reader & Closer & { + readable: ReadableStream; + } + : (Reader & Closer & { readable: ReadableStream }) | null; + /** Wait for the process to exit and return its exit status. + * + * Calling this function multiple times will return the same status. + * + * The `stdin` reference to the process will be closed before waiting to + * avoid a deadlock. + * + * If `stdout` and/or `stderr` were set to `"piped"`, they must be closed + * manually before the process can exit. + * + * To run process to completion and collect output from both `stdout` and + * `stderr` use: + * + * ```ts + * const p = Deno.run({ cmd: [ "echo", "hello world" ], stderr: 'piped', stdout: 'piped' }); + * const [status, stdout, stderr] = await Promise.all([ + * p.status(), + * p.output(), + * p.stderrOutput() + * ]); + * p.close(); + * ``` + */ + status(): Promise; + /** Buffer the stdout until EOF and return it as `Uint8Array`. + * + * You must set `stdout` to `"piped"` when creating the process. + * + * This calls `close()` on stdout after its done. */ + output(): Promise; + /** Buffer the stderr until EOF and return it as `Uint8Array`. + * + * You must set `stderr` to `"piped"` when creating the process. + * + * This calls `close()` on stderr after its done. */ + stderrOutput(): Promise; + /** Clean up resources associated with the sub-process instance. */ + close(): void; + /** Send a signal to process. + * Default signal is `"SIGTERM"`. + * + * ```ts + * const p = Deno.run({ cmd: [ "sleep", "20" ]}); + * p.kill("SIGTERM"); + * p.close(); + * ``` + */ + kill(signo?: Signal): void; + } + + /** Operating signals which can be listened for or sent to sub-processes. What + * signals and what their standard behaviors are OS dependent. + * + * @category Runtime Environment */ + export type Signal = + | "SIGABRT" + | "SIGALRM" + | "SIGBREAK" + | "SIGBUS" + | "SIGCHLD" + | "SIGCONT" + | "SIGEMT" + | "SIGFPE" + | "SIGHUP" + | "SIGILL" + | "SIGINFO" + | "SIGINT" + | "SIGIO" + | "SIGKILL" + | "SIGPIPE" + | "SIGPROF" + | "SIGPWR" + | "SIGQUIT" + | "SIGSEGV" + | "SIGSTKFLT" + | "SIGSTOP" + | "SIGSYS" + | "SIGTERM" + | "SIGTRAP" + | "SIGTSTP" + | "SIGTTIN" + | "SIGTTOU" + | "SIGURG" + | "SIGUSR1" + | "SIGUSR2" + | "SIGVTALRM" + | "SIGWINCH" + | "SIGXCPU" + | "SIGXFSZ"; + + /** Registers the given function as a listener of the given signal event. + * + * ```ts + * Deno.addSignalListener( + * "SIGTERM", + * () => { + * console.log("SIGTERM!") + * } + * ); + * ``` + * + * _Note_: On Windows only `"SIGINT"` (CTRL+C) and `"SIGBREAK"` (CTRL+Break) + * are supported. + * + * @category Runtime Environment + */ + export function addSignalListener(signal: Signal, handler: () => void): void; + + /** Removes the given signal listener that has been registered with + * {@linkcode Deno.addSignalListener}. + * + * ```ts + * const listener = () => { + * console.log("SIGTERM!") + * }; + * Deno.addSignalListener("SIGTERM", listener); + * Deno.removeSignalListener("SIGTERM", listener); + * ``` + * + * _Note_: On Windows only `"SIGINT"` (CTRL+C) and `"SIGBREAK"` (CTRL+Break) + * are supported. + * + * @category Runtime Environment + */ + export function removeSignalListener( + signal: Signal, + handler: () => void, + ): void; + + /** + * Spawns new subprocess. RunOptions must contain at a minimum the `opt.cmd`, + * an array of program arguments, the first of which is the binary. + * + * ```ts + * const p = Deno.run({ + * cmd: ["curl", "https://example.com"], + * }); + * const status = await p.status(); + * ``` + * + * Subprocess uses same working directory as parent process unless `opt.cwd` + * is specified. + * + * Environmental variables from parent process can be cleared using `opt.clearEnv`. + * Doesn't guarantee that only `opt.env` variables are present, + * as the OS may set environmental variables for processes. + * + * Environmental variables for subprocess can be specified using `opt.env` + * mapping. + * + * `opt.uid` sets the child process’s user ID. This translates to a setuid call + * in the child process. Failure in the setuid call will cause the spawn to fail. + * + * `opt.gid` is similar to `opt.uid`, but sets the group ID of the child process. + * This has the same semantics as the uid field. + * + * By default subprocess inherits stdio of parent process. To change + * this this, `opt.stdin`, `opt.stdout`, and `opt.stderr` can be set + * independently to a resource ID (_rid_) of an open file, `"inherit"`, + * `"piped"`, or `"null"`: + * + * - _number_: the resource ID of an open file/resource. This allows you to + * read or write to a file. + * - `"inherit"`: The default if unspecified. The subprocess inherits from the + * parent. + * - `"piped"`: A new pipe should be arranged to connect the parent and child + * sub-process. + * - `"null"`: This stream will be ignored. This is the equivalent of attaching + * the stream to `/dev/null`. + * + * Details of the spawned process are returned as an instance of + * {@linkcode Deno.Process}. + * + * Requires `allow-run` permission. + * + * @deprecated Use {@linkcode Deno.Command} instead. {@linkcode Deno.run} + * will be removed in v2.0.0. + * + * @tags allow-run + * @category Sub Process + */ + export function run(opt: T): Process; + + /** Create a child process. + * + * If any stdio options are not set to `"piped"`, accessing the corresponding + * field on the `Command` or its `CommandOutput` will throw a `TypeError`. + * + * If `stdin` is set to `"piped"`, the `stdin` {@linkcode WritableStream} + * needs to be closed manually. + * + * @example Spawn a subprocess and pipe the output to a file + * + * ```ts + * const command = new Deno.Command(Deno.execPath(), { + * args: [ + * "eval", + * "console.log('Hello World')", + * ], + * stdin: "piped", + * stdout: "piped", + * }); + * const child = command.spawn(); + * + * // open a file and pipe the subprocess output to it. + * child.stdout.pipeTo( + * Deno.openSync("output", { write: true, create: true }).writable, + * ); + * + * // manually close stdin + * child.stdin.close(); + * const status = await child.status; + * ``` + * + * @example Spawn a subprocess and collect its output + * + * ```ts + * const command = new Deno.Command(Deno.execPath(), { + * args: [ + * "eval", + * "console.log('hello'); console.error('world')", + * ], + * }); + * const { code, stdout, stderr } = await command.output(); + * console.assert(code === 0); + * console.assert("hello\n" === new TextDecoder().decode(stdout)); + * console.assert("world\n" === new TextDecoder().decode(stderr)); + * ``` + * + * @example Spawn a subprocess and collect its output synchronously + * + * ```ts + * const command = new Deno.Command(Deno.execPath(), { + * args: [ + * "eval", + * "console.log('hello'); console.error('world')", + * ], + * }); + * const { code, stdout, stderr } = command.outputSync(); + * console.assert(code === 0); + * console.assert("hello\n" === new TextDecoder().decode(stdout)); + * console.assert("world\n" === new TextDecoder().decode(stderr)); + * ``` + * + * @tags allow-run + * @category Sub Process + */ + export class Command { + constructor(command: string | URL, options?: CommandOptions); + /** + * Executes the {@linkcode Deno.Command}, waiting for it to finish and + * collecting all of its output. + * If `spawn()` was called, calling this function will collect the remaining + * output. + * + * Will throw an error if `stdin: "piped"` is set. + * + * If options `stdout` or `stderr` are not set to `"piped"`, accessing the + * corresponding field on {@linkcode Deno.CommandOutput} will throw a `TypeError`. + */ + output(): Promise; + /** + * Synchronously executes the {@linkcode Deno.Command}, waiting for it to + * finish and collecting all of its output. + * + * Will throw an error if `stdin: "piped"` is set. + * + * If options `stdout` or `stderr` are not set to `"piped"`, accessing the + * corresponding field on {@linkcode Deno.CommandOutput} will throw a `TypeError`. + */ + outputSync(): CommandOutput; + /** + * Spawns a streamable subprocess, allowing to use the other methods. + */ + spawn(): ChildProcess; + } + + /** + * The interface for handling a child process returned from + * {@linkcode Deno.Command.spawn}. + * + * @category Sub Process + */ + export class ChildProcess implements AsyncDisposable { + get stdin(): WritableStream; + get stdout(): ReadableStream; + get stderr(): ReadableStream; + readonly pid: number; + /** Get the status of the child. */ + readonly status: Promise; + + /** Waits for the child to exit completely, returning all its output and + * status. */ + output(): Promise; + /** Kills the process with given {@linkcode Deno.Signal}. + * + * Defaults to `SIGTERM` if no signal is provided. + * + * @param [signo="SIGTERM"] + */ + kill(signo?: Signal): void; + + /** Ensure that the status of the child process prevents the Deno process + * from exiting. */ + ref(): void; + /** Ensure that the status of the child process does not block the Deno + * process from exiting. */ + unref(): void; + + [Symbol.asyncDispose](): Promise; + } + + /** + * Options which can be set when calling {@linkcode Deno.Command}. + * + * @category Sub Process + */ + export interface CommandOptions { + /** Arguments to pass to the process. */ + args?: string[]; + /** + * The working directory of the process. + * + * If not specified, the `cwd` of the parent process is used. + */ + cwd?: string | URL; + /** + * Clear environmental variables from parent process. + * + * Doesn't guarantee that only `env` variables are present, as the OS may + * set environmental variables for processes. + * + * @default {false} + */ + clearEnv?: boolean; + /** Environmental variables to pass to the subprocess. */ + env?: Record; + /** + * Sets the child process’s user ID. This translates to a setuid call in the + * child process. Failure in the set uid call will cause the spawn to fail. + */ + uid?: number; + /** Similar to `uid`, but sets the group ID of the child process. */ + gid?: number; + /** + * An {@linkcode AbortSignal} that allows closing the process using the + * corresponding {@linkcode AbortController} by sending the process a + * SIGTERM signal. + * + * Not supported in {@linkcode Deno.Command.outputSync}. + */ + signal?: AbortSignal; + + /** How `stdin` of the spawned process should be handled. + * + * Defaults to `"inherit"` for `output` & `outputSync`, + * and `"inherit"` for `spawn`. */ + stdin?: "piped" | "inherit" | "null"; + /** How `stdout` of the spawned process should be handled. + * + * Defaults to `"piped"` for `output` & `outputSync`, + * and `"inherit"` for `spawn`. */ + stdout?: "piped" | "inherit" | "null"; + /** How `stderr` of the spawned process should be handled. + * + * Defaults to `"piped"` for `output` & `outputSync`, + * and `"inherit"` for `spawn`. */ + stderr?: "piped" | "inherit" | "null"; + + /** Skips quoting and escaping of the arguments on windows. This option + * is ignored on non-windows platforms. + * + * @default {false} */ + windowsRawArguments?: boolean; + } + + /** + * @category Sub Process + */ + export interface CommandStatus { + /** If the child process exits with a 0 status code, `success` will be set + * to `true`, otherwise `false`. */ + success: boolean; + /** The exit code of the child process. */ + code: number; + /** The signal associated with the child process. */ + signal: Signal | null; + } + + /** + * The interface returned from calling {@linkcode Deno.Command.output} or + * {@linkcode Deno.Command.outputSync} which represents the result of spawning the + * child process. + * + * @category Sub Process + */ + export interface CommandOutput extends CommandStatus { + /** The buffered output from the child process' `stdout`. */ + readonly stdout: Uint8Array; + /** The buffered output from the child process' `stderr`. */ + readonly stderr: Uint8Array; + } + + /** Option which can be specified when performing {@linkcode Deno.inspect}. + * + * @category Console and Debugging */ + export interface InspectOptions { + /** Stylize output with ANSI colors. + * + * @default {false} */ + colors?: boolean; + /** Try to fit more than one entry of a collection on the same line. + * + * @default {true} */ + compact?: boolean; + /** Traversal depth for nested objects. + * + * @default {4} */ + depth?: number; + /** The maximum length for an inspection to take up a single line. + * + * @default {80} */ + breakLength?: number; + /** Whether or not to escape sequences. + * + * @default {true} */ + escapeSequences?: boolean; + /** The maximum number of iterable entries to print. + * + * @default {100} */ + iterableLimit?: number; + /** Show a Proxy's target and handler. + * + * @default {false} */ + showProxy?: boolean; + /** Sort Object, Set and Map entries by key. + * + * @default {false} */ + sorted?: boolean; + /** Add a trailing comma for multiline collections. + * + * @default {false} */ + trailingComma?: boolean; + /** Evaluate the result of calling getters. + * + * @default {false} */ + getters?: boolean; + /** Show an object's non-enumerable properties. + * + * @default {false} */ + showHidden?: boolean; + /** The maximum length of a string before it is truncated with an + * ellipsis. */ + strAbbreviateSize?: number; + } + + /** Converts the input into a string that has the same format as printed by + * `console.log()`. + * + * ```ts + * const obj = { + * a: 10, + * b: "hello", + * }; + * const objAsString = Deno.inspect(obj); // { a: 10, b: "hello" } + * console.log(obj); // prints same value as objAsString, e.g. { a: 10, b: "hello" } + * ``` + * + * A custom inspect functions can be registered on objects, via the symbol + * `Symbol.for("Deno.customInspect")`, to control and customize the output + * of `inspect()` or when using `console` logging: + * + * ```ts + * class A { + * x = 10; + * y = "hello"; + * [Symbol.for("Deno.customInspect")]() { + * return `x=${this.x}, y=${this.y}`; + * } + * } + * + * const inStringFormat = Deno.inspect(new A()); // "x=10, y=hello" + * console.log(inStringFormat); // prints "x=10, y=hello" + * ``` + * + * A depth can be specified by using the `depth` option: + * + * ```ts + * Deno.inspect({a: {b: {c: {d: 'hello'}}}}, {depth: 2}); // { a: { b: [Object] } } + * ``` + * + * @category Console and Debugging + */ + export function inspect(value: unknown, options?: InspectOptions): string; + + /** The name of a privileged feature which needs permission. + * + * @category Permissions + */ + export type PermissionName = + | "run" + | "read" + | "write" + | "net" + | "env" + | "sys" + | "ffi" + | "hrtime"; + + /** The current status of the permission: + * + * - `"granted"` - the permission has been granted. + * - `"denied"` - the permission has been explicitly denied. + * - `"prompt"` - the permission has not explicitly granted nor denied. + * + * @category Permissions + */ + export type PermissionState = + | "granted" + | "denied" + | "prompt"; + + /** The permission descriptor for the `allow-run` and `deny-run` permissions, which controls + * access to what sub-processes can be executed by Deno. The option `command` + * allows scoping the permission to a specific executable. + * + * **Warning, in practice, `allow-run` is effectively the same as `allow-all` + * in the sense that malicious code could execute any arbitrary code on the + * host.** + * + * @category Permissions */ + export interface RunPermissionDescriptor { + name: "run"; + /** An `allow-run` or `deny-run` permission can be scoped to a specific executable, + * which would be relative to the start-up CWD of the Deno CLI. */ + command?: string | URL; + } + + /** The permission descriptor for the `allow-read` and `deny-read` permissions, which controls + * access to reading resources from the local host. The option `path` allows + * scoping the permission to a specific path (and if the path is a directory + * any sub paths). + * + * Permission granted under `allow-read` only allows runtime code to attempt + * to read, the underlying operating system may apply additional permissions. + * + * @category Permissions */ + export interface ReadPermissionDescriptor { + name: "read"; + /** An `allow-read` or `deny-read` permission can be scoped to a specific path (and if + * the path is a directory, any sub paths). */ + path?: string | URL; + } + + /** The permission descriptor for the `allow-write` and `deny-write` permissions, which + * controls access to writing to resources from the local host. The option + * `path` allow scoping the permission to a specific path (and if the path is + * a directory any sub paths). + * + * Permission granted under `allow-write` only allows runtime code to attempt + * to write, the underlying operating system may apply additional permissions. + * + * @category Permissions */ + export interface WritePermissionDescriptor { + name: "write"; + /** An `allow-write` or `deny-write` permission can be scoped to a specific path (and if + * the path is a directory, any sub paths). */ + path?: string | URL; + } + + /** The permission descriptor for the `allow-net` and `deny-net` permissions, which controls + * access to opening network ports and connecting to remote hosts via the + * network. The option `host` allows scoping the permission for outbound + * connection to a specific host and port. + * + * @category Permissions */ + export interface NetPermissionDescriptor { + name: "net"; + /** Optional host string of the form `"[:]"`. Examples: + * + * "github.com" + * "deno.land:8080" + */ + host?: string; + } + + /** The permission descriptor for the `allow-env` and `deny-env` permissions, which controls + * access to being able to read and write to the process environment variables + * as well as access other information about the environment. The option + * `variable` allows scoping the permission to a specific environment + * variable. + * + * @category Permissions */ + export interface EnvPermissionDescriptor { + name: "env"; + /** Optional environment variable name (e.g. `PATH`). */ + variable?: string; + } + + /** The permission descriptor for the `allow-sys` and `deny-sys` permissions, which controls + * access to sensitive host system information, which malicious code might + * attempt to exploit. The option `kind` allows scoping the permission to a + * specific piece of information. + * + * @category Permissions */ + export interface SysPermissionDescriptor { + name: "sys"; + /** The specific information to scope the permission to. */ + kind?: + | "loadavg" + | "hostname" + | "systemMemoryInfo" + | "networkInterfaces" + | "osRelease" + | "osUptime" + | "uid" + | "gid"; + } + + /** The permission descriptor for the `allow-ffi` and `deny-ffi` permissions, which controls + * access to loading _foreign_ code and interfacing with it via the + * [Foreign Function Interface API](https://deno.land/manual/runtime/ffi_api) + * available in Deno. The option `path` allows scoping the permission to a + * specific path on the host. + * + * @category Permissions */ + export interface FfiPermissionDescriptor { + name: "ffi"; + /** Optional path on the local host to scope the permission to. */ + path?: string | URL; + } + + /** The permission descriptor for the `allow-hrtime` and `deny-hrtime` permissions, which + * controls if the runtime code has access to high resolution time. High + * resolution time is considered sensitive information, because it can be used + * by malicious code to gain information about the host that it might not + * otherwise have access to. + * + * @category Permissions */ + export interface HrtimePermissionDescriptor { + name: "hrtime"; + } + + /** Permission descriptors which define a permission and can be queried, + * requested, or revoked. + * + * View the specifics of the individual descriptors for more information about + * each permission kind. + * + * @category Permissions + */ + export type PermissionDescriptor = + | RunPermissionDescriptor + | ReadPermissionDescriptor + | WritePermissionDescriptor + | NetPermissionDescriptor + | EnvPermissionDescriptor + | SysPermissionDescriptor + | FfiPermissionDescriptor + | HrtimePermissionDescriptor; + + /** The interface which defines what event types are supported by + * {@linkcode PermissionStatus} instances. + * + * @category Permissions */ + export interface PermissionStatusEventMap { + "change": Event; + } + + /** An {@linkcode EventTarget} returned from the {@linkcode Deno.permissions} + * API which can provide updates to any state changes of the permission. + * + * @category Permissions */ + export class PermissionStatus extends EventTarget { + // deno-lint-ignore no-explicit-any + onchange: ((this: PermissionStatus, ev: Event) => any) | null; + readonly state: PermissionState; + /** + * Describes if permission is only granted partially, eg. an access + * might be granted to "/foo" directory, but denied for "/foo/bar". + * In such case this field will be set to `true` when querying for + * read permissions of "/foo" directory. + */ + readonly partial: boolean; + addEventListener( + type: K, + listener: ( + this: PermissionStatus, + ev: PermissionStatusEventMap[K], + ) => any, + options?: boolean | AddEventListenerOptions, + ): void; + addEventListener( + type: string, + listener: EventListenerOrEventListenerObject, + options?: boolean | AddEventListenerOptions, + ): void; + removeEventListener( + type: K, + listener: ( + this: PermissionStatus, + ev: PermissionStatusEventMap[K], + ) => any, + options?: boolean | EventListenerOptions, + ): void; + removeEventListener( + type: string, + listener: EventListenerOrEventListenerObject, + options?: boolean | EventListenerOptions, + ): void; + } + + /** + * Deno's permission management API. + * + * The class which provides the interface for the {@linkcode Deno.permissions} + * global instance and is based on the web platform + * [Permissions API](https://developer.mozilla.org/en-US/docs/Web/API/Permissions_API), + * though some proposed parts of the API which are useful in a server side + * runtime context were removed or abandoned in the web platform specification + * which is why it was chosen to locate it in the {@linkcode Deno} namespace + * instead. + * + * By default, if the `stdin`/`stdout` is TTY for the Deno CLI (meaning it can + * send and receive text), then the CLI will prompt the user to grant + * permission when an un-granted permission is requested. This behavior can + * be changed by using the `--no-prompt` command at startup. When prompting + * the CLI will request the narrowest permission possible, potentially making + * it annoying to the user. The permissions APIs allow the code author to + * request a wider set of permissions at one time in order to provide a better + * user experience. + * + * @category Permissions */ + export class Permissions { + /** Resolves to the current status of a permission. + * + * Note, if the permission is already granted, `request()` will not prompt + * the user again, therefore `query()` is only necessary if you are going + * to react differently existing permissions without wanting to modify them + * or prompt the user to modify them. + * + * ```ts + * const status = await Deno.permissions.query({ name: "read", path: "/etc" }); + * console.log(status.state); + * ``` + */ + query(desc: PermissionDescriptor): Promise; + + /** Returns the current status of a permission. + * + * Note, if the permission is already granted, `request()` will not prompt + * the user again, therefore `querySync()` is only necessary if you are going + * to react differently existing permissions without wanting to modify them + * or prompt the user to modify them. + * + * ```ts + * const status = Deno.permissions.querySync({ name: "read", path: "/etc" }); + * console.log(status.state); + * ``` + */ + querySync(desc: PermissionDescriptor): PermissionStatus; + + /** Revokes a permission, and resolves to the state of the permission. + * + * ```ts + * import { assert } from "https://deno.land/std/assert/mod.ts"; + * + * const status = await Deno.permissions.revoke({ name: "run" }); + * assert(status.state !== "granted") + * ``` + */ + revoke(desc: PermissionDescriptor): Promise; + + /** Revokes a permission, and returns the state of the permission. + * + * ```ts + * import { assert } from "https://deno.land/std/assert/mod.ts"; + * + * const status = Deno.permissions.revokeSync({ name: "run" }); + * assert(status.state !== "granted") + * ``` + */ + revokeSync(desc: PermissionDescriptor): PermissionStatus; + + /** Requests the permission, and resolves to the state of the permission. + * + * If the permission is already granted, the user will not be prompted to + * grant the permission again. + * + * ```ts + * const status = await Deno.permissions.request({ name: "env" }); + * if (status.state === "granted") { + * console.log("'env' permission is granted."); + * } else { + * console.log("'env' permission is denied."); + * } + * ``` + */ + request(desc: PermissionDescriptor): Promise; + + /** Requests the permission, and returns the state of the permission. + * + * If the permission is already granted, the user will not be prompted to + * grant the permission again. + * + * ```ts + * const status = Deno.permissions.requestSync({ name: "env" }); + * if (status.state === "granted") { + * console.log("'env' permission is granted."); + * } else { + * console.log("'env' permission is denied."); + * } + * ``` + */ + requestSync(desc: PermissionDescriptor): PermissionStatus; + } + + /** Deno's permission management API. + * + * It is a singleton instance of the {@linkcode Permissions} object and is + * based on the web platform + * [Permissions API](https://developer.mozilla.org/en-US/docs/Web/API/Permissions_API), + * though some proposed parts of the API which are useful in a server side + * runtime context were removed or abandoned in the web platform specification + * which is why it was chosen to locate it in the {@linkcode Deno} namespace + * instead. + * + * By default, if the `stdin`/`stdout` is TTY for the Deno CLI (meaning it can + * send and receive text), then the CLI will prompt the user to grant + * permission when an un-granted permission is requested. This behavior can + * be changed by using the `--no-prompt` command at startup. When prompting + * the CLI will request the narrowest permission possible, potentially making + * it annoying to the user. The permissions APIs allow the code author to + * request a wider set of permissions at one time in order to provide a better + * user experience. + * + * Requesting already granted permissions will not prompt the user and will + * return that the permission was granted. + * + * ### Querying + * + * ```ts + * const status = await Deno.permissions.query({ name: "read", path: "/etc" }); + * console.log(status.state); + * ``` + * + * ```ts + * const status = Deno.permissions.querySync({ name: "read", path: "/etc" }); + * console.log(status.state); + * ``` + * + * ### Revoking + * + * ```ts + * import { assert } from "https://deno.land/std/assert/mod.ts"; + * + * const status = await Deno.permissions.revoke({ name: "run" }); + * assert(status.state !== "granted") + * ``` + * + * ```ts + * import { assert } from "https://deno.land/std/assert/mod.ts"; + * + * const status = Deno.permissions.revokeSync({ name: "run" }); + * assert(status.state !== "granted") + * ``` + * + * ### Requesting + * + * ```ts + * const status = await Deno.permissions.request({ name: "env" }); + * if (status.state === "granted") { + * console.log("'env' permission is granted."); + * } else { + * console.log("'env' permission is denied."); + * } + * ``` + * + * ```ts + * const status = Deno.permissions.requestSync({ name: "env" }); + * if (status.state === "granted") { + * console.log("'env' permission is granted."); + * } else { + * console.log("'env' permission is denied."); + * } + * ``` + * + * @category Permissions + */ + export const permissions: Permissions; + + /** Information related to the build of the current Deno runtime. + * + * Users are discouraged from code branching based on this information, as + * assumptions about what is available in what build environment might change + * over time. Developers should specifically sniff out the features they + * intend to use. + * + * The intended use for the information is for logging and debugging purposes. + * + * @category Runtime Environment + */ + export const build: { + /** The [LLVM](https://llvm.org/) target triple, which is the combination + * of `${arch}-${vendor}-${os}` and represent the specific build target that + * the current runtime was built for. */ + target: string; + /** Instruction set architecture that the Deno CLI was built for. */ + arch: "x86_64" | "aarch64"; + /** The operating system that the Deno CLI was built for. `"darwin"` is + * also known as OSX or MacOS. */ + os: + | "darwin" + | "linux" + | "android" + | "windows" + | "freebsd" + | "netbsd" + | "aix" + | "solaris" + | "illumos"; + /** The computer vendor that the Deno CLI was built for. */ + vendor: string; + /** Optional environment flags that were set for this build of Deno CLI. */ + env?: string; + }; + + /** Version information related to the current Deno CLI runtime environment. + * + * Users are discouraged from code branching based on this information, as + * assumptions about what is available in what build environment might change + * over time. Developers should specifically sniff out the features they + * intend to use. + * + * The intended use for the information is for logging and debugging purposes. + * + * @category Runtime Environment + */ + export const version: { + /** Deno CLI's version. For example: `"1.26.0"`. */ + deno: string; + /** The V8 version used by Deno. For example: `"10.7.100.0"`. + * + * V8 is the underlying JavaScript runtime platform that Deno is built on + * top of. */ + v8: string; + /** The TypeScript version used by Deno. For example: `"4.8.3"`. + * + * A version of the TypeScript type checker and language server is built-in + * to the Deno CLI. */ + typescript: string; + }; + + /** Returns the script arguments to the program. + * + * Give the following command line invocation of Deno: + * + * ```sh + * deno run --allow-read https://examples.deno.land/command-line-arguments.ts Sushi + * ``` + * + * Then `Deno.args` will contain: + * + * ```ts + * [ "Sushi" ] + * ``` + * + * If you are looking for a structured way to parse arguments, there is the + * [`std/flags`](https://deno.land/std/flags) module as part of the Deno + * standard library. + * + * @category Runtime Environment + */ + export const args: string[]; + + /** + * A symbol which can be used as a key for a custom method which will be + * called when `Deno.inspect()` is called, or when the object is logged to + * the console. + * + * @deprecated Use `Symbol.for("Deno.customInspect")` instead. This symbol + * will be removed in Deno 2.0. + * + * @category Console and Debugging + */ + export const customInspect: unique symbol; + + /** The URL of the entrypoint module entered from the command-line. It + * requires read permission to the CWD. + * + * Also see {@linkcode ImportMeta} for other related information. + * + * @tags allow-read + * @category Runtime Environment + */ + export const mainModule: string; + + /** Options that can be used with {@linkcode symlink} and + * {@linkcode symlinkSync}. + * + * @category File System */ + export interface SymlinkOptions { + /** If the symbolic link should be either a file or directory. This option + * only applies to Windows and is ignored on other operating systems. */ + type: "file" | "dir"; + } + + /** + * Creates `newpath` as a symbolic link to `oldpath`. + * + * The `options.type` parameter can be set to `"file"` or `"dir"`. This + * argument is only available on Windows and ignored on other platforms. + * + * ```ts + * await Deno.symlink("old/name", "new/name"); + * ``` + * + * Requires full `allow-read` and `allow-write` permissions. + * + * @tags allow-read, allow-write + * @category File System + */ + export function symlink( + oldpath: string | URL, + newpath: string | URL, + options?: SymlinkOptions, + ): Promise; + + /** + * Creates `newpath` as a symbolic link to `oldpath`. + * + * The `options.type` parameter can be set to `"file"` or `"dir"`. This + * argument is only available on Windows and ignored on other platforms. + * + * ```ts + * Deno.symlinkSync("old/name", "new/name"); + * ``` + * + * Requires full `allow-read` and `allow-write` permissions. + * + * @tags allow-read, allow-write + * @category File System + */ + export function symlinkSync( + oldpath: string | URL, + newpath: string | URL, + options?: SymlinkOptions, + ): void; + + /** + * Truncates or extends the specified file stream, to reach the specified + * `len`. + * + * If `len` is not specified then the entire file contents are truncated as if + * `len` was set to `0`. + * + * If the file previously was larger than this new length, the extra data is + * lost. + * + * If the file previously was shorter, it is extended, and the extended part + * reads as null bytes ('\0'). + * + * ### Truncate the entire file + * + * ```ts + * const file = await Deno.open( + * "my_file.txt", + * { read: true, write: true, create: true } + * ); + * await Deno.ftruncate(file.rid); + * ``` + * + * ### Truncate part of the file + * + * ```ts + * const file = await Deno.open( + * "my_file.txt", + * { read: true, write: true, create: true } + * ); + * await file.write(new TextEncoder().encode("Hello World")); + * await Deno.ftruncate(file.rid, 7); + * const data = new Uint8Array(32); + * await Deno.read(file.rid, data); + * console.log(new TextDecoder().decode(data)); // Hello W + * ``` + * + * @deprecated Use {@linkcode Deno.FsFile.truncate} instead. + * {@linkcode Deno.ftruncate} will be removed in Deno 2.0. + * + * @category File System + */ + export function ftruncate(rid: number, len?: number): Promise; + + /** + * Synchronously truncates or extends the specified file stream, to reach the + * specified `len`. + * + * If `len` is not specified then the entire file contents are truncated as if + * `len` was set to `0`. + * + * If the file previously was larger than this new length, the extra data is + * lost. + * + * If the file previously was shorter, it is extended, and the extended part + * reads as null bytes ('\0'). + * + * ### Truncate the entire file + * + * ```ts + * const file = Deno.openSync( + * "my_file.txt", + * { read: true, write: true, truncate: true, create: true } + * ); + * Deno.ftruncateSync(file.rid); + * ``` + * + * ### Truncate part of the file + * + * ```ts + * const file = Deno.openSync( + * "my_file.txt", + * { read: true, write: true, create: true } + * ); + * Deno.writeSync(file.rid, new TextEncoder().encode("Hello World")); + * Deno.ftruncateSync(file.rid, 7); + * Deno.seekSync(file.rid, 0, Deno.SeekMode.Start); + * const data = new Uint8Array(32); + * Deno.readSync(file.rid, data); + * console.log(new TextDecoder().decode(data)); // Hello W + * ``` + * + * @deprecated Use {@linkcode Deno.FsFile.truncateSync} instead. + * {@linkcode Deno.ftruncateSync} will be removed in Deno 2.0. + * + * @category File System + */ + export function ftruncateSync(rid: number, len?: number): void; + + /** + * Synchronously changes the access (`atime`) and modification (`mtime`) times + * of a file stream resource referenced by `rid`. Given times are either in + * seconds (UNIX epoch time) or as `Date` objects. + * + * ```ts + * const file = Deno.openSync("file.txt", { create: true, write: true }); + * Deno.futimeSync(file.rid, 1556495550, new Date()); + * ``` + * + * @deprecated Use {@linkcode Deno.FsFile.utimeSync} instead. + * {@linkcode Deno.futimeSync} will be removed in Deno 2.0. + * + * @category File System + */ + export function futimeSync( + rid: number, + atime: number | Date, + mtime: number | Date, + ): void; + + /** + * Changes the access (`atime`) and modification (`mtime`) times of a file + * stream resource referenced by `rid`. Given times are either in seconds + * (UNIX epoch time) or as `Date` objects. + * + * ```ts + * const file = await Deno.open("file.txt", { create: true, write: true }); + * await Deno.futime(file.rid, 1556495550, new Date()); + * ``` + * + * @deprecated Use {@linkcode Deno.FsFile.utime} instead. + * {@linkcode Deno.futime} will be removed in Deno 2.0. + * + * @category File System + */ + export function futime( + rid: number, + atime: number | Date, + mtime: number | Date, + ): Promise; + + /** + * Returns a `Deno.FileInfo` for the given file stream. + * + * ```ts + * import { assert } from "https://deno.land/std/assert/mod.ts"; + * + * const file = await Deno.open("file.txt", { read: true }); + * const fileInfo = await Deno.fstat(file.rid); + * assert(fileInfo.isFile); + * ``` + * + * @deprecated Use `file.stat()` instead. + * {@linkcode Deno.fstat} will be removed in Deno 2.0. + * + * @category File System + */ + export function fstat(rid: number): Promise; + + /** + * Synchronously returns a {@linkcode Deno.FileInfo} for the given file + * stream. + * + * ```ts + * import { assert } from "https://deno.land/std/assert/mod.ts"; + * + * const file = Deno.openSync("file.txt", { read: true }); + * const fileInfo = Deno.fstatSync(file.rid); + * assert(fileInfo.isFile); + * ``` + * + * @deprecated Use `file.statSync()` instead. + * {@linkcode Deno.fstatSync} will be removed in Deno 2.0. + * + * @category File System + */ + export function fstatSync(rid: number): FileInfo; + + /** + * Synchronously changes the access (`atime`) and modification (`mtime`) times + * of a file system object referenced by `path`. Given times are either in + * seconds (UNIX epoch time) or as `Date` objects. + * + * ```ts + * Deno.utimeSync("myfile.txt", 1556495550, new Date()); + * ``` + * + * Requires `allow-write` permission. + * + * @tags allow-write + * @category File System + */ + export function utimeSync( + path: string | URL, + atime: number | Date, + mtime: number | Date, + ): void; + + /** + * Changes the access (`atime`) and modification (`mtime`) times of a file + * system object referenced by `path`. Given times are either in seconds + * (UNIX epoch time) or as `Date` objects. + * + * ```ts + * await Deno.utime("myfile.txt", 1556495550, new Date()); + * ``` + * + * Requires `allow-write` permission. + * + * @tags allow-write + * @category File System + */ + export function utime( + path: string | URL, + atime: number | Date, + mtime: number | Date, + ): Promise; + + /** The event yielded from an {@linkcode HttpConn} which represents an HTTP + * request from a remote client. + * + * @category HTTP Server + * @deprecated Use {@linkcode Deno.serve} instead. This will be removed in + * Deno 2.0. + */ + export interface RequestEvent { + /** The request from the client in the form of the web platform + * {@linkcode Request}. */ + readonly request: Request; + /** The method to be used to respond to the event. The response needs to + * either be an instance of {@linkcode Response} or a promise that resolves + * with an instance of `Response`. + * + * When the response is successfully processed then the promise returned + * will be resolved. If there are any issues with sending the response, + * the promise will be rejected. */ + respondWith(r: Response | PromiseLike): Promise; + } + + /** The async iterable that is returned from {@linkcode Deno.serveHttp} which + * yields up {@linkcode RequestEvent} events, representing individual + * requests on the HTTP server connection. + * + * @category HTTP Server + * @deprecated Use {@linkcode Deno.serve} instead. This will be removed in + * Deno 2.0. + */ + export interface HttpConn extends AsyncIterable, Disposable { + /** The resource ID associated with this connection. Generally users do not + * need to be aware of this identifier. */ + readonly rid: number; + + /** An alternative to the async iterable interface which provides promises + * which resolve with either a {@linkcode RequestEvent} when there is + * another request or `null` when the client has closed the connection. */ + nextRequest(): Promise; + /** Initiate a server side closure of the connection, indicating to the + * client that you refuse to accept any more requests on this connection. + * + * Typically the client closes the connection, which will result in the + * async iterable terminating or the `nextRequest()` method returning + * `null`. */ + close(): void; + } + + /** + * Provides an interface to handle HTTP request and responses over TCP or TLS + * connections. The method returns an {@linkcode HttpConn} which yields up + * {@linkcode RequestEvent} events, which utilize the web platform standard + * {@linkcode Request} and {@linkcode Response} objects to handle the request. + * + * ```ts + * const conn = Deno.listen({ port: 80 }); + * const httpConn = Deno.serveHttp(await conn.accept()); + * const e = await httpConn.nextRequest(); + * if (e) { + * e.respondWith(new Response("Hello World")); + * } + * ``` + * + * Alternatively, you can also use the async iterator approach: + * + * ```ts + * async function handleHttp(conn: Deno.Conn) { + * for await (const e of Deno.serveHttp(conn)) { + * e.respondWith(new Response("Hello World")); + * } + * } + * + * for await (const conn of Deno.listen({ port: 80 })) { + * handleHttp(conn); + * } + * ``` + * + * If `httpConn.nextRequest()` encounters an error or returns `null` then the + * underlying {@linkcode HttpConn} resource is closed automatically. + * + * Also see the experimental Flash HTTP server {@linkcode Deno.serve} which + * provides a ground up rewrite of handling of HTTP requests and responses + * within the Deno CLI. + * + * Note that this function *consumes* the given connection passed to it, thus + * the original connection will be unusable after calling this. Additionally, + * you need to ensure that the connection is not being used elsewhere when + * calling this function in order for the connection to be consumed properly. + * + * For instance, if there is a `Promise` that is waiting for read operation on + * the connection to complete, it is considered that the connection is being + * used elsewhere. In such a case, this function will fail. + * + * @category HTTP Server + * @deprecated Use {@linkcode Deno.serve} instead. This will be removed in + * Deno 2.0. + */ + export function serveHttp(conn: Conn): HttpConn; + + /** The object that is returned from a {@linkcode Deno.upgradeWebSocket} + * request. + * + * @category Web Sockets */ + export interface WebSocketUpgrade { + /** The response object that represents the HTTP response to the client, + * which should be used to the {@linkcode RequestEvent} `.respondWith()` for + * the upgrade to be successful. */ + response: Response; + /** The {@linkcode WebSocket} interface to communicate to the client via a + * web socket. */ + socket: WebSocket; + } + + /** Options which can be set when performing a + * {@linkcode Deno.upgradeWebSocket} upgrade of a {@linkcode Request} + * + * @category Web Sockets */ + export interface UpgradeWebSocketOptions { + /** Sets the `.protocol` property on the client side web socket to the + * value provided here, which should be one of the strings specified in the + * `protocols` parameter when requesting the web socket. This is intended + * for clients and servers to specify sub-protocols to use to communicate to + * each other. */ + protocol?: string; + /** If the client does not respond to this frame with a + * `pong` within the timeout specified, the connection is deemed + * unhealthy and is closed. The `close` and `error` event will be emitted. + * + * The default is 120 seconds. Set to `0` to disable timeouts. */ + idleTimeout?: number; + } + + /** + * Upgrade an incoming HTTP request to a WebSocket. + * + * Given a {@linkcode Request}, returns a pair of {@linkcode WebSocket} and + * {@linkcode Response} instances. The original request must be responded to + * with the returned response for the websocket upgrade to be successful. + * + * ```ts + * const conn = Deno.listen({ port: 80 }); + * const httpConn = Deno.serveHttp(await conn.accept()); + * const e = await httpConn.nextRequest(); + * if (e) { + * const { socket, response } = Deno.upgradeWebSocket(e.request); + * socket.onopen = () => { + * socket.send("Hello World!"); + * }; + * socket.onmessage = (e) => { + * console.log(e.data); + * socket.close(); + * }; + * socket.onclose = () => console.log("WebSocket has been closed."); + * socket.onerror = (e) => console.error("WebSocket error:", e); + * e.respondWith(response); + * } + * ``` + * + * If the request body is disturbed (read from) before the upgrade is + * completed, upgrading fails. + * + * This operation does not yet consume the request or open the websocket. This + * only happens once the returned response has been passed to `respondWith()`. + * + * @category Web Sockets + */ + export function upgradeWebSocket( + request: Request, + options?: UpgradeWebSocketOptions, + ): WebSocketUpgrade; + + /** Send a signal to process under given `pid`. The value and meaning of the + * `signal` to the process is operating system and process dependant. + * {@linkcode Signal} provides the most common signals. Default signal + * is `"SIGTERM"`. + * + * The term `kill` is adopted from the UNIX-like command line command `kill` + * which also signals processes. + * + * If `pid` is negative, the signal will be sent to the process group + * identified by `pid`. An error will be thrown if a negative `pid` is used on + * Windows. + * + * ```ts + * const p = Deno.run({ + * cmd: ["sleep", "10000"] + * }); + * + * Deno.kill(p.pid, "SIGINT"); + * ``` + * + * Requires `allow-run` permission. + * + * @tags allow-run + * @category Sub Process + */ + export function kill(pid: number, signo?: Signal): void; + + /** The type of the resource record to resolve via DNS using + * {@linkcode Deno.resolveDns}. + * + * Only the listed types are supported currently. + * + * @category Network + */ + export type RecordType = + | "A" + | "AAAA" + | "ANAME" + | "CAA" + | "CNAME" + | "MX" + | "NAPTR" + | "NS" + | "PTR" + | "SOA" + | "SRV" + | "TXT"; + + /** + * Options which can be set when using {@linkcode Deno.resolveDns}. + * + * @category Network */ + export interface ResolveDnsOptions { + /** The name server to be used for lookups. + * + * If not specified, defaults to the system configuration. For example + * `/etc/resolv.conf` on Unix-like systems. */ + nameServer?: { + /** The IP address of the name server. */ + ipAddr: string; + /** The port number the query will be sent to. + * + * @default {53} */ + port?: number; + }; + /** + * An abort signal to allow cancellation of the DNS resolution operation. + * If the signal becomes aborted the resolveDns operation will be stopped + * and the promise returned will be rejected with an AbortError. + */ + signal?: AbortSignal; + } + + /** If {@linkcode Deno.resolveDns} is called with `"CAA"` record type + * specified, it will resolve with an array of objects with this interface. + * + * @category Network + */ + export interface CAARecord { + /** If `true`, indicates that the corresponding property tag **must** be + * understood if the semantics of the CAA record are to be correctly + * interpreted by an issuer. + * + * Issuers **must not** issue certificates for a domain if the relevant CAA + * Resource Record set contains unknown property tags that have `critical` + * set. */ + critical: boolean; + /** An string that represents the identifier of the property represented by + * the record. */ + tag: string; + /** The value associated with the tag. */ + value: string; + } + + /** If {@linkcode Deno.resolveDns} is called with `"MX"` record type + * specified, it will return an array of objects with this interface. + * + * @category Network */ + export interface MXRecord { + /** A priority value, which is a relative value compared to the other + * preferences of MX records for the domain. */ + preference: number; + /** The server that mail should be delivered to. */ + exchange: string; + } + + /** If {@linkcode Deno.resolveDns} is called with `"NAPTR"` record type + * specified, it will return an array of objects with this interface. + * + * @category Network */ + export interface NAPTRRecord { + order: number; + preference: number; + flags: string; + services: string; + regexp: string; + replacement: string; + } + + /** If {@linkcode Deno.resolveDns} is called with `"SOA"` record type + * specified, it will return an array of objects with this interface. + * + * @category Network */ + export interface SOARecord { + mname: string; + rname: string; + serial: number; + refresh: number; + retry: number; + expire: number; + minimum: number; + } + + /** If {@linkcode Deno.resolveDns} is called with `"SRV"` record type + * specified, it will return an array of objects with this interface. + * + * @category Network + */ + export interface SRVRecord { + priority: number; + weight: number; + port: number; + target: string; + } + + /** + * Performs DNS resolution against the given query, returning resolved + * records. + * + * Fails in the cases such as: + * + * - the query is in invalid format. + * - the options have an invalid parameter. For example `nameServer.port` is + * beyond the range of 16-bit unsigned integer. + * - the request timed out. + * + * ```ts + * const a = await Deno.resolveDns("example.com", "A"); + * + * const aaaa = await Deno.resolveDns("example.com", "AAAA", { + * nameServer: { ipAddr: "8.8.8.8", port: 53 }, + * }); + * ``` + * + * Requires `allow-net` permission. + * + * @tags allow-net + * @category Network + */ + export function resolveDns( + query: string, + recordType: "A" | "AAAA" | "ANAME" | "CNAME" | "NS" | "PTR", + options?: ResolveDnsOptions, + ): Promise; + + /** + * Performs DNS resolution against the given query, returning resolved + * records. + * + * Fails in the cases such as: + * + * - the query is in invalid format. + * - the options have an invalid parameter. For example `nameServer.port` is + * beyond the range of 16-bit unsigned integer. + * - the request timed out. + * + * ```ts + * const a = await Deno.resolveDns("example.com", "A"); + * + * const aaaa = await Deno.resolveDns("example.com", "AAAA", { + * nameServer: { ipAddr: "8.8.8.8", port: 53 }, + * }); + * ``` + * + * Requires `allow-net` permission. + * + * @tags allow-net + * @category Network + */ + export function resolveDns( + query: string, + recordType: "CAA", + options?: ResolveDnsOptions, + ): Promise; + + /** + * Performs DNS resolution against the given query, returning resolved + * records. + * + * Fails in the cases such as: + * + * - the query is in invalid format. + * - the options have an invalid parameter. For example `nameServer.port` is + * beyond the range of 16-bit unsigned integer. + * - the request timed out. + * + * ```ts + * const a = await Deno.resolveDns("example.com", "A"); + * + * const aaaa = await Deno.resolveDns("example.com", "AAAA", { + * nameServer: { ipAddr: "8.8.8.8", port: 53 }, + * }); + * ``` + * + * Requires `allow-net` permission. + * + * @tags allow-net + * @category Network + */ + export function resolveDns( + query: string, + recordType: "MX", + options?: ResolveDnsOptions, + ): Promise; + + /** + * Performs DNS resolution against the given query, returning resolved + * records. + * + * Fails in the cases such as: + * + * - the query is in invalid format. + * - the options have an invalid parameter. For example `nameServer.port` is + * beyond the range of 16-bit unsigned integer. + * - the request timed out. + * + * ```ts + * const a = await Deno.resolveDns("example.com", "A"); + * + * const aaaa = await Deno.resolveDns("example.com", "AAAA", { + * nameServer: { ipAddr: "8.8.8.8", port: 53 }, + * }); + * ``` + * + * Requires `allow-net` permission. + * + * @tags allow-net + * @category Network + */ + export function resolveDns( + query: string, + recordType: "NAPTR", + options?: ResolveDnsOptions, + ): Promise; + + /** + * Performs DNS resolution against the given query, returning resolved + * records. + * + * Fails in the cases such as: + * + * - the query is in invalid format. + * - the options have an invalid parameter. For example `nameServer.port` is + * beyond the range of 16-bit unsigned integer. + * - the request timed out. + * + * ```ts + * const a = await Deno.resolveDns("example.com", "A"); + * + * const aaaa = await Deno.resolveDns("example.com", "AAAA", { + * nameServer: { ipAddr: "8.8.8.8", port: 53 }, + * }); + * ``` + * + * Requires `allow-net` permission. + * + * @tags allow-net + * @category Network + */ + export function resolveDns( + query: string, + recordType: "SOA", + options?: ResolveDnsOptions, + ): Promise; + + /** + * Performs DNS resolution against the given query, returning resolved + * records. + * + * Fails in the cases such as: + * + * - the query is in invalid format. + * - the options have an invalid parameter. For example `nameServer.port` is + * beyond the range of 16-bit unsigned integer. + * - the request timed out. + * + * ```ts + * const a = await Deno.resolveDns("example.com", "A"); + * + * const aaaa = await Deno.resolveDns("example.com", "AAAA", { + * nameServer: { ipAddr: "8.8.8.8", port: 53 }, + * }); + * ``` + * + * Requires `allow-net` permission. + * + * @tags allow-net + * @category Network + */ + export function resolveDns( + query: string, + recordType: "SRV", + options?: ResolveDnsOptions, + ): Promise; + + /** + * Performs DNS resolution against the given query, returning resolved + * records. + * + * Fails in the cases such as: + * + * - the query is in invalid format. + * - the options have an invalid parameter. For example `nameServer.port` is + * beyond the range of 16-bit unsigned integer. + * - the request timed out. + * + * ```ts + * const a = await Deno.resolveDns("example.com", "A"); + * + * const aaaa = await Deno.resolveDns("example.com", "AAAA", { + * nameServer: { ipAddr: "8.8.8.8", port: 53 }, + * }); + * ``` + * + * Requires `allow-net` permission. + * + * @tags allow-net + * @category Network + */ + export function resolveDns( + query: string, + recordType: "TXT", + options?: ResolveDnsOptions, + ): Promise; + + /** + * Performs DNS resolution against the given query, returning resolved + * records. + * + * Fails in the cases such as: + * + * - the query is in invalid format. + * - the options have an invalid parameter. For example `nameServer.port` is + * beyond the range of 16-bit unsigned integer. + * - the request timed out. + * + * ```ts + * const a = await Deno.resolveDns("example.com", "A"); + * + * const aaaa = await Deno.resolveDns("example.com", "AAAA", { + * nameServer: { ipAddr: "8.8.8.8", port: 53 }, + * }); + * ``` + * + * Requires `allow-net` permission. + * + * @tags allow-net + * @category Network + */ + export function resolveDns( + query: string, + recordType: RecordType, + options?: ResolveDnsOptions, + ): Promise< + | string[] + | CAARecord[] + | MXRecord[] + | NAPTRRecord[] + | SOARecord[] + | SRVRecord[] + | string[][] + >; + + /** + * Make the timer of the given `id` block the event loop from finishing. + * + * @category Timers + */ + export function refTimer(id: number): void; + + /** + * Make the timer of the given `id` not block the event loop from finishing. + * + * @category Timers + */ + export function unrefTimer(id: number): void; + + /** + * Returns the user id of the process on POSIX platforms. Returns null on Windows. + * + * ```ts + * console.log(Deno.uid()); + * ``` + * + * Requires `allow-sys` permission. + * + * @tags allow-sys + * @category Runtime Environment + */ + export function uid(): number | null; + + /** + * Returns the group id of the process on POSIX platforms. Returns null on windows. + * + * ```ts + * console.log(Deno.gid()); + * ``` + * + * Requires `allow-sys` permission. + * + * @tags allow-sys + * @category Runtime Environment + */ + export function gid(): number | null; + + /** Information for a HTTP request. + * + * @category HTTP Server + */ + export interface ServeHandlerInfo { + /** The remote address of the connection. */ + remoteAddr: Deno.NetAddr; + } + + /** A handler for HTTP requests. Consumes a request and returns a response. + * + * If a handler throws, the server calling the handler will assume the impact + * of the error is isolated to the individual request. It will catch the error + * and if necessary will close the underlying connection. + * + * @category HTTP Server + */ + export type ServeHandler = ( + request: Request, + info: ServeHandlerInfo, + ) => Response | Promise; + + /** Options which can be set when calling {@linkcode Deno.serve}. + * + * @category HTTP Server + */ + export interface ServeOptions { + /** The port to listen on. + * + * @default {8000} */ + port?: number; + + /** A literal IP address or host name that can be resolved to an IP address. + * + * __Note about `0.0.0.0`__ While listening `0.0.0.0` works on all platforms, + * the browsers on Windows don't work with the address `0.0.0.0`. + * You should show the message like `server running on localhost:8080` instead of + * `server running on 0.0.0.0:8080` if your program supports Windows. + * + * @default {"0.0.0.0"} */ + hostname?: string; + + /** An {@linkcode AbortSignal} to close the server and all connections. */ + signal?: AbortSignal; + + /** Sets `SO_REUSEPORT` on POSIX systems. */ + reusePort?: boolean; + + /** The handler to invoke when route handlers throw an error. */ + onError?: (error: unknown) => Response | Promise; + + /** The callback which is called when the server starts listening. */ + onListen?: (params: { hostname: string; port: number }) => void; + } + + /** Additional options which are used when opening a TLS (HTTPS) server. + * + * @category HTTP Server + */ + export interface ServeTlsOptions extends ServeOptions { + /** Server private key in PEM format */ + cert: string; + + /** Cert chain in PEM format */ + key: string; + } + + /** + * @category HTTP Server + */ + export interface ServeInit { + /** The handler to invoke to process each incoming request. */ + handler: ServeHandler; + } + + export interface ServeUnixOptions { + /** The unix domain socket path to listen on. */ + path: string; + + /** An {@linkcode AbortSignal} to close the server and all connections. */ + signal?: AbortSignal; + + /** The handler to invoke when route handlers throw an error. */ + onError?: (error: unknown) => Response | Promise; + + /** The callback which is called when the server starts listening. */ + onListen?: (params: { path: string }) => void; + } + + /** Information for a unix domain socket HTTP request. + * + * @category HTTP Server + */ + export interface ServeUnixHandlerInfo { + /** The remote address of the connection. */ + remoteAddr: Deno.UnixAddr; + } + + /** A handler for unix domain socket HTTP requests. Consumes a request and returns a response. + * + * If a handler throws, the server calling the handler will assume the impact + * of the error is isolated to the individual request. It will catch the error + * and if necessary will close the underlying connection. + * + * @category HTTP Server + */ + export type ServeUnixHandler = ( + request: Request, + info: ServeUnixHandlerInfo, + ) => Response | Promise; + + /** + * @category HTTP Server + */ + export interface ServeUnixInit { + /** The handler to invoke to process each incoming request. */ + handler: ServeUnixHandler; + } + + /** An instance of the server created using `Deno.serve()` API. + * + * @category HTTP Server + */ + export interface HttpServer extends AsyncDisposable { + /** A promise that resolves once server finishes - eg. when aborted using + * the signal passed to {@linkcode ServeOptions.signal}. + */ + finished: Promise; + + /** + * Make the server block the event loop from finishing. + * + * Note: the server blocks the event loop from finishing by default. + * This method is only meaningful after `.unref()` is called. + */ + ref(): void; + + /** Make the server not block the event loop from finishing. */ + unref(): void; + + /** Gracefully close the server. No more new connections will be accepted, + * while pending requests will be allowed to finish. + */ + shutdown(): Promise; + } + + /** + * @category HTTP Server + * + * @deprecated Use {@linkcode Deno.HttpServer} instead. + * {@linkcode Deno.Server} will be removed in Deno 2.0. + */ + export type Server = HttpServer; + + /** Serves HTTP requests with the given handler. + * + * The below example serves with the port `8000` on hostname `"127.0.0.1"`. + * + * ```ts + * Deno.serve((_req) => new Response("Hello, world")); + * ``` + * + * @category HTTP Server + */ + export function serve(handler: ServeHandler): HttpServer; + /** Serves HTTP requests with the given option bag and handler. + * + * You can specify the socket path with `path` option. + * + * ```ts + * Deno.serve( + * { path: "path/to/socket" }, + * (_req) => new Response("Hello, world") + * ); + * ``` + * + * You can stop the server with an {@linkcode AbortSignal}. The abort signal + * needs to be passed as the `signal` option in the options bag. The server + * aborts when the abort signal is aborted. To wait for the server to close, + * await the promise returned from the `Deno.serve` API. + * + * ```ts + * const ac = new AbortController(); + * + * const server = Deno.serve( + * { signal: ac.signal, path: "path/to/socket" }, + * (_req) => new Response("Hello, world") + * ); + * server.finished.then(() => console.log("Server closed")); + * + * console.log("Closing server..."); + * ac.abort(); + * ``` + * + * By default `Deno.serve` prints the message + * `Listening on path/to/socket` on listening. If you like to + * change this behavior, you can specify a custom `onListen` callback. + * + * ```ts + * Deno.serve({ + * onListen({ path }) { + * console.log(`Server started at ${path}`); + * // ... more info specific to your server .. + * }, + * path: "path/to/socket", + * }, (_req) => new Response("Hello, world")); + * ``` + * + * @category HTTP Server + */ + export function serve( + options: ServeUnixOptions, + handler: ServeUnixHandler, + ): HttpServer; + /** Serves HTTP requests with the given option bag and handler. + * + * You can specify an object with a port and hostname option, which is the + * address to listen on. The default is port `8000` on hostname `"127.0.0.1"`. + * + * You can change the address to listen on using the `hostname` and `port` + * options. The below example serves on port `3000` and hostname `"0.0.0.0"`. + * + * ```ts + * Deno.serve( + * { port: 3000, hostname: "0.0.0.0" }, + * (_req) => new Response("Hello, world") + * ); + * ``` + * + * You can stop the server with an {@linkcode AbortSignal}. The abort signal + * needs to be passed as the `signal` option in the options bag. The server + * aborts when the abort signal is aborted. To wait for the server to close, + * await the promise returned from the `Deno.serve` API. + * + * ```ts + * const ac = new AbortController(); + * + * const server = Deno.serve( + * { signal: ac.signal }, + * (_req) => new Response("Hello, world") + * ); + * server.finished.then(() => console.log("Server closed")); + * + * console.log("Closing server..."); + * ac.abort(); + * ``` + * + * By default `Deno.serve` prints the message + * `Listening on http://:/` on listening. If you like to + * change this behavior, you can specify a custom `onListen` callback. + * + * ```ts + * Deno.serve({ + * onListen({ port, hostname }) { + * console.log(`Server started at http://${hostname}:${port}`); + * // ... more info specific to your server .. + * }, + * }, (_req) => new Response("Hello, world")); + * ``` + * + * To enable TLS you must specify the `key` and `cert` options. + * + * ```ts + * const cert = "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----\n"; + * const key = "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n"; + * Deno.serve({ cert, key }, (_req) => new Response("Hello, world")); + * ``` + * + * @category HTTP Server + */ + export function serve( + options: ServeOptions | ServeTlsOptions, + handler: ServeHandler, + ): HttpServer; + /** Serves HTTP requests with the given option bag. + * + * You can specify an object with the path option, which is the + * unix domain socket to listen on. + * + * ```ts + * const ac = new AbortController(); + * + * const server = Deno.serve({ + * path: "path/to/socket", + * handler: (_req) => new Response("Hello, world"), + * signal: ac.signal, + * onListen({ path }) { + * console.log(`Server started at ${path}`); + * }, + * }); + * server.finished.then(() => console.log("Server closed")); + * + * console.log("Closing server..."); + * ac.abort(); + * ``` + * + * @category HTTP Server + */ + export function serve( + options: ServeUnixInit & ServeUnixOptions, + ): HttpServer; + /** Serves HTTP requests with the given option bag. + * + * You can specify an object with a port and hostname option, which is the + * address to listen on. The default is port `8000` on hostname `"127.0.0.1"`. + * + * ```ts + * const ac = new AbortController(); + * + * const server = Deno.serve({ + * port: 3000, + * hostname: "0.0.0.0", + * handler: (_req) => new Response("Hello, world"), + * signal: ac.signal, + * onListen({ port, hostname }) { + * console.log(`Server started at http://${hostname}:${port}`); + * }, + * }); + * server.finished.then(() => console.log("Server closed")); + * + * console.log("Closing server..."); + * ac.abort(); + * ``` + * + * @category HTTP Server + */ + export function serve( + options: ServeInit & (ServeOptions | ServeTlsOptions), + ): HttpServer; +} + +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +// deno-lint-ignore-file no-explicit-any + +/// +/// + +/** @category Console and Debugging */ +declare interface Console { + assert(condition?: boolean, ...data: any[]): void; + clear(): void; + count(label?: string): void; + countReset(label?: string): void; + debug(...data: any[]): void; + dir(item?: any, options?: any): void; + dirxml(...data: any[]): void; + error(...data: any[]): void; + group(...data: any[]): void; + groupCollapsed(...data: any[]): void; + groupEnd(): void; + info(...data: any[]): void; + log(...data: any[]): void; + table(tabularData?: any, properties?: string[]): void; + time(label?: string): void; + timeEnd(label?: string): void; + timeLog(label?: string, ...data: any[]): void; + trace(...data: any[]): void; + warn(...data: any[]): void; + + /** This method is a noop, unless used in inspector */ + timeStamp(label?: string): void; + + /** This method is a noop, unless used in inspector */ + profile(label?: string): void; + + /** This method is a noop, unless used in inspector */ + profileEnd(label?: string): void; +} + +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +// deno-lint-ignore-file no-explicit-any no-var + +/// +/// + +/** @category Web APIs */ +declare interface URLSearchParams { + /** Appends a specified key/value pair as a new search parameter. + * + * ```ts + * let searchParams = new URLSearchParams(); + * searchParams.append('name', 'first'); + * searchParams.append('name', 'second'); + * ``` + */ + append(name: string, value: string): void; + + /** Deletes search parameters that match a name, and optional value, + * from the list of all search parameters. + * + * ```ts + * let searchParams = new URLSearchParams([['name', 'value']]); + * searchParams.delete('name'); + * searchParams.delete('name', 'value'); + * ``` + */ + delete(name: string, value?: string): void; + + /** Returns all the values associated with a given search parameter + * as an array. + * + * ```ts + * searchParams.getAll('name'); + * ``` + */ + getAll(name: string): string[]; + + /** Returns the first value associated to the given search parameter. + * + * ```ts + * searchParams.get('name'); + * ``` + */ + get(name: string): string | null; + + /** Returns a boolean value indicating if a given parameter, + * or parameter and value pair, exists. + * + * ```ts + * searchParams.has('name'); + * searchParams.has('name', 'value'); + * ``` + */ + has(name: string, value?: string): boolean; + + /** Sets the value associated with a given search parameter to the + * given value. If there were several matching values, this method + * deletes the others. If the search parameter doesn't exist, this + * method creates it. + * + * ```ts + * searchParams.set('name', 'value'); + * ``` + */ + set(name: string, value: string): void; + + /** Sort all key/value pairs contained in this object in place and + * return undefined. The sort order is according to Unicode code + * points of the keys. + * + * ```ts + * searchParams.sort(); + * ``` + */ + sort(): void; + + /** Calls a function for each element contained in this object in + * place and return undefined. Optionally accepts an object to use + * as this when executing callback as second argument. + * + * ```ts + * const params = new URLSearchParams([["a", "b"], ["c", "d"]]); + * params.forEach((value, key, parent) => { + * console.log(value, key, parent); + * }); + * ``` + */ + forEach( + callbackfn: (value: string, key: string, parent: this) => void, + thisArg?: any, + ): void; + + /** Returns an iterator allowing to go through all keys contained + * in this object. + * + * ```ts + * const params = new URLSearchParams([["a", "b"], ["c", "d"]]); + * for (const key of params.keys()) { + * console.log(key); + * } + * ``` + */ + keys(): IterableIterator; + + /** Returns an iterator allowing to go through all values contained + * in this object. + * + * ```ts + * const params = new URLSearchParams([["a", "b"], ["c", "d"]]); + * for (const value of params.values()) { + * console.log(value); + * } + * ``` + */ + values(): IterableIterator; + + /** Returns an iterator allowing to go through all key/value + * pairs contained in this object. + * + * ```ts + * const params = new URLSearchParams([["a", "b"], ["c", "d"]]); + * for (const [key, value] of params.entries()) { + * console.log(key, value); + * } + * ``` + */ + entries(): IterableIterator<[string, string]>; + + /** Returns an iterator allowing to go through all key/value + * pairs contained in this object. + * + * ```ts + * const params = new URLSearchParams([["a", "b"], ["c", "d"]]); + * for (const [key, value] of params) { + * console.log(key, value); + * } + * ``` + */ + [Symbol.iterator](): IterableIterator<[string, string]>; + + /** Returns a query string suitable for use in a URL. + * + * ```ts + * searchParams.toString(); + * ``` + */ + toString(): string; + + /** Contains the number of search parameters + * + * ```ts + * searchParams.size + * ``` + */ + size: number; +} + +/** @category Web APIs */ +declare var URLSearchParams: { + readonly prototype: URLSearchParams; + new ( + init?: Iterable | Record | string, + ): URLSearchParams; +}; + +/** The URL interface represents an object providing static methods used for + * creating object URLs. + * + * @category Web APIs + */ +declare interface URL { + hash: string; + host: string; + hostname: string; + href: string; + toString(): string; + readonly origin: string; + password: string; + pathname: string; + port: string; + protocol: string; + search: string; + readonly searchParams: URLSearchParams; + username: string; + toJSON(): string; +} + +/** The URL interface represents an object providing static methods used for + * creating object URLs. + * + * @category Web APIs + */ +declare var URL: { + readonly prototype: URL; + new (url: string | URL, base?: string | URL): URL; + canParse(url: string | URL, base?: string | URL): boolean; + createObjectURL(blob: Blob): string; + revokeObjectURL(url: string): void; +}; + +/** @category Web APIs */ +declare interface URLPatternInit { + protocol?: string; + username?: string; + password?: string; + hostname?: string; + port?: string; + pathname?: string; + search?: string; + hash?: string; + baseURL?: string; +} + +/** @category Web APIs */ +declare type URLPatternInput = string | URLPatternInit; + +/** @category Web APIs */ +declare interface URLPatternComponentResult { + input: string; + groups: Record; +} + +/** `URLPatternResult` is the object returned from `URLPattern.exec`. + * + * @category Web APIs + */ +declare interface URLPatternResult { + /** The inputs provided when matching. */ + inputs: [URLPatternInit] | [URLPatternInit, string]; + + /** The matched result for the `protocol` matcher. */ + protocol: URLPatternComponentResult; + /** The matched result for the `username` matcher. */ + username: URLPatternComponentResult; + /** The matched result for the `password` matcher. */ + password: URLPatternComponentResult; + /** The matched result for the `hostname` matcher. */ + hostname: URLPatternComponentResult; + /** The matched result for the `port` matcher. */ + port: URLPatternComponentResult; + /** The matched result for the `pathname` matcher. */ + pathname: URLPatternComponentResult; + /** The matched result for the `search` matcher. */ + search: URLPatternComponentResult; + /** The matched result for the `hash` matcher. */ + hash: URLPatternComponentResult; +} + +/** + * The URLPattern API provides a web platform primitive for matching URLs based + * on a convenient pattern syntax. + * + * The syntax is based on path-to-regexp. Wildcards, named capture groups, + * regular groups, and group modifiers are all supported. + * + * ```ts + * // Specify the pattern as structured data. + * const pattern = new URLPattern({ pathname: "/users/:user" }); + * const match = pattern.exec("https://blog.example.com/users/joe"); + * console.log(match.pathname.groups.user); // joe + * ``` + * + * ```ts + * // Specify a fully qualified string pattern. + * const pattern = new URLPattern("https://example.com/books/:id"); + * console.log(pattern.test("https://example.com/books/123")); // true + * console.log(pattern.test("https://deno.land/books/123")); // false + * ``` + * + * ```ts + * // Specify a relative string pattern with a base URL. + * const pattern = new URLPattern("/article/:id", "https://blog.example.com"); + * console.log(pattern.test("https://blog.example.com/article")); // false + * console.log(pattern.test("https://blog.example.com/article/123")); // true + * ``` + * + * @category Web APIs + */ +declare interface URLPattern { + /** + * Test if the given input matches the stored pattern. + * + * The input can either be provided as an absolute URL string with an optional base, + * relative URL string with a required base, or as individual components + * in the form of an `URLPatternInit` object. + * + * ```ts + * const pattern = new URLPattern("https://example.com/books/:id"); + * + * // Test an absolute url string. + * console.log(pattern.test("https://example.com/books/123")); // true + * + * // Test a relative url with a base. + * console.log(pattern.test("/books/123", "https://example.com")); // true + * + * // Test an object of url components. + * console.log(pattern.test({ pathname: "/books/123" })); // true + * ``` + */ + test(input: URLPatternInput, baseURL?: string): boolean; + + /** + * Match the given input against the stored pattern. + * + * The input can either be provided as an absolute URL string with an optional base, + * relative URL string with a required base, or as individual components + * in the form of an `URLPatternInit` object. + * + * ```ts + * const pattern = new URLPattern("https://example.com/books/:id"); + * + * // Match an absolute url string. + * let match = pattern.exec("https://example.com/books/123"); + * console.log(match.pathname.groups.id); // 123 + * + * // Match a relative url with a base. + * match = pattern.exec("/books/123", "https://example.com"); + * console.log(match.pathname.groups.id); // 123 + * + * // Match an object of url components. + * match = pattern.exec({ pathname: "/books/123" }); + * console.log(match.pathname.groups.id); // 123 + * ``` + */ + exec(input: URLPatternInput, baseURL?: string): URLPatternResult | null; + + /** The pattern string for the `protocol`. */ + readonly protocol: string; + /** The pattern string for the `username`. */ + readonly username: string; + /** The pattern string for the `password`. */ + readonly password: string; + /** The pattern string for the `hostname`. */ + readonly hostname: string; + /** The pattern string for the `port`. */ + readonly port: string; + /** The pattern string for the `pathname`. */ + readonly pathname: string; + /** The pattern string for the `search`. */ + readonly search: string; + /** The pattern string for the `hash`. */ + readonly hash: string; +} + +/** + * The URLPattern API provides a web platform primitive for matching URLs based + * on a convenient pattern syntax. + * + * The syntax is based on path-to-regexp. Wildcards, named capture groups, + * regular groups, and group modifiers are all supported. + * + * ```ts + * // Specify the pattern as structured data. + * const pattern = new URLPattern({ pathname: "/users/:user" }); + * const match = pattern.exec("https://blog.example.com/users/joe"); + * console.log(match.pathname.groups.user); // joe + * ``` + * + * ```ts + * // Specify a fully qualified string pattern. + * const pattern = new URLPattern("https://example.com/books/:id"); + * console.log(pattern.test("https://example.com/books/123")); // true + * console.log(pattern.test("https://deno.land/books/123")); // false + * ``` + * + * ```ts + * // Specify a relative string pattern with a base URL. + * const pattern = new URLPattern("/article/:id", "https://blog.example.com"); + * console.log(pattern.test("https://blog.example.com/article")); // false + * console.log(pattern.test("https://blog.example.com/article/123")); // true + * ``` + * + * @category Web APIs + */ +declare var URLPattern: { + readonly prototype: URLPattern; + new (input: URLPatternInput, baseURL?: string): URLPattern; +}; + +// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. + +// deno-lint-ignore-file no-explicit-any no-var + +/// +/// + +/** @category Web APIs */ +declare interface DOMException extends Error { + readonly name: string; + readonly message: string; + readonly code: number; + readonly INDEX_SIZE_ERR: 1; + readonly DOMSTRING_SIZE_ERR: 2; + readonly HIERARCHY_REQUEST_ERR: 3; + readonly WRONG_DOCUMENT_ERR: 4; + readonly INVALID_CHARACTER_ERR: 5; + readonly NO_DATA_ALLOWED_ERR: 6; + readonly NO_MODIFICATION_ALLOWED_ERR: 7; + readonly NOT_FOUND_ERR: 8; + readonly NOT_SUPPORTED_ERR: 9; + readonly INUSE_ATTRIBUTE_ERR: 10; + readonly INVALID_STATE_ERR: 11; + readonly SYNTAX_ERR: 12; + readonly INVALID_MODIFICATION_ERR: 13; + readonly NAMESPACE_ERR: 14; + readonly INVALID_ACCESS_ERR: 15; + readonly VALIDATION_ERR: 16; + readonly TYPE_MISMATCH_ERR: 17; + readonly SECURITY_ERR: 18; + readonly NETWORK_ERR: 19; + readonly ABORT_ERR: 20; + readonly URL_MISMATCH_ERR: 21; + readonly QUOTA_EXCEEDED_ERR: 22; + readonly TIMEOUT_ERR: 23; + readonly INVALID_NODE_TYPE_ERR: 24; + readonly DATA_CLONE_ERR: 25; +} + +/** @category Web APIs */ +declare var DOMException: { + readonly prototype: DOMException; + new (message?: string, name?: string): DOMException; + readonly INDEX_SIZE_ERR: 1; + readonly DOMSTRING_SIZE_ERR: 2; + readonly HIERARCHY_REQUEST_ERR: 3; + readonly WRONG_DOCUMENT_ERR: 4; + readonly INVALID_CHARACTER_ERR: 5; + readonly NO_DATA_ALLOWED_ERR: 6; + readonly NO_MODIFICATION_ALLOWED_ERR: 7; + readonly NOT_FOUND_ERR: 8; + readonly NOT_SUPPORTED_ERR: 9; + readonly INUSE_ATTRIBUTE_ERR: 10; + readonly INVALID_STATE_ERR: 11; + readonly SYNTAX_ERR: 12; + readonly INVALID_MODIFICATION_ERR: 13; + readonly NAMESPACE_ERR: 14; + readonly INVALID_ACCESS_ERR: 15; + readonly VALIDATION_ERR: 16; + readonly TYPE_MISMATCH_ERR: 17; + readonly SECURITY_ERR: 18; + readonly NETWORK_ERR: 19; + readonly ABORT_ERR: 20; + readonly URL_MISMATCH_ERR: 21; + readonly QUOTA_EXCEEDED_ERR: 22; + readonly TIMEOUT_ERR: 23; + readonly INVALID_NODE_TYPE_ERR: 24; + readonly DATA_CLONE_ERR: 25; +}; + +/** @category DOM Events */ +declare interface EventInit { + bubbles?: boolean; + cancelable?: boolean; + composed?: boolean; +} + +/** An event which takes place in the DOM. + * + * @category DOM Events + */ +declare interface Event { + /** Returns true or false depending on how event was initialized. True if + * event goes through its target's ancestors in reverse tree order, and + * false otherwise. */ + readonly bubbles: boolean; + cancelBubble: boolean; + /** Returns true or false depending on how event was initialized. Its return + * value does not always carry meaning, but true can indicate that part of the + * operation during which event was dispatched, can be canceled by invoking + * the preventDefault() method. */ + readonly cancelable: boolean; + /** Returns true or false depending on how event was initialized. True if + * event invokes listeners past a ShadowRoot node that is the root of its + * target, and false otherwise. */ + readonly composed: boolean; + /** Returns the object whose event listener's callback is currently being + * invoked. */ + readonly currentTarget: EventTarget | null; + /** Returns true if preventDefault() was invoked successfully to indicate + * cancellation, and false otherwise. */ + readonly defaultPrevented: boolean; + /** Returns the event's phase, which is one of NONE, CAPTURING_PHASE, + * AT_TARGET, and BUBBLING_PHASE. */ + readonly eventPhase: number; + /** Returns true if event was dispatched by the user agent, and false + * otherwise. */ + readonly isTrusted: boolean; + /** Returns the object to which event is dispatched (its target). */ + readonly target: EventTarget | null; + /** Returns the event's timestamp as the number of milliseconds measured + * relative to the time origin. */ + readonly timeStamp: number; + /** Returns the type of event, e.g. "click", "hashchange", or "submit". */ + readonly type: string; + /** Returns the invocation target objects of event's path (objects on which + * listeners will be invoked), except for any nodes in shadow trees of which + * the shadow root's mode is "closed" that are not reachable from event's + * currentTarget. */ + composedPath(): EventTarget[]; + /** If invoked when the cancelable attribute value is true, and while + * executing a listener for the event with passive set to false, signals to + * the operation that caused event to be dispatched that it needs to be + * canceled. */ + preventDefault(): void; + /** Invoking this method prevents event from reaching any registered event + * listeners after the current one finishes running and, when dispatched in a + * tree, also prevents event from reaching any other objects. */ + stopImmediatePropagation(): void; + /** When dispatched in a tree, invoking this method prevents event from + * reaching any objects other than the current object. */ + stopPropagation(): void; + readonly AT_TARGET: number; + readonly BUBBLING_PHASE: number; + readonly CAPTURING_PHASE: number; + readonly NONE: number; +} + +/** An event which takes place in the DOM. + * + * @category DOM Events + */ +declare var Event: { + readonly prototype: Event; + new (type: string, eventInitDict?: EventInit): Event; + readonly AT_TARGET: number; + readonly BUBBLING_PHASE: number; + readonly CAPTURING_PHASE: number; + readonly NONE: number; +}; + +/** + * EventTarget is a DOM interface implemented by objects that can receive events + * and may have listeners for them. + * + * @category DOM Events + */ +declare interface EventTarget { + /** Appends an event listener for events whose type attribute value is type. + * The callback argument sets the callback that will be invoked when the event + * is dispatched. + * + * The options argument sets listener-specific options. For compatibility this + * can be a boolean, in which case the method behaves exactly as if the value + * was specified as options's capture. + * + * When set to true, options's capture prevents callback from being invoked + * when the event's eventPhase attribute value is BUBBLING_PHASE. When false + * (or not present), callback will not be invoked when event's eventPhase + * attribute value is CAPTURING_PHASE. Either way, callback will be invoked if + * event's eventPhase attribute value is AT_TARGET. + * + * When set to true, options's passive indicates that the callback will not + * cancel the event by invoking preventDefault(). This is used to enable + * performance optimizations described in § 2.8 Observing event listeners. + * + * When set to true, options's once indicates that the callback will only be + * invoked once after which the event listener will be removed. + * + * The event listener is appended to target's event listener list and is not + * appended if it has the same type, callback, and capture. */ + addEventListener( + type: string, + listener: EventListenerOrEventListenerObject | null, + options?: boolean | AddEventListenerOptions, + ): void; + /** Dispatches a synthetic event to event target and returns true if either + * event's cancelable attribute value is false or its preventDefault() method + * was not invoked, and false otherwise. */ + dispatchEvent(event: Event): boolean; + /** Removes the event listener in target's event listener list with the same + * type, callback, and options. */ + removeEventListener( + type: string, + callback: EventListenerOrEventListenerObject | null, + options?: EventListenerOptions | boolean, + ): void; +} + +/** + * EventTarget is a DOM interface implemented by objects that can receive events + * and may have listeners for them. + * + * @category DOM Events + */ +declare var EventTarget: { + readonly prototype: EventTarget; + new (): EventTarget; +}; + +/** @category DOM Events */ +declare interface EventListener { + (evt: Event): void | Promise; +} + +/** @category DOM Events */ +declare interface EventListenerObject { + handleEvent(evt: Event): void | Promise; +} + +/** @category DOM Events */ +declare type EventListenerOrEventListenerObject = + | EventListener + | EventListenerObject; + +/** @category DOM Events */ +declare interface AddEventListenerOptions extends EventListenerOptions { + once?: boolean; + passive?: boolean; + signal?: AbortSignal; +} + +/** @category DOM Events */ +declare interface EventListenerOptions { + capture?: boolean; +} + +/** @category DOM Events */ +declare interface ProgressEventInit extends EventInit { + lengthComputable?: boolean; + loaded?: number; + total?: number; +} + +/** Events measuring progress of an underlying process, like an HTTP request + * (for an XMLHttpRequest, or the loading of the underlying resource of an + * ,