Compare commits

..

1 Commits

Author SHA1 Message Date
Julien Goux
9da7121816 feat: caching 2026-04-10 11:53:47 +02:00
15 changed files with 168741 additions and 218 deletions

View File

@@ -20,14 +20,14 @@ jobs:
steps:
# Metadata drives the non-major gating used for approval and auto-merge.
- id: meta
uses: dependabot/fetch-metadata@25dd0e34f4fe68f24cc83900b1fe3fe149efef98 # v3.1.0
uses: dependabot/fetch-metadata@ffa630c65fa7e0ecfa0625b5ceda64399aea1b36 # v3.0.0
with:
github-token: "${{ secrets.GITHUB_TOKEN }}"
- name: Generate token
id: app-token
if: ${{ steps.meta.outputs.update-type == null || steps.meta.outputs.update-type == 'version-update:semver-patch' || steps.meta.outputs.update-type == 'version-update:semver-minor' }}
uses: actions/create-github-app-token@1b10c78c7865c340bc4f6099eb2f838309f1e8c3 # v3.1.1
uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.GH_APP_PRIVATE_KEY }}

View File

@@ -1,6 +1,6 @@
# This workflow refreshes and checks dependency license records used in this
# action with the help of the Licensed tool. If any licenses are invalid or
# missing, this workflow will fail. See: https://github.com/licensee/licensed
# This workflow checks the statuses of cached dependencies used in this action
# with the help of the Licensed tool. If any licenses are invalid or missing,
# this workflow will fail. See: https://github.com/licensee/licensed
name: Licensed
@@ -71,7 +71,7 @@ jobs:
- name: Setup Ruby
id: setup-ruby
if: steps.license-inputs.outputs.changed == 'true'
uses: ruby/setup-ruby@7372622e62b60b3cb750dcd2b9e32c247ffec26a # v1.302.0
uses: ruby/setup-ruby@e65c17d16e57e481586a6a5a0282698790062f92 # v1.300.0
with:
ruby-version: ruby
@@ -81,11 +81,6 @@ jobs:
version: 4.x
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Refresh License Cache
id: refresh-license-cache
if: steps.license-inputs.outputs.changed == 'true'
run: licensed cache
- name: Check Licenses
id: check-licenses
if: steps.license-inputs.outputs.changed == 'true'
@@ -116,7 +111,7 @@ jobs:
- name: Setup Ruby
id: setup-ruby
uses: ruby/setup-ruby@7372622e62b60b3cb750dcd2b9e32c247ffec26a # v1.302.0
uses: ruby/setup-ruby@e65c17d16e57e481586a6a5a0282698790062f92 # v1.300.0
with:
ruby-version: ruby

View File

@@ -40,6 +40,22 @@ steps:
version: 2.84.2
```
Cache Docker images used by `supabase start` across workflow runs:
```yaml
steps:
- uses: actions/checkout@v6
- uses: supabase/setup-cli@v2
with:
version: 2.84.2
cache: true
- run: supabase start
```
The first run still pulls images from the registry. Later runs can restore the
same image set from the GitHub Actions cache before `supabase start` runs, and
the action saves newly pulled Supabase images at the end of a successful job.
Run `supabase db start` to execute all migrations on a fresh database:
```yaml
@@ -58,9 +74,18 @@ on Windows and macOS runners.
The action supports the following inputs:
| Name | Type | Description | Default | Required |
| --------- | ------ | ---------------------------------- | --------------------------------- | -------- |
| `version` | String | Supabase CLI version (or `latest`) | Root lockfile version or `latest` | false |
| Name | Type | Description | Default | Required |
| ----------- | ------- | ------------------------------------------------------ | --------------------------------- | -------- |
| `version` | String | Supabase CLI version (or `latest`) | Root lockfile version or `latest` | false |
| `cache` | Boolean | Cache Docker images used by Supabase local development | `false` | false |
| `cache-key` | String | Explicit cache key for Supabase Docker images | Generated from runner and config | false |
The action exposes these outputs:
| Name | Description |
| ----------- | ------------------------------------------------------ |
| `version` | Version of installed Supabase CLI |
| `cache-hit` | Whether an exact Supabase Docker image cache was found |
## Advanced Usage
@@ -101,6 +126,8 @@ Export local Supabase env vars for app tests:
```yaml
steps:
- uses: supabase/setup-cli@v2
with:
cache: true
- run: supabase init
- run: supabase start
- name: Export local Supabase env vars
@@ -113,6 +140,22 @@ steps:
- run: bun test
```
Customize the Docker image cache key when the image set depends on your workflow
flags, generated config, or monorepo layout:
```yaml
steps:
- uses: actions/checkout@v6
- uses: supabase/setup-cli@v2
with:
cache: true
cache-key: supabase-${{ runner.os }}-${{ runner.arch }}-${{ hashFiles('supabase/config.toml') }}-start-all
- run: supabase start
```
Avoid running `docker system prune -a` before the job ends if you want the
post-action cache save to include images pulled by `supabase start`.
## Develop
After you've cloned the repository to your local machine or codespace, you'll
@@ -136,6 +179,12 @@ need to perform a few setup steps before you can work on the action.
bun test
```
1. :package: Build the bundled action entrypoints
```bash
bun run build
```
1. :mag: Run the full local CI suite
```bash

View File

@@ -5,27 +5,20 @@ inputs:
version:
description: Version of Supabase CLI to install. If omitted, detect from the root lockfile and otherwise use latest.
required: false
cache:
description: Cache Docker images used by Supabase local development commands.
required: false
default: "false"
cache-key:
description: Optional explicit cache key for Supabase Docker images.
required: false
outputs:
version:
description: Version of installed Supabase CLI
value: ${{ steps.setup-cli.outputs.version }}
cache-hit:
description: Whether an exact Supabase Docker image cache was restored.
runs:
using: composite
steps:
- name: Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0
with:
bun-version-file: ${{ github.action_path }}/.bun-version
- name: Install Action Dependencies
shell: bash
working-directory: ${{ github.action_path }}
run: bun install --frozen-lockfile --production
- id: setup-cli
name: Setup Supabase CLI
shell: bash
working-directory: ${{ github.action_path }}
env:
INPUT_VERSION: ${{ inputs.version }}
run: bun src/main.ts
using: node24
main: dist/main.js
post: dist/post.js
post-if: success()

216
bun.lock
View File

@@ -5,158 +5,240 @@
"": {
"name": "setup-cli",
"dependencies": {
"@actions/core": "^3.0.1",
"@actions/cache": "^6.0.0",
"@actions/core": "^3.0.0",
"@actions/tool-cache": "^4.0.0",
"js-yaml": "^4.1.1",
"semver": "^7.7.4",
},
"devDependencies": {
"@tsconfig/bun": "^1.0.10",
"@types/bun": "^1.3.13",
"@typescript/native-preview": "^7.0.0-dev.20260410.1",
"oxfmt": "^0.46.0",
"oxlint": "^1.61.0",
"oxlint-tsgolint": "^0.21.1",
"@types/bun": "^1.3.11",
"@types/js-yaml": "^4.0.9",
"@types/node": "^24",
"@types/semver": "^7.7.1",
"@typescript/native-preview": "^7.0.0-dev.20260409.1",
"oxfmt": "^0.44.0",
"oxlint": "^1.59.0",
"oxlint-tsgolint": "^0.20.0",
},
},
},
"packages": {
"@actions/core": ["@actions/core@3.0.1", "", { "dependencies": { "@actions/exec": "^3.0.0", "@actions/http-client": "^4.0.0" } }, "sha512-a6d/Nwahm9fliVGRhdhofo40HjHQasUPusmc7vBfyky+7Z+P2A1J68zyFVaNcEclc/Se+eO595oAr5nwEIoIUA=="],
"@actions/cache": ["@actions/cache@6.0.0", "", { "dependencies": { "@actions/core": "^3.0.0", "@actions/exec": "^3.0.0", "@actions/glob": "^0.6.1", "@actions/http-client": "^4.0.0", "@actions/io": "^3.0.0", "@azure/core-rest-pipeline": "^1.22.0", "@azure/storage-blob": "^12.30.0", "@protobuf-ts/runtime-rpc": "^2.11.1", "semver": "^7.7.3" } }, "sha512-+tCs634SyGBQJ3KU1rtAVabmN/gYiT9WgzTSJzWwdPCLmM3zWrdbysaErKv8HyI6OozClrxNvDgPjJimbHZZvw=="],
"@actions/core": ["@actions/core@3.0.0", "", { "dependencies": { "@actions/exec": "^3.0.0", "@actions/http-client": "^4.0.0" } }, "sha512-zYt6cz+ivnTmiT/ksRVriMBOiuoUpDCJJlZ5KPl2/FRdvwU3f7MPh9qftvbkXJThragzUZieit2nyHUyw53Seg=="],
"@actions/exec": ["@actions/exec@3.0.0", "", { "dependencies": { "@actions/io": "^3.0.2" } }, "sha512-6xH/puSoNBXb72VPlZVm7vQ+svQpFyA96qdDBvhB8eNZOE8LtPf9L4oAsfzK/crCL8YZ+19fKYVnM63Sl+Xzlw=="],
"@actions/glob": ["@actions/glob@0.6.1", "", { "dependencies": { "@actions/core": "^3.0.0", "minimatch": "^3.0.4" } }, "sha512-K4+2Ac5ILcf2ySdJCha+Pop9NcKjxqCL4xL4zI50dgB2PbXgC0+AcP011xfH4Of6b4QEJJg8dyZYv7zl4byTsw=="],
"@actions/http-client": ["@actions/http-client@4.0.0", "", { "dependencies": { "tunnel": "^0.0.6", "undici": "^6.23.0" } }, "sha512-QuwPsgVMsD6qaPD57GLZi9sqzAZCtiJT8kVBCDpLtxhL5MydQ4gS+DrejtZZPdIYyB1e95uCK9Luyds7ybHI3g=="],
"@actions/io": ["@actions/io@3.0.2", "", {}, "sha512-nRBchcMM+QK1pdjO7/idu86rbJI5YHUKCvKs0KxnSYbVe3F51UfGxuZX4Qy/fWlp6l7gWFwIkrOzN+oUK03kfw=="],
"@actions/tool-cache": ["@actions/tool-cache@4.0.0", "", { "dependencies": { "@actions/core": "^3.0.0", "@actions/exec": "^3.0.0", "@actions/http-client": "^4.0.0", "@actions/io": "^3.0.0", "semver": "^7.7.3" } }, "sha512-L8P9HbXvpvqjZDveb/fdsa55IVC0trfPgQ4ZwGo6r5af6YDVdM9vMGPZ7rgY2fAT9gGj4PSYd6bYlg3p3jD78A=="],
"@oxfmt/binding-android-arm-eabi": ["@oxfmt/binding-android-arm-eabi@0.46.0", "", { "os": "android", "cpu": "arm" }, "sha512-b1doV4WRcJU+BESSlCvCjV+5CEr/T6h0frArAdV26Nir+gGNFNaylvDiiMPfF1pxeV0txZEs38ojzJaxBYg+ng=="],
"@azure/abort-controller": ["@azure/abort-controller@2.1.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA=="],
"@oxfmt/binding-android-arm64": ["@oxfmt/binding-android-arm64@0.46.0", "", { "os": "android", "cpu": "arm64" }, "sha512-v6+HhjsoV3GO0u2u9jLSAZrvWfTraDxKofUIQ7/ktS7tzS+epVsxdHmeM+XxuNcAY/nWxxU1Sg4JcGTNRXraBA=="],
"@azure/core-auth": ["@azure/core-auth@1.10.1", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-util": "^1.13.0", "tslib": "^2.6.2" } }, "sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg=="],
"@oxfmt/binding-darwin-arm64": ["@oxfmt/binding-darwin-arm64@0.46.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-3eeooJGrqGIlI5MyryDZsAcKXSmKIgAD4yYtfRrRJzXZ0UTFZtiSveIur56YPrGMYZwT4XyVhHsMqrNwr1XeFA=="],
"@azure/core-client": ["@azure/core-client@1.10.1", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.10.0", "@azure/core-rest-pipeline": "^1.22.0", "@azure/core-tracing": "^1.3.0", "@azure/core-util": "^1.13.0", "@azure/logger": "^1.3.0", "tslib": "^2.6.2" } }, "sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w=="],
"@oxfmt/binding-darwin-x64": ["@oxfmt/binding-darwin-x64@0.46.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-QG8BDM0CXWbu84k2SKmCqfEddPQPFiBicwtYnLqHRWZZl57HbtOLRMac/KTq2NO4AEc4ICCBpFxJIV9zcqYfkQ=="],
"@azure/core-http-compat": ["@azure/core-http-compat@2.4.0", "", { "dependencies": { "@azure/abort-controller": "^2.1.2" }, "peerDependencies": { "@azure/core-client": "^1.10.0", "@azure/core-rest-pipeline": "^1.22.0" } }, "sha512-f1P96IB399YiN2ARYHP7EpZi3Bf3wH4SN2lGzrw7JVwm7bbsVYtf2iKSBwTywD2P62NOPZGHFSZi+6jjb75JuA=="],
"@oxfmt/binding-freebsd-x64": ["@oxfmt/binding-freebsd-x64@0.46.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-9DdCqS/n2ncu/Chazvt3cpgAjAmIGQDz7hFKSrNItMApyV/Ja9mz3hD4JakIE3nS8PW9smEbPWnb389QLBY4nw=="],
"@azure/core-lro": ["@azure/core-lro@2.7.2", "", { "dependencies": { "@azure/abort-controller": "^2.0.0", "@azure/core-util": "^1.2.0", "@azure/logger": "^1.0.0", "tslib": "^2.6.2" } }, "sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw=="],
"@oxfmt/binding-linux-arm-gnueabihf": ["@oxfmt/binding-linux-arm-gnueabihf@0.46.0", "", { "os": "linux", "cpu": "arm" }, "sha512-Dgs7VeE2jT0LHMhw6tPEt0xQYe54kBqHEovmWsv4FVQlegCOvlIJNx0S8n4vj8WUtpT+Z6BD2HhKJPLglLxvZg=="],
"@azure/core-paging": ["@azure/core-paging@1.6.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA=="],
"@oxfmt/binding-linux-arm-musleabihf": ["@oxfmt/binding-linux-arm-musleabihf@0.46.0", "", { "os": "linux", "cpu": "arm" }, "sha512-Zxn3adhTH13JKnU4xXJj8FeEfF680XjXh3gSShKl57HCMBRde2tUJTgogV/1MSHA80PJEVrDa7r66TLVq3Ia7Q=="],
"@azure/core-rest-pipeline": ["@azure/core-rest-pipeline@1.23.0", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.10.0", "@azure/core-tracing": "^1.3.0", "@azure/core-util": "^1.13.0", "@azure/logger": "^1.3.0", "@typespec/ts-http-runtime": "^0.3.4", "tslib": "^2.6.2" } }, "sha512-Evs1INHo+jUjwHi1T6SG6Ua/LHOQBCLuKEEE6efIpt4ZOoNonaT1kP32GoOcdNDbfqsD2445CPri3MubBy5DEQ=="],
"@oxfmt/binding-linux-arm64-gnu": ["@oxfmt/binding-linux-arm64-gnu@0.46.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-+TWipjrgVM8D7aIdDD0tlr3teLTTvQTn7QTE5BpT10H1Fj82gfdn9X6nn2sDgx/MepuSCfSnzFNJq2paLL0OiA=="],
"@azure/core-tracing": ["@azure/core-tracing@1.3.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ=="],
"@oxfmt/binding-linux-arm64-musl": ["@oxfmt/binding-linux-arm64-musl@0.46.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-aAUPBWJ1lGwwnxZUEDLJ94+Iy6MuwJwPxUgO4sCA5mEEyDk7b+cDQ+JpX1VR150Zoyd+D49gsrUzpUK5h587Eg=="],
"@azure/core-util": ["@azure/core-util@1.13.1", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" } }, "sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A=="],
"@oxfmt/binding-linux-ppc64-gnu": ["@oxfmt/binding-linux-ppc64-gnu@0.46.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-ufBCJukyFX/UDrokP/r6BGDoTInnsDs7bxyzKAgMiZlt2Qu8GPJSJ6Zm6whIiJzKk0naxA8ilwmbO1LMw6Htxw=="],
"@azure/core-xml": ["@azure/core-xml@1.5.1", "", { "dependencies": { "fast-xml-parser": "^5.5.9", "tslib": "^2.8.1" } }, "sha512-xcNRHqCoSp4AunOALEae6A8f3qATb83gSrm31Iqb01OzblvC3/W/bfXozcq78EzIdzZzuH1bZ2NvRR0TdX709w=="],
"@oxfmt/binding-linux-riscv64-gnu": ["@oxfmt/binding-linux-riscv64-gnu@0.46.0", "", { "os": "linux", "cpu": "none" }, "sha512-eqtlC2YmPqjun76R1gVfGLuKWx7NuEnLEAudZ7n6ipSKbCZTqIKSs1b5Y8K/JHZsRpLkeSmAAjig5HOIg8fQzQ=="],
"@azure/logger": ["@azure/logger@1.3.0", "", { "dependencies": { "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" } }, "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA=="],
"@oxfmt/binding-linux-riscv64-musl": ["@oxfmt/binding-linux-riscv64-musl@0.46.0", "", { "os": "linux", "cpu": "none" }, "sha512-yccVOO2nMXkQLGgy0He3EQEwKD7NF0zEk+/OWmroznkqXyJdN6bfK0LtNnr6/14Bh3FjpYq7bP33l/VloCnxpA=="],
"@azure/storage-blob": ["@azure/storage-blob@12.31.0", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.9.0", "@azure/core-client": "^1.9.3", "@azure/core-http-compat": "^2.2.0", "@azure/core-lro": "^2.2.0", "@azure/core-paging": "^1.6.2", "@azure/core-rest-pipeline": "^1.19.1", "@azure/core-tracing": "^1.2.0", "@azure/core-util": "^1.11.0", "@azure/core-xml": "^1.4.5", "@azure/logger": "^1.1.4", "@azure/storage-common": "^12.3.0", "events": "^3.0.0", "tslib": "^2.8.1" } }, "sha512-DBgNv10aCSxopt92DkTDD0o9xScXeBqPKGmR50FPZQaEcH4JLQ+GEOGEDv19V5BMkB7kxr+m4h6il/cCDPvmHg=="],
"@oxfmt/binding-linux-s390x-gnu": ["@oxfmt/binding-linux-s390x-gnu@0.46.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-aAf7fG23OQCey6VRPj9IeCraoYtpgtx0ZyJ1CXkPyT1wjzBE7c3xtuxHe/AdHaJfVVb/SXpSk8Gl1LzyQupSqw=="],
"@azure/storage-common": ["@azure/storage-common@12.3.0", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.9.0", "@azure/core-http-compat": "^2.2.0", "@azure/core-rest-pipeline": "^1.19.1", "@azure/core-tracing": "^1.2.0", "@azure/core-util": "^1.11.0", "@azure/logger": "^1.1.4", "events": "^3.3.0", "tslib": "^2.8.1" } }, "sha512-/OFHhy86aG5Pe8dP5tsp+BuJ25JOAl9yaMU3WZbkeoiFMHFtJ7tu5ili7qEdBXNW9G5lDB19trwyI6V49F/8iQ=="],
"@oxfmt/binding-linux-x64-gnu": ["@oxfmt/binding-linux-x64-gnu@0.46.0", "", { "os": "linux", "cpu": "x64" }, "sha512-q0JPsTMyJNjYrBvYFDz4WbVsafNZaPCZv4RnFypRotLqpKROtBZcEaXQW4eb9YmvLU3NckVemLJnzkSZSdmOxw=="],
"@oxfmt/binding-android-arm-eabi": ["@oxfmt/binding-android-arm-eabi@0.44.0", "", { "os": "android", "cpu": "arm" }, "sha512-5UvghMd9SA/yvKTWCAxMAPXS1d2i054UeOf4iFjZjfayTwCINcC3oaSXjtbZfCaEpxgJod7XiOjTtby5yEv/BQ=="],
"@oxfmt/binding-linux-x64-musl": ["@oxfmt/binding-linux-x64-musl@0.46.0", "", { "os": "linux", "cpu": "x64" }, "sha512-7LsLY9Cw57GPkhSR+duI3mt9baRczK/DtHYSldQ4BEU92da9igBQNl4z7Vq5U9NNPsh1FmpKvv1q9WDtiUQR1A=="],
"@oxfmt/binding-android-arm64": ["@oxfmt/binding-android-arm64@0.44.0", "", { "os": "android", "cpu": "arm64" }, "sha512-IVudM1BWfvrYO++Khtzr8q9n5Rxu7msUvoFMqzGJVdX7HfUXUDHwaH2zHZNB58svx2J56pmCUzophyaPFkcG/A=="],
"@oxfmt/binding-openharmony-arm64": ["@oxfmt/binding-openharmony-arm64@0.46.0", "", { "os": "none", "cpu": "arm64" }, "sha512-lHiBOz8Duaku7JtRNLlps3j++eOaICPZSd8FCVmTDM4DFOPT71Bjn7g6iar1z7StXlKRweUKxWUs4sA+zWGDXg=="],
"@oxfmt/binding-darwin-arm64": ["@oxfmt/binding-darwin-arm64@0.44.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-eWCLAIKAHfx88EqEP1Ga2yz7qVcqDU5lemn4xck+07bH182hDdprOHjbogyk0In1Djys3T0/pO2JepFnRJ41Mg=="],
"@oxfmt/binding-win32-arm64-msvc": ["@oxfmt/binding-win32-arm64-msvc@0.46.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-/5ktYUliP89RhgC37DBH1x20U5zPSZMy3cMEcO0j3793rbHP9MWsknBwQB6eozRzWmYrh0IFM/p20EbPvDlYlg=="],
"@oxfmt/binding-darwin-x64": ["@oxfmt/binding-darwin-x64@0.44.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-eHTBznHLM49++dwz07MblQ2cOXyIgeedmE3Wgy4ptUESj38/qYZyRi1MPwC9olQJWssMeY6WI3UZ7YmU5ggvyQ=="],
"@oxfmt/binding-win32-ia32-msvc": ["@oxfmt/binding-win32-ia32-msvc@0.46.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-3WTnoiuIr8XvV0DIY7SN+1uJSwKf4sPpcbHfobcRT9JutGcLaef/miyBB87jxd3aqH+mS0+G5lsgHuXLUwjjpQ=="],
"@oxfmt/binding-freebsd-x64": ["@oxfmt/binding-freebsd-x64@0.44.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-jLMmbj0u0Ft43QpkUVr/0v1ZfQCGWAvU+WznEHcN3wZC/q6ox7XeSJtk9P36CCpiDSUf3sGnzbIuG1KdEMEDJQ=="],
"@oxfmt/binding-win32-x64-msvc": ["@oxfmt/binding-win32-x64-msvc@0.46.0", "", { "os": "win32", "cpu": "x64" }, "sha512-IXxiQpkYnOwNfP23vzwSfhdpxJzyiPTY7eTn6dn3DsriKddESzM8i6kfq9R7CD/PUJwCvQT22NgtygBeug3KoA=="],
"@oxfmt/binding-linux-arm-gnueabihf": ["@oxfmt/binding-linux-arm-gnueabihf@0.44.0", "", { "os": "linux", "cpu": "arm" }, "sha512-n+A/u/ByK1qV8FVGOwyaSpw5NPNl0qlZfgTBqHeGIqr8Qzq1tyWZ4lAaxPoe5mZqE3w88vn3+jZtMxriHPE7tg=="],
"@oxlint-tsgolint/darwin-arm64": ["@oxlint-tsgolint/darwin-arm64@0.21.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-7TLjyWe4wG9saJc992VWmaHq2hwKfOEEVTjheReXJXaDhavMZI4X9a6nKhbEng4IVkYtzjD2jw16vw2WFXLYLw=="],
"@oxfmt/binding-linux-arm-musleabihf": ["@oxfmt/binding-linux-arm-musleabihf@0.44.0", "", { "os": "linux", "cpu": "arm" }, "sha512-5eax+FkxyCqAi3Rw0mrZFr7+KTt/XweFsbALR+B5ljWBLBl8nHe4ADrUnb1gLEfQCJLl+Ca5FIVD4xEt95AwIw=="],
"@oxlint-tsgolint/darwin-x64": ["@oxlint-tsgolint/darwin-x64@0.21.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-7wf9Wf75nTzA7zpL9myhFe2RKvfuqGUOADNvUooCjEWvh7hmPz3lSEqTMh5Z/VQhzsG04mM9ACyghxhRzq7zFw=="],
"@oxfmt/binding-linux-arm64-gnu": ["@oxfmt/binding-linux-arm64-gnu@0.44.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-58l8JaHxSGOmOMOG2CIrNsnkRJAj0YcHQCmvNACniOa/vd1iRHhlPajczegzS5jwMENlqgreyiTR9iNlke8qCw=="],
"@oxlint-tsgolint/linux-arm64": ["@oxlint-tsgolint/linux-arm64@0.21.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-IPuQN/Vd0Rjklg/cCGBbQyUuRBp2f6LQXpZYwk5ivOR6V/+CgiYsv8pn/PVY7gjeyoNvPQrXB7xMjHUO2YZbdw=="],
"@oxfmt/binding-linux-arm64-musl": ["@oxfmt/binding-linux-arm64-musl@0.44.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-AlObQIXyVRZ96LbtVljtFq0JqH5B92NU+BQeDFrXWBUWlCKAM0wF5GLfIhCLT5kQ3Sl+U0YjRJ7Alqj5hGQaCg=="],
"@oxlint-tsgolint/linux-x64": ["@oxlint-tsgolint/linux-x64@0.21.1", "", { "os": "linux", "cpu": "x64" }, "sha512-d1niGuTbh2qiv7dR7tqkbOcM5cIR63of0lMBFdEQavL1KrJV8zuRdwdi68K7MNGdgoR+J5A9ajpGGvsHwp1bPg=="],
"@oxfmt/binding-linux-ppc64-gnu": ["@oxfmt/binding-linux-ppc64-gnu@0.44.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-YcFE8/q/BbrCiIiM5piwbkA6GwJc5QqhMQp2yDrqQ2fuVkZ7CInb1aIijZ/k8EXc72qXMSwKpVlBv1w/MsGO/A=="],
"@oxlint-tsgolint/win32-arm64": ["@oxlint-tsgolint/win32-arm64@0.21.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-ICu9y2JLnFPvFqstnWPPNqBM8LK8BWw2OTeaR0UgEMm4hOSbrZAKv1/hwZYyiLqnCNjBL87AGSQIgTHCYlsipw=="],
"@oxfmt/binding-linux-riscv64-gnu": ["@oxfmt/binding-linux-riscv64-gnu@0.44.0", "", { "os": "linux", "cpu": "none" }, "sha512-eOdzs6RqkRzuqNHUX5C8ISN5xfGh4xDww8OEd9YAmc3OWN8oAe5bmlIqQ+rrHLpv58/0BuU48bxkhnIGjA/ATQ=="],
"@oxlint-tsgolint/win32-x64": ["@oxlint-tsgolint/win32-x64@0.21.1", "", { "os": "win32", "cpu": "x64" }, "sha512-cTEFCFjCj6iXfrSHcvajSPNqhEA4TxSzU3gFxbdGSAUTNXGToU99IbdhWAPSbhcucoym0XE4Zl7E41NiSkNTug=="],
"@oxfmt/binding-linux-riscv64-musl": ["@oxfmt/binding-linux-riscv64-musl@0.44.0", "", { "os": "linux", "cpu": "none" }, "sha512-YBgNTxntD/QvlFUfgvh8bEdwOhXiquX8gaofZJAwYa/Xp1S1DQrFVZEeck7GFktr24DztsSp8N8WtWCBwxs0Hw=="],
"@oxlint/binding-android-arm-eabi": ["@oxlint/binding-android-arm-eabi@1.61.0", "", { "os": "android", "cpu": "arm" }, "sha512-6eZBPgiigK5txqoVgRqxbaxiom4lM8AP8CyKPPvpzKnQ3iFRFOIDc+0AapF+qsUSwjOzr5SGk4SxQDpQhkSJMQ=="],
"@oxfmt/binding-linux-s390x-gnu": ["@oxfmt/binding-linux-s390x-gnu@0.44.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-GLIh1R6WHWshl/i4QQDNgj0WtT25aRO4HNUWEoitxiywyRdhTFmFEYT2rXlcl9U6/26vhmOqG5cRlMLG3ocaIA=="],
"@oxlint/binding-android-arm64": ["@oxlint/binding-android-arm64@1.61.0", "", { "os": "android", "cpu": "arm64" }, "sha512-CkwLR69MUnyv5wjzebvbbtTSUwqLxM35CXE79bHqDIK+NtKmPEUpStTcLQRZMCo4MP0qRT6TXIQVpK0ZVScnMA=="],
"@oxfmt/binding-linux-x64-gnu": ["@oxfmt/binding-linux-x64-gnu@0.44.0", "", { "os": "linux", "cpu": "x64" }, "sha512-gZOpgTlOsLcLfAF9qgpTr7FIIFSKnQN3hDf/0JvQ4CIwMY7h+eilNjxq/CorqvYcEOu+LRt1W4ZS7KccEHLOdA=="],
"@oxlint/binding-darwin-arm64": ["@oxlint/binding-darwin-arm64@1.61.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-8JbefTkbmvqkqWjmQrHke+MdpgT2UghhD/ktM4FOQSpGeCgbMToJEKdl9zwhr/YWTl92i4QI1KiTwVExpcUN8A=="],
"@oxfmt/binding-linux-x64-musl": ["@oxfmt/binding-linux-x64-musl@0.44.0", "", { "os": "linux", "cpu": "x64" }, "sha512-1CyS9JTB+pCUFYFI6pkQGGZaT/AY5gnhHVrQQLhFba6idP9AzVYm1xbdWfywoldTYvjxQJV6x4SuduCIfP3W+A=="],
"@oxlint/binding-darwin-x64": ["@oxlint/binding-darwin-x64@1.61.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-uWpoxDT47hTnDLcdEh5jVbso8rlTTu5o0zuqa9J8E0JAKmIWn7kGFEIB03Pycn2hd2vKxybPGLhjURy/9We5FQ=="],
"@oxfmt/binding-openharmony-arm64": ["@oxfmt/binding-openharmony-arm64@0.44.0", "", { "os": "none", "cpu": "arm64" }, "sha512-bmEv70Ak6jLr1xotCbF5TxIKjsmQaiX+jFRtnGtfA03tJPf6VG3cKh96S21boAt3JZc+Vjx8PYcDuLj39vM2Pw=="],
"@oxlint/binding-freebsd-x64": ["@oxlint/binding-freebsd-x64@1.61.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-K/o4hEyW7flfMel0iBVznmMBt7VIMHGdjADocHKpK1DUF9erpWnJ+BSSWd2W0c8K3mPtpph+CuHzRU6CI3l9jQ=="],
"@oxfmt/binding-win32-arm64-msvc": ["@oxfmt/binding-win32-arm64-msvc@0.44.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-yWzB+oCpSnP/dmw85eFLAT5o35Ve5pkGS2uF/UCISpIwDqf1xa7OpmtomiqY/Vzg8VyvMbuf6vroF2khF/+1Vg=="],
"@oxlint/binding-linux-arm-gnueabihf": ["@oxlint/binding-linux-arm-gnueabihf@1.61.0", "", { "os": "linux", "cpu": "arm" }, "sha512-P6040ZkcyweJ0Po9yEFqJCdvZnf3VNCGs1SIHgXDf8AAQNC6ID/heXQs9iSgo2FH7gKaKq32VWc59XZwL34C5Q=="],
"@oxfmt/binding-win32-ia32-msvc": ["@oxfmt/binding-win32-ia32-msvc@0.44.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-TcWpo18xEIE3AmIG2kpr3kz5IEhQgnx0lazl2+8L+3eTopOAUevQcmlr4nhguImNWz0OMeOZrYZOhJNCf16nlQ=="],
"@oxlint/binding-linux-arm-musleabihf": ["@oxlint/binding-linux-arm-musleabihf@1.61.0", "", { "os": "linux", "cpu": "arm" }, "sha512-bwxrGCzTZkuB+THv2TQ1aTkVEfv5oz8sl+0XZZCpoYzErJD8OhPQOTA0ENPd1zJz8QsVdSzSrS2umKtPq4/JXg=="],
"@oxfmt/binding-win32-x64-msvc": ["@oxfmt/binding-win32-x64-msvc@0.44.0", "", { "os": "win32", "cpu": "x64" }, "sha512-oj8aLkPJZppIM4CMQNsyir9ybM1Xw/CfGPTSsTnzpVGyljgfbdP0EVUlURiGM0BDrmw5psQ6ArmGCcUY/yABaQ=="],
"@oxlint/binding-linux-arm64-gnu": ["@oxlint/binding-linux-arm64-gnu@1.61.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-vkhb9/wKguMkLlrm3FoJW/Xmdv31GgYAE+x8lxxQ+7HeOxXUySI0q36a3NTVIuQUdLzxCI1zzMGsk1o37FOe3w=="],
"@oxlint-tsgolint/darwin-arm64": ["@oxlint-tsgolint/darwin-arm64@0.20.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-KKQcIHZHMxqpHUA1VXIbOG6chNCFkUWbQy6M+AFVtPKkA/3xAeJkJ3njoV66bfzwPHRcWQO+kcj5XqtbkjakoA=="],
"@oxlint/binding-linux-arm64-musl": ["@oxlint/binding-linux-arm64-musl@1.61.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-bl1dQh8LnVqsj6oOQAcxwbuOmNJkwc4p6o//HTBZhNTzJy21TLDwAviMqUFNUxDHkPGpmdKTSN4tWTjLryP8xg=="],
"@oxlint-tsgolint/darwin-x64": ["@oxlint-tsgolint/darwin-x64@0.20.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-7HeVMuclGfG+NLZi2ybY0T4fMI7/XxO/208rJk+zEIloKkVnlh11Wd241JMGwgNFXn+MLJbOqOfojDb2Dt4L1g=="],
"@oxlint/binding-linux-ppc64-gnu": ["@oxlint/binding-linux-ppc64-gnu@1.61.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-QoOX6KB2IiEpyOj/HKqaxi+NQHPnOgNgnr22n9N4ANJCzXkUlj1UmeAbFb4PpqdlHIzvGDM5xZ0OKtcLq9RhiQ=="],
"@oxlint-tsgolint/linux-arm64": ["@oxlint-tsgolint/linux-arm64@0.20.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-zxhUwz+WSxE6oWlZLK2z2ps9yC6ebmgoYmjAl0Oa48+GqkZ56NVgo+wb8DURNv6xrggzHStQxqQxe3mK51HZag=="],
"@oxlint/binding-linux-riscv64-gnu": ["@oxlint/binding-linux-riscv64-gnu@1.61.0", "", { "os": "linux", "cpu": "none" }, "sha512-1TGcTerjY6p152wCof3oKElccq3xHljS/Mucp04gV/4ATpP6nO7YNnp7opEg6SHkv2a57/b4b8Ndm9znJ1/qAw=="],
"@oxlint-tsgolint/linux-x64": ["@oxlint-tsgolint/linux-x64@0.20.0", "", { "os": "linux", "cpu": "x64" }, "sha512-/1l6FnahC9im8PK+Ekkx/V3yetO/PzZnJegE2FXcv/iXEhbeVxP/ouiTYcUQu9shT1FWJCSNti1VJHH+21Y1dg=="],
"@oxlint/binding-linux-riscv64-musl": ["@oxlint/binding-linux-riscv64-musl@1.61.0", "", { "os": "linux", "cpu": "none" }, "sha512-65wXEmZIrX2ADwC8i/qFL4EWLSbeuBpAm3suuX1vu4IQkKd+wLT/HU/BOl84kp91u2SxPkPDyQgu4yrqp8vwVA=="],
"@oxlint-tsgolint/win32-arm64": ["@oxlint-tsgolint/win32-arm64@0.20.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-oPZ5Yz8sVdo7P/5q+i3IKeix31eFZ55JAPa1+RGPoe9PoaYVsdMvR6Jvib6YtrqoJnFPlg3fjEjlEPL8VBKYJA=="],
"@oxlint/binding-linux-s390x-gnu": ["@oxlint/binding-linux-s390x-gnu@1.61.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-TVvhgMvor7Qa6COeXxCJ7ENOM+lcAOGsQ0iUdPSCv2hxb9qSHLQ4XF1h50S6RE1gBOJ0WV3rNukg4JJJP1LWRA=="],
"@oxlint-tsgolint/win32-x64": ["@oxlint-tsgolint/win32-x64@0.20.0", "", { "os": "win32", "cpu": "x64" }, "sha512-4stx8RHj3SP9vQyRF/yZbz5igtPvYMEUR8CUoha4BVNZihi39DpCR8qkU7lpjB5Ga1DRMo2pHaA4bdTOMaY4mw=="],
"@oxlint/binding-linux-x64-gnu": ["@oxlint/binding-linux-x64-gnu@1.61.0", "", { "os": "linux", "cpu": "x64" }, "sha512-SjpS5uYuFoDnDdZPwZE59ndF95AsY47R5MliuneTWR1pDm2CxGJaYXbKULI71t5TVfLQUWmrHEGRL9xvuq6dnA=="],
"@oxlint/binding-android-arm-eabi": ["@oxlint/binding-android-arm-eabi@1.59.0", "", { "os": "android", "cpu": "arm" }, "sha512-etYDw/UaEv936AQUd/CRMBVd+e+XuuU6wC+VzOv1STvsTyZenLChepLWqLtnyTTp4YMlM22ypzogDDwqYxv5cg=="],
"@oxlint/binding-linux-x64-musl": ["@oxlint/binding-linux-x64-musl@1.61.0", "", { "os": "linux", "cpu": "x64" }, "sha512-gGfAeGD4sNJGILZbc/yKcIimO9wQnPMoYp9swAaKeEtwsSQAbU+rsdQze5SBtIP6j0QDzeYd4XSSUCRCF+LIeQ=="],
"@oxlint/binding-android-arm64": ["@oxlint/binding-android-arm64@1.59.0", "", { "os": "android", "cpu": "arm64" }, "sha512-TgLc7XVLKH2a4h8j3vn1MDjfK33i9MY60f/bKhRGWyVzbk5LCZ4X01VZG7iHrMmi5vYbAp8//Ponigx03CLsdw=="],
"@oxlint/binding-openharmony-arm64": ["@oxlint/binding-openharmony-arm64@1.61.0", "", { "os": "none", "cpu": "arm64" }, "sha512-OlVT0LrG/ct33EVtWRyR+B/othwmDWeRxfi13wUdPeb3lAT5TgTcFDcfLfarZtzB4W1nWF/zICMgYdkggX2WmQ=="],
"@oxlint/binding-darwin-arm64": ["@oxlint/binding-darwin-arm64@1.59.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-DXyFPf5ZKldMLloRHx/B9fsxsiTQomaw7cmEW3YIJko2HgCh+GUhp9gGYwHrqlLJPsEe3dYj9JebjX92D3j3AA=="],
"@oxlint/binding-win32-arm64-msvc": ["@oxlint/binding-win32-arm64-msvc@1.61.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-vI//NZPJk6DToiovPtaiwD4iQ7kO1r5ReWQD0sOOyKRtP3E2f6jxin4uvwi3OvDzHA2EFfd7DcZl5dtkQh7g1w=="],
"@oxlint/binding-darwin-x64": ["@oxlint/binding-darwin-x64@1.59.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-LgvrsdgVLX1qWqIEmNsSmMXJhpAWdtUQ0M+oR0CySwi+9IHWyOGuIL8w8+u/kbZNMyZr4WUyYB5i0+D+AKgkLg=="],
"@oxlint/binding-win32-ia32-msvc": ["@oxlint/binding-win32-ia32-msvc@1.61.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-0ySj4/4zd2XjePs3XAQq7IigIstN4LPQZgCyigX5/ERMLjdWAJfnxcTsrtxZxuij8guJW8foXuHmhGxW0H4dDA=="],
"@oxlint/binding-freebsd-x64": ["@oxlint/binding-freebsd-x64@1.59.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-bOJhqX/ny4hrFuTPlyk8foSRx/vLRpxJh0jOOKN2NWW6FScXHPAA5rQbrwdQPcgGB5V8Ua51RS03fke8ssBcug=="],
"@oxlint/binding-win32-x64-msvc": ["@oxlint/binding-win32-x64-msvc@1.61.0", "", { "os": "win32", "cpu": "x64" }, "sha512-0xgSiyeqDLDZxXoe9CVJrOx3TUVsfyoOY7cNi03JbItNcC9WCZqrSNdrAbHONxhSPaVh/lzfnDcON1RqSUMhHw=="],
"@oxlint/binding-linux-arm-gnueabihf": ["@oxlint/binding-linux-arm-gnueabihf@1.59.0", "", { "os": "linux", "cpu": "arm" }, "sha512-vVUXxYMF9trXCsz4m9H6U0IjehosVHxBzVgJUxly1uz4W1PdDyicaBnpC0KRXsHYretLVe+uS9pJy8iM57Kujw=="],
"@oxlint/binding-linux-arm-musleabihf": ["@oxlint/binding-linux-arm-musleabihf@1.59.0", "", { "os": "linux", "cpu": "arm" }, "sha512-TULQW8YBPGRWg5yZpFPL54HLOnJ3/HiX6VenDPi6YfxB/jlItwSMFh3/hCeSNbh+DAMaE1Py0j5MOaivHkI/9Q=="],
"@oxlint/binding-linux-arm64-gnu": ["@oxlint/binding-linux-arm64-gnu@1.59.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-Gt54Y4eqSgYJ90xipm24xeyaPV854706o/kiT8oZvUt3VDY7qqxdqyGqchMaujd87ib+/MXvnl9WkK8Cc1BExg=="],
"@oxlint/binding-linux-arm64-musl": ["@oxlint/binding-linux-arm64-musl@1.59.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-3CtsKp7NFB3OfqQzbuAecrY7GIZeiv7AD+xutU4tefVQzlfmTI7/ygWLrvkzsDEjTlMq41rYHxgsn6Yh8tybmA=="],
"@oxlint/binding-linux-ppc64-gnu": ["@oxlint/binding-linux-ppc64-gnu@1.59.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-K0diOpT3ncDmOfl9I1HuvpEsAuTxkts0VYwIv/w6Xiy9CdwyPBVX88Ga9l8VlGgMrwBMnSY4xIvVlVY/fkQk7Q=="],
"@oxlint/binding-linux-riscv64-gnu": ["@oxlint/binding-linux-riscv64-gnu@1.59.0", "", { "os": "linux", "cpu": "none" }, "sha512-xAU7+QDU6kTJJ7mJLOGgo7oOjtAtkKyFZ0Yjdb5cEo3DiCCPFLvyr08rWiQh6evZ7RiUTf+o65NY/bqttzJiQQ=="],
"@oxlint/binding-linux-riscv64-musl": ["@oxlint/binding-linux-riscv64-musl@1.59.0", "", { "os": "linux", "cpu": "none" }, "sha512-KUmZmKlTTyauOnvUNVxK7G40sSSx0+w5l1UhaGsC6KPpOYHenx2oqJTnabmpLJicok7IC+3Y6fXAUOMyexaeJQ=="],
"@oxlint/binding-linux-s390x-gnu": ["@oxlint/binding-linux-s390x-gnu@1.59.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-4usRxC8gS0PGdkHnRmwJt/4zrQNZyk6vL0trCxwZSsAKM+OxhB8nKiR+mhjdBbl8lbMh2gc3bZpNN/ik8c4c2A=="],
"@oxlint/binding-linux-x64-gnu": ["@oxlint/binding-linux-x64-gnu@1.59.0", "", { "os": "linux", "cpu": "x64" }, "sha512-s/rNE2gDmbwAOOP493xk2X7M8LZfI1LJFSSW1+yanz3vuQCFPiHkx4GY+O1HuLUDtkzGlhtMrIcxxzyYLv308w=="],
"@oxlint/binding-linux-x64-musl": ["@oxlint/binding-linux-x64-musl@1.59.0", "", { "os": "linux", "cpu": "x64" }, "sha512-+yYj1udJa2UvvIUmEm0IcKgc0UlPMgz0nsSTvkPL2y6n0uU5LgIHSwVu4AHhrve6j9BpVSoRksnz8c9QcvITJA=="],
"@oxlint/binding-openharmony-arm64": ["@oxlint/binding-openharmony-arm64@1.59.0", "", { "os": "none", "cpu": "arm64" }, "sha512-bUplUb48LYsB3hHlQXP2ZMOenpieWoOyppLAnnAhuPag3MGPnt+7caxE3w/Vl9wpQsTA3gzLntQi9rxWrs7Xqg=="],
"@oxlint/binding-win32-arm64-msvc": ["@oxlint/binding-win32-arm64-msvc@1.59.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-/HLsLuz42rWl7h7ePdmMTpHm2HIDmPtcEMYgm5BBEHiEiuNOrzMaUpd2z7UnNni5LGN9obJy2YoAYBLXQwazrA=="],
"@oxlint/binding-win32-ia32-msvc": ["@oxlint/binding-win32-ia32-msvc@1.59.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-rUPy+JnanpPwV/aJCPnxAD1fW50+XPI0VkWr7f0vEbqcdsS8NpB24Rw6RsS7SdpFv8Dw+8ugCwao5nCFbqOUSg=="],
"@oxlint/binding-win32-x64-msvc": ["@oxlint/binding-win32-x64-msvc@1.59.0", "", { "os": "win32", "cpu": "x64" }, "sha512-xkE7puteDS/vUyRngLXW0t8WgdWoS/tfxXjhP/P7SMqPDx+hs44SpssO3h3qmTqECYEuXBUPzcAw5257Ka+ofA=="],
"@protobuf-ts/runtime": ["@protobuf-ts/runtime@2.11.1", "", {}, "sha512-KuDaT1IfHkugM2pyz+FwiY80ejWrkH1pAtOBOZFuR6SXEFTsnb/jiQWQ1rCIrcKx2BtyxnxW6BWwsVSA/Ie+WQ=="],
"@protobuf-ts/runtime-rpc": ["@protobuf-ts/runtime-rpc@2.11.1", "", { "dependencies": { "@protobuf-ts/runtime": "^2.11.1" } }, "sha512-4CqqUmNA+/uMz00+d3CYKgElXO9VrEbucjnBFEjqI4GuDrEQ32MaI3q+9qPBvIGOlL4PmHXrzM32vBPWRhQKWQ=="],
"@tsconfig/bun": ["@tsconfig/bun@1.0.10", "", {}, "sha512-5AV5YknQjNyoYzZ/8NG0dawqew/wH+x7ANiCfCIn29qo0cdbd1EryvFD1k5NSZWLBMOI/fGqMIaxi58GPIP9Cg=="],
"@types/bun": ["@types/bun@1.3.13", "", { "dependencies": { "bun-types": "1.3.13" } }, "sha512-9fqXWk5YIHGGnUau9TEi+qdlTYDAnOj+xLCmSTwXfAIqXr2x4tytJb43E9uCvt09zJURKXwAtkoH4nLQfzeTXw=="],
"@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="],
"@types/node": ["@types/node@20.19.37", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw=="],
"@types/js-yaml": ["@types/js-yaml@4.0.9", "", {}, "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg=="],
"@typescript/native-preview": ["@typescript/native-preview@7.0.0-dev.20260410.1", "", { "optionalDependencies": { "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20260410.1", "@typescript/native-preview-darwin-x64": "7.0.0-dev.20260410.1", "@typescript/native-preview-linux-arm": "7.0.0-dev.20260410.1", "@typescript/native-preview-linux-arm64": "7.0.0-dev.20260410.1", "@typescript/native-preview-linux-x64": "7.0.0-dev.20260410.1", "@typescript/native-preview-win32-arm64": "7.0.0-dev.20260410.1", "@typescript/native-preview-win32-x64": "7.0.0-dev.20260410.1" }, "bin": { "tsgo": "bin/tsgo.js" } }, "sha512-K3TIwBw4XGQM33wW8KUqRU7r6ZY1IqB8chk1u1kT+CDj4iu+eQ6jCXgU7EDxmpJ++gbNcIf8iBYgWgYNssrhZQ=="],
"@types/node": ["@types/node@24.12.2", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g=="],
"@typescript/native-preview-darwin-arm64": ["@typescript/native-preview-darwin-arm64@7.0.0-dev.20260410.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-bpLYm6woXd8BECzV9AQvPqISVeohpekK1qwpRopvNIxydhRQ4fEjZsS7EtDYpqHAW4/u1uEv07P9/iS6TAL1fQ=="],
"@types/semver": ["@types/semver@7.7.1", "", {}, "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA=="],
"@typescript/native-preview-darwin-x64": ["@typescript/native-preview-darwin-x64@7.0.0-dev.20260410.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-V8bW8g5hgu+bAwGvTqF1kilkkoDgxhxi5egrdMUeWQkR+MIisoBQeaAupqMpLoSkqZsc/kKucM0zwBNC/KRU3Q=="],
"@typescript/native-preview": ["@typescript/native-preview@7.0.0-dev.20260409.1", "", { "optionalDependencies": { "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20260409.1", "@typescript/native-preview-darwin-x64": "7.0.0-dev.20260409.1", "@typescript/native-preview-linux-arm": "7.0.0-dev.20260409.1", "@typescript/native-preview-linux-arm64": "7.0.0-dev.20260409.1", "@typescript/native-preview-linux-x64": "7.0.0-dev.20260409.1", "@typescript/native-preview-win32-arm64": "7.0.0-dev.20260409.1", "@typescript/native-preview-win32-x64": "7.0.0-dev.20260409.1" }, "bin": { "tsgo": "bin/tsgo.js" } }, "sha512-CV1HEMGo1xCySwUJbCQOF+mmrTue8KTJ1Od2kKWhcbOpu8fPBfaqIpbAM6tGLcNEykEjMMTYHc/VTLbMgxdScQ=="],
"@typescript/native-preview-linux-arm": ["@typescript/native-preview-linux-arm@7.0.0-dev.20260410.1", "", { "os": "linux", "cpu": "arm" }, "sha512-NO6Ci65ADadOCr2ycTxOyCgC5kyk+Ryjl8k5c78mz9sKDxYqwEtryFFjLqitAG+rejtJbnUq897WRICjAOwslA=="],
"@typescript/native-preview-darwin-arm64": ["@typescript/native-preview-darwin-arm64@7.0.0-dev.20260409.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-GcRRnaoeZVrbC47woQ/2t3vPoQcTSjsWPEAQGtwNSdw7Z9TKxG4ES22ghJIQXd3ncTRCMJ+XELnnuqxVutkJ9w=="],
"@typescript/native-preview-linux-arm64": ["@typescript/native-preview-linux-arm64@7.0.0-dev.20260410.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-MOluRRAhv46s9ScFmePa0InMHmpZ/z0Evc11RrTKsg+bN8BR7sWoAtFq6IujEDK9WVP7YmEYtBRgEfMLuqVojw=="],
"@typescript/native-preview-darwin-x64": ["@typescript/native-preview-darwin-x64@7.0.0-dev.20260409.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-7s8DXAa0Xpu/8PEjYIc4I36Ju7eVpoz9k3E+3WQdOF8pIPWYohiOj+zi68m9XYQck+rnkjUFo26ThVKqVetoMA=="],
"@typescript/native-preview-linux-x64": ["@typescript/native-preview-linux-x64@7.0.0-dev.20260410.1", "", { "os": "linux", "cpu": "x64" }, "sha512-IofIUrMGjXmZKDEMaRgshzOne0EQZtx9vE/6URHfgmDnWLDKWzz9eQ2qWmvsFD2vOBbgc6GwVWEq6XTHMEfx2A=="],
"@typescript/native-preview-linux-arm": ["@typescript/native-preview-linux-arm@7.0.0-dev.20260409.1", "", { "os": "linux", "cpu": "arm" }, "sha512-fOa07JBUXQpEPq+024g346inYZ2xp63ELuoRq6J0jwDWQ/ftCCuvdQNMncwFhsm1qlMdKT3S68NrnSxX16hiaw=="],
"@typescript/native-preview-win32-arm64": ["@typescript/native-preview-win32-arm64@7.0.0-dev.20260410.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-TXmE+wovQqRo+qAhaewB0MPB9esgayvSHr6vFlCpHykHqbDl3FUucuC4F8yU6zVOA3UqXTk4/GHeLsAvU7YEgQ=="],
"@typescript/native-preview-linux-arm64": ["@typescript/native-preview-linux-arm64@7.0.0-dev.20260409.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-cGTzTUqRGlIDwdtkDy6qTrvrqpe27W4CdgnFn0FpxpiWnaIi3wqjlzQ1grtqrqainw/yuPy5hn/I86sQgN6nvA=="],
"@typescript/native-preview-win32-x64": ["@typescript/native-preview-win32-x64@7.0.0-dev.20260410.1", "", { "os": "win32", "cpu": "x64" }, "sha512-dMFT4tdHBe2vVA2WPQMjorT+fzCURRtillevQzz8/bwCEz2uXSnpu4oLRLS5045ppGE0wCFELE+Hq5z2oRddDw=="],
"@typescript/native-preview-linux-x64": ["@typescript/native-preview-linux-x64@7.0.0-dev.20260409.1", "", { "os": "linux", "cpu": "x64" }, "sha512-lQrbc/BJKBxQrR1ttBDU5sYY1Hb2moFQgHL20T6nbapNqGpK4pzy64p+NK39O93D4omiCSk04pkchBCVrMPSAg=="],
"bun-types": ["bun-types@1.3.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-QXKeHLlOLqQX9LgYaHJfzdBaV21T63HhFJnvuRCcjZiaUDpbs5ED1MgxbMra71CsryN/1dAoXuJJJwIv/2drVA=="],
"@typescript/native-preview-win32-arm64": ["@typescript/native-preview-win32-arm64@7.0.0-dev.20260409.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-kmCafMo1xZlYx+9WnfpeZJ2tnB/CcJdR8QPX7j9vqcpe51D7b7Intmr921dD48KGpVh5YgjQ1MEFE5mjGqGMaA=="],
"oxfmt": ["oxfmt@0.46.0", "", { "dependencies": { "tinypool": "2.1.0" }, "optionalDependencies": { "@oxfmt/binding-android-arm-eabi": "0.46.0", "@oxfmt/binding-android-arm64": "0.46.0", "@oxfmt/binding-darwin-arm64": "0.46.0", "@oxfmt/binding-darwin-x64": "0.46.0", "@oxfmt/binding-freebsd-x64": "0.46.0", "@oxfmt/binding-linux-arm-gnueabihf": "0.46.0", "@oxfmt/binding-linux-arm-musleabihf": "0.46.0", "@oxfmt/binding-linux-arm64-gnu": "0.46.0", "@oxfmt/binding-linux-arm64-musl": "0.46.0", "@oxfmt/binding-linux-ppc64-gnu": "0.46.0", "@oxfmt/binding-linux-riscv64-gnu": "0.46.0", "@oxfmt/binding-linux-riscv64-musl": "0.46.0", "@oxfmt/binding-linux-s390x-gnu": "0.46.0", "@oxfmt/binding-linux-x64-gnu": "0.46.0", "@oxfmt/binding-linux-x64-musl": "0.46.0", "@oxfmt/binding-openharmony-arm64": "0.46.0", "@oxfmt/binding-win32-arm64-msvc": "0.46.0", "@oxfmt/binding-win32-ia32-msvc": "0.46.0", "@oxfmt/binding-win32-x64-msvc": "0.46.0" }, "bin": { "oxfmt": "bin/oxfmt" } }, "sha512-CopwJOwPAjZ9p76fCvz+mSOJTw9/NY3cSksZK3VO/bUQ8UoEcketNgUuYS0UB3p+R9XnXe7wGGXUmyFxc7QxJA=="],
"@typescript/native-preview-win32-x64": ["@typescript/native-preview-win32-x64@7.0.0-dev.20260409.1", "", { "os": "win32", "cpu": "x64" }, "sha512-WRd+JpQipTsE15QgYr3w7J0f1NKvGcq2QEgmcq8hB0WZA1X2WhQopNu+MpPQ3tdDD42VjMhm8ZoB8HpuOoXK5w=="],
"oxlint": ["oxlint@1.61.0", "", { "optionalDependencies": { "@oxlint/binding-android-arm-eabi": "1.61.0", "@oxlint/binding-android-arm64": "1.61.0", "@oxlint/binding-darwin-arm64": "1.61.0", "@oxlint/binding-darwin-x64": "1.61.0", "@oxlint/binding-freebsd-x64": "1.61.0", "@oxlint/binding-linux-arm-gnueabihf": "1.61.0", "@oxlint/binding-linux-arm-musleabihf": "1.61.0", "@oxlint/binding-linux-arm64-gnu": "1.61.0", "@oxlint/binding-linux-arm64-musl": "1.61.0", "@oxlint/binding-linux-ppc64-gnu": "1.61.0", "@oxlint/binding-linux-riscv64-gnu": "1.61.0", "@oxlint/binding-linux-riscv64-musl": "1.61.0", "@oxlint/binding-linux-s390x-gnu": "1.61.0", "@oxlint/binding-linux-x64-gnu": "1.61.0", "@oxlint/binding-linux-x64-musl": "1.61.0", "@oxlint/binding-openharmony-arm64": "1.61.0", "@oxlint/binding-win32-arm64-msvc": "1.61.0", "@oxlint/binding-win32-ia32-msvc": "1.61.0", "@oxlint/binding-win32-x64-msvc": "1.61.0" }, "peerDependencies": { "oxlint-tsgolint": ">=0.18.0" }, "optionalPeers": ["oxlint-tsgolint"], "bin": { "oxlint": "bin/oxlint" } }, "sha512-ZC0ALuhDZ6ivOFG+sy0D0pEDN49EvsId98zVlmYdkcXHsEM14m/qTNUEsUpiFiCVbpIxYtVBmmLE87nsbUHohQ=="],
"@typespec/ts-http-runtime": ["@typespec/ts-http-runtime@0.3.5", "", { "dependencies": { "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", "tslib": "^2.6.2" } }, "sha512-yURCknZhvywvQItHMMmFSo+fq5arCUIyz/CVk7jD89MSai7dkaX8ufjCWp3NttLojoTVbcE72ri+be/TnEbMHw=="],
"oxlint-tsgolint": ["oxlint-tsgolint@0.21.1", "", { "optionalDependencies": { "@oxlint-tsgolint/darwin-arm64": "0.21.1", "@oxlint-tsgolint/darwin-x64": "0.21.1", "@oxlint-tsgolint/linux-arm64": "0.21.1", "@oxlint-tsgolint/linux-x64": "0.21.1", "@oxlint-tsgolint/win32-arm64": "0.21.1", "@oxlint-tsgolint/win32-x64": "0.21.1" }, "bin": { "tsgolint": "bin/tsgolint.js" } }, "sha512-O2hxiT14C2HJkwzBU6CQBFPoagSd/IcV+Tt3e3UUaXFwbW4BO5DSDPSSboc3UM5MIDY+MLyepvtQwBQafNxWdw=="],
"agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
"balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="],
"brace-expansion": ["brace-expansion@1.1.13", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w=="],
"bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="],
"concat-map": ["concat-map@0.0.1", "", {}, "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="],
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
"events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="],
"fast-xml-builder": ["fast-xml-builder@1.1.4", "", { "dependencies": { "path-expression-matcher": "^1.1.3" } }, "sha512-f2jhpN4Eccy0/Uz9csxh3Nu6q4ErKxf0XIsasomfOihuSUa3/xw6w8dnOtCDgEItQFJG8KyXPzQXzcODDrrbOg=="],
"fast-xml-parser": ["fast-xml-parser@5.5.11", "", { "dependencies": { "fast-xml-builder": "^1.1.4", "path-expression-matcher": "^1.4.0", "strnum": "^2.2.3" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-QL0eb0YbSTVWF6tTf1+LEMSgtCEjBYPpnAjoLC8SscESlAjXEIRJ7cHtLG0pLeDFaZLa4VKZLArtA/60ZS7vyA=="],
"http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="],
"https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
"js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="],
"minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="],
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
"oxfmt": ["oxfmt@0.44.0", "", { "dependencies": { "tinypool": "2.1.0" }, "optionalDependencies": { "@oxfmt/binding-android-arm-eabi": "0.44.0", "@oxfmt/binding-android-arm64": "0.44.0", "@oxfmt/binding-darwin-arm64": "0.44.0", "@oxfmt/binding-darwin-x64": "0.44.0", "@oxfmt/binding-freebsd-x64": "0.44.0", "@oxfmt/binding-linux-arm-gnueabihf": "0.44.0", "@oxfmt/binding-linux-arm-musleabihf": "0.44.0", "@oxfmt/binding-linux-arm64-gnu": "0.44.0", "@oxfmt/binding-linux-arm64-musl": "0.44.0", "@oxfmt/binding-linux-ppc64-gnu": "0.44.0", "@oxfmt/binding-linux-riscv64-gnu": "0.44.0", "@oxfmt/binding-linux-riscv64-musl": "0.44.0", "@oxfmt/binding-linux-s390x-gnu": "0.44.0", "@oxfmt/binding-linux-x64-gnu": "0.44.0", "@oxfmt/binding-linux-x64-musl": "0.44.0", "@oxfmt/binding-openharmony-arm64": "0.44.0", "@oxfmt/binding-win32-arm64-msvc": "0.44.0", "@oxfmt/binding-win32-ia32-msvc": "0.44.0", "@oxfmt/binding-win32-x64-msvc": "0.44.0" }, "bin": { "oxfmt": "bin/oxfmt" } }, "sha512-lnncqvHewyRvaqdrnntVIrZV2tEddz8lbvPsQzG/zlkfvgZkwy0HP1p/2u1aCDToeg1jb9zBpbJdfkV73Itw+w=="],
"oxlint": ["oxlint@1.59.0", "", { "optionalDependencies": { "@oxlint/binding-android-arm-eabi": "1.59.0", "@oxlint/binding-android-arm64": "1.59.0", "@oxlint/binding-darwin-arm64": "1.59.0", "@oxlint/binding-darwin-x64": "1.59.0", "@oxlint/binding-freebsd-x64": "1.59.0", "@oxlint/binding-linux-arm-gnueabihf": "1.59.0", "@oxlint/binding-linux-arm-musleabihf": "1.59.0", "@oxlint/binding-linux-arm64-gnu": "1.59.0", "@oxlint/binding-linux-arm64-musl": "1.59.0", "@oxlint/binding-linux-ppc64-gnu": "1.59.0", "@oxlint/binding-linux-riscv64-gnu": "1.59.0", "@oxlint/binding-linux-riscv64-musl": "1.59.0", "@oxlint/binding-linux-s390x-gnu": "1.59.0", "@oxlint/binding-linux-x64-gnu": "1.59.0", "@oxlint/binding-linux-x64-musl": "1.59.0", "@oxlint/binding-openharmony-arm64": "1.59.0", "@oxlint/binding-win32-arm64-msvc": "1.59.0", "@oxlint/binding-win32-ia32-msvc": "1.59.0", "@oxlint/binding-win32-x64-msvc": "1.59.0" }, "peerDependencies": { "oxlint-tsgolint": ">=0.18.0" }, "optionalPeers": ["oxlint-tsgolint"], "bin": { "oxlint": "bin/oxlint" } }, "sha512-0xBLeGGjP4vD9pygRo8iuOkOzEU1MqOnfiOl7KYezL/QvWL8NUg6n03zXc7ZVqltiOpUxBk2zgHI3PnRIEdAvw=="],
"oxlint-tsgolint": ["oxlint-tsgolint@0.20.0", "", { "optionalDependencies": { "@oxlint-tsgolint/darwin-arm64": "0.20.0", "@oxlint-tsgolint/darwin-x64": "0.20.0", "@oxlint-tsgolint/linux-arm64": "0.20.0", "@oxlint-tsgolint/linux-x64": "0.20.0", "@oxlint-tsgolint/win32-arm64": "0.20.0", "@oxlint-tsgolint/win32-x64": "0.20.0" }, "bin": { "tsgolint": "bin/tsgolint.js" } }, "sha512-/Uc9TQyN1l8w9QNvXtVHYtz+SzDJHKpb5X0UnHodl0BVzijUPk0LPlDOHAvogd1UI+iy9ZSF6gQxEqfzUxCULQ=="],
"path-expression-matcher": ["path-expression-matcher@1.5.0", "", {}, "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ=="],
"semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="],
"strnum": ["strnum@2.2.3", "", {}, "sha512-oKx6RUCuHfT3oyVjtnrmn19H1SiCqgJSg+54XqURKp5aCMbrXrhLjRN9TjuwMjiYstZ0MzDrHqkGZ5dFTKd+zg=="],
"tinypool": ["tinypool@2.1.0", "", {}, "sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw=="],
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
"tunnel": ["tunnel@0.0.6", "", {}, "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="],
"undici": ["undici@6.24.1", "", {}, "sha512-sC+b0tB1whOCzbtlx20fx3WgCXwkW627p4EA9uM+/tNNPkSS+eSEld6pAs9nDv7WbY1UUljBMYPtu9BCOrCWKA=="],
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
"@actions/tool-cache/@actions/core": ["@actions/core@3.0.0", "", { "dependencies": { "@actions/exec": "^3.0.0", "@actions/http-client": "^4.0.0" } }, "sha512-zYt6cz+ivnTmiT/ksRVriMBOiuoUpDCJJlZ5KPl2/FRdvwU3f7MPh9qftvbkXJThragzUZieit2nyHUyw53Seg=="],
"bun-types/@types/node": ["@types/node@20.19.37", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw=="],
"bun-types/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
}
}

94560
dist/main.js vendored Normal file

File diff suppressed because one or more lines are too long

73430
dist/post.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -37,6 +37,21 @@ To pin a specific CLI version:
version: 2.84.2
```
To cache the Docker images pulled by `supabase start`:
```yaml
- uses: supabase/setup-cli@v2
with:
version: 2.84.2
cache: true
- run: supabase start
```
The first run pulls the images from the registry. Later runs can restore the
same image archive from the GitHub Actions cache before `supabase start` runs.
Use `cache-key` when your workflow flags or generated config change the image
set.
## Resources
- **Source Code**: <https://github.com/supabase/setup-cli>

View File

@@ -14,26 +14,33 @@
},
"type": "module",
"scripts": {
"all": "bun run format && bun run lint && bun run coverage",
"ci": "bun run format:check && bun run lint && bun run coverage",
"all": "bun run format && bun run lint && bun run coverage && bun run build",
"build": "bun build src/main.ts src/post.ts --target=node --format=esm --outdir=dist",
"ci": "bun run format:check && bun run lint && bun run coverage && bun run build",
"coverage": "bun test --coverage --coverage-reporter=text --coverage-reporter=lcov",
"format": "bun x oxfmt --write . '!coverage/**'",
"format:check": "bun x oxfmt --check . '!coverage/**'",
"format": "bun x oxfmt --write . '!coverage/**' '!dist/**'",
"format:check": "bun x oxfmt --check . '!coverage/**' '!dist/**'",
"lint": "bun x oxlint --deny-warnings --type-aware --type-check --tsconfig tsconfig.json src",
"test": "bun test",
"typecheck": "bun x tsgo -p tsconfig.json --noEmit"
},
"dependencies": {
"@actions/core": "^3.0.1",
"@actions/tool-cache": "^4.0.0"
"@actions/cache": "^6.0.0",
"@actions/core": "^3.0.0",
"@actions/tool-cache": "^4.0.0",
"js-yaml": "^4.1.1",
"semver": "^7.7.4"
},
"devDependencies": {
"@tsconfig/bun": "^1.0.10",
"@types/bun": "^1.3.13",
"@typescript/native-preview": "^7.0.0-dev.20260410.1",
"oxfmt": "^0.46.0",
"oxlint": "^1.61.0",
"oxlint-tsgolint": "^0.21.1"
"@types/bun": "^1.3.11",
"@types/js-yaml": "^4.0.9",
"@types/node": "^24",
"@types/semver": "^7.7.1",
"@typescript/native-preview": "^7.0.0-dev.20260409.1",
"oxfmt": "^0.44.0",
"oxlint": "^1.59.0",
"oxlint-tsgolint": "^0.20.0"
},
"engines": {
"bun": ">=1.3.10"

168
src/cache.test.ts Normal file
View File

@@ -0,0 +1,168 @@
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import os from "node:os";
import path from "node:path";
import { afterEach, expect, mock, spyOn, test } from "bun:test";
import * as cacheAction from "@actions/cache";
import * as core from "@actions/core";
import {
collectDockerImageRefs,
createDockerImageCacheKey,
restoreDockerImageCache,
saveDockerImageCache,
} from "./cache";
const originalRunnerTemp = process.env.RUNNER_TEMP;
const originalWorkspace = process.env.GITHUB_WORKSPACE;
const tempDirs = new Set<string>();
afterEach(() => {
mock.restore();
process.env.RUNNER_TEMP = originalRunnerTemp;
process.env.GITHUB_WORKSPACE = originalWorkspace;
for (const dir of tempDirs) {
rmSync(dir, { force: true, recursive: true });
}
tempDirs.clear();
});
function createTempDir(prefix: string): string {
const dir = mkdtempSync(path.join(os.tmpdir(), prefix));
tempDirs.add(dir);
return dir;
}
test("creates a docker image cache key from runner, version, registry, and config", () => {
const workspace = createTempDir("setup-cli-workspace-");
mkdirSync(path.join(workspace, "supabase"), { recursive: true });
writeFileSync(path.join(workspace, "supabase", "config.toml"), 'project_id = "test"\n');
process.env.GITHUB_WORKSPACE = workspace;
const key = createDockerImageCacheKey("supabase 2.84.2", "ghcr.io");
expect(key).toStartWith(`supabase-cli-containers-v1-${process.platform}-${process.arch}-`);
expect(key).toContain("supabase-2.84.2-ghcr.io-");
});
test("collects images from labeled containers and Supabase image repositories", async () => {
const run = mock(async (_file: string, args: string[]) => {
if (args[0] === "ps") {
return {
stdout: "ghcr.io/supabase/postgres:15.8.1\ncustom/image:latest\n",
stderr: "",
};
}
return {
stdout:
"ghcr.io/supabase/studio:2026.04.08\npublic.ecr.aws/supabase/kong:2.8.1\nlibrary/postgres:16\n<none>:<none>\n",
stderr: "",
};
});
expect(await collectDockerImageRefs(run)).toEqual([
"custom/image:latest",
"ghcr.io/supabase/postgres:15.8.1",
"ghcr.io/supabase/studio:2026.04.08",
"public.ecr.aws/supabase/kong:2.8.1",
]);
});
test("restore skips docker and cache calls when cache input is disabled", async () => {
const run = mock(async () => ({ stdout: "", stderr: "" }));
const restoreCache = spyOn(cacheAction, "restoreCache").mockImplementation(async () => undefined);
const spies = {
getBooleanInput: spyOn(core, "getBooleanInput").mockImplementation(() => false),
getInput: spyOn(core, "getInput").mockImplementation(() => ""),
setOutput: spyOn(core, "setOutput").mockImplementation(() => {}),
saveState: spyOn(core, "saveState").mockImplementation(() => {}),
};
await restoreDockerImageCache("supabase 2.84.2", "ghcr.io", run);
expect(spies.setOutput).toHaveBeenCalledWith("cache-hit", "false");
expect(spies.saveState).toHaveBeenCalledWith("cache-enabled", "false");
expect(run).not.toHaveBeenCalled();
expect(restoreCache).not.toHaveBeenCalled();
});
test("restore loads a docker archive on exact cache hit", async () => {
const temp = createTempDir("setup-cli-runner-");
process.env.RUNNER_TEMP = temp;
const calls: string[][] = [];
const run = mock(async (_file: string, args: string[]) => {
calls.push(args);
return { stdout: "ok\n", stderr: "" };
});
const restoreCache = spyOn(cacheAction, "restoreCache").mockImplementation(
async (paths: string[], key: string) => {
writeFileSync(paths[0]!, "archive");
return key;
},
);
const spies = {
getBooleanInput: spyOn(core, "getBooleanInput").mockImplementation(() => true),
getInput: spyOn(core, "getInput").mockImplementation((name: string) =>
name === "cache-key" ? "cache-key" : "",
),
setOutput: spyOn(core, "setOutput").mockImplementation(() => {}),
saveState: spyOn(core, "saveState").mockImplementation(() => {}),
info: spyOn(core, "info").mockImplementation(() => {}),
warning: spyOn(core, "warning").mockImplementation(() => {}),
};
await restoreDockerImageCache("supabase 2.84.2", "ghcr.io", run);
expect(restoreCache).toHaveBeenCalledWith(
[path.join(temp, "setup-supabase-cli", "supabase-cli-docker-images.tar")],
"cache-key",
);
expect(spies.setOutput).toHaveBeenCalledWith("cache-hit", "true");
expect(spies.saveState).toHaveBeenCalledWith("cache-hit", "true");
expect(calls).toContainEqual([
"load",
"-i",
path.join(temp, "setup-supabase-cli", "supabase-cli-docker-images.tar"),
]);
expect(spies.warning).not.toHaveBeenCalled();
});
test("post saves collected Supabase docker images", async () => {
const temp = createTempDir("setup-cli-runner-");
process.env.RUNNER_TEMP = temp;
const calls: string[][] = [];
const run = mock(async (_file: string, args: string[]) => {
calls.push(args);
if (args[0] === "ps") {
return { stdout: "ghcr.io/supabase/postgres:15.8.1\n", stderr: "" };
}
if (args[0] === "image") {
return { stdout: "ghcr.io/supabase/studio:2026.04.08\n", stderr: "" };
}
return { stdout: "ok\n", stderr: "" };
});
const saveCache = spyOn(cacheAction, "saveCache").mockImplementation(async () => 1);
const state = new Map([
["cache-enabled", "true"],
["cache-hit", "false"],
["cache-primary-key", "cache-key"],
["cache-archive-path", path.join(temp, "setup-supabase-cli", "supabase-cli-docker-images.tar")],
]);
spyOn(core, "getState").mockImplementation((name: string) => state.get(name) ?? "");
spyOn(core, "info").mockImplementation(() => {});
spyOn(core, "warning").mockImplementation(() => {});
await saveDockerImageCache(run);
expect(calls).toContainEqual([
"save",
"-o",
path.join(temp, "setup-supabase-cli", "supabase-cli-docker-images.tar"),
"ghcr.io/supabase/postgres:15.8.1",
"ghcr.io/supabase/studio:2026.04.08",
]);
expect(saveCache).toHaveBeenCalledWith(
[path.join(temp, "setup-supabase-cli", "supabase-cli-docker-images.tar")],
"cache-key",
);
});

254
src/cache.ts Normal file
View File

@@ -0,0 +1,254 @@
import * as cache from "@actions/cache";
import * as core from "@actions/core";
import { execFile as execFileCallback } from "node:child_process";
import { createHash } from "node:crypto";
import { existsSync, mkdirSync, readFileSync } from "node:fs";
import os from "node:os";
import path from "node:path";
import { promisify } from "node:util";
const CLI_CONFIG_REGISTRY = "SUPABASE_INTERNAL_IMAGE_REGISTRY";
const CACHE_ARCHIVE = "supabase-cli-docker-images.tar";
const CACHE_DIR = "setup-supabase-cli";
const CACHE_KEY_VERSION = "v1";
const DEFAULT_REGISTRY = "public.ecr.aws";
const GHCR_REGISTRY = "ghcr.io";
const CACHE_INPUT = "cache";
const CACHE_KEY_INPUT = "cache-key";
const CACHE_HIT_OUTPUT = "cache-hit";
const STATE_ENABLED = "cache-enabled";
const STATE_PRIMARY_KEY = "cache-primary-key";
const STATE_ARCHIVE_PATH = "cache-archive-path";
const STATE_CACHE_HIT = "cache-hit";
const CLI_PROJECT_LABEL = "com.supabase.cli.project";
const SUPABASE_IMAGE_PREFIXES = ["ghcr.io/supabase/", "public.ecr.aws/supabase/", "supabase/"];
type ExecFile = (
file: string,
args: string[],
options?: { maxBuffer?: number },
) => Promise<{ stdout: string; stderr: string }>;
const execFile = promisify(execFileCallback) as ExecFile;
function sanitizeCacheKeyPart(value: string): string {
return value.replace(/[^A-Za-z0-9_.-]/g, "-").replace(/-+/g, "-");
}
function hashFile(filePath: string): string | null {
if (!existsSync(filePath)) {
return null;
}
try {
return createHash("sha256").update(readFileSync(filePath)).digest("hex");
} catch {
return null;
}
}
function getConfigHash(): string {
const workspaceRoot = process.env.GITHUB_WORKSPACE?.trim();
if (!workspaceRoot) {
return "no-config";
}
return hashFile(path.join(workspaceRoot, "supabase", "config.toml")) ?? "no-config";
}
function getCacheArchivePath(): string {
const runnerTemp = process.env.RUNNER_TEMP?.trim() || os.tmpdir();
return path.join(runnerTemp, CACHE_DIR, CACHE_ARCHIVE);
}
export function getImageRegistry(): string {
return process.env[CLI_CONFIG_REGISTRY]?.trim() || DEFAULT_REGISTRY;
}
export function getGhcrImageRegistry(): string {
return GHCR_REGISTRY;
}
export function createDockerImageCacheKey(installedVersion: string, registry: string): string {
return [
"supabase-cli-containers",
CACHE_KEY_VERSION,
sanitizeCacheKeyPart(process.platform),
sanitizeCacheKeyPart(process.arch),
sanitizeCacheKeyPart(installedVersion),
sanitizeCacheKeyPart(registry),
getConfigHash(),
].join("-");
}
function getPrimaryCacheKey(installedVersion: string, registry: string): string {
const cacheKeyInput = core.getInput(CACHE_KEY_INPUT).trim();
if (cacheKeyInput) {
return cacheKeyInput;
}
return createDockerImageCacheKey(installedVersion, registry);
}
async function isDockerAvailable(run: ExecFile = execFile): Promise<boolean> {
try {
await run("docker", ["version"]);
return true;
} catch {
return false;
}
}
async function runDocker(args: string[], run: ExecFile = execFile): Promise<string> {
const { stdout } = await run("docker", args, { maxBuffer: 1024 * 1024 * 16 });
return stdout;
}
function normalizeImageRefs(output: string): string[] {
return output
.split(/\r?\n/)
.map((line) => line.trim())
.filter((line) => line.length > 0 && !line.includes("<none>"));
}
function isSupabaseImageRef(ref: string): boolean {
return SUPABASE_IMAGE_PREFIXES.some((prefix) => ref.startsWith(prefix));
}
export async function collectDockerImageRefs(run: ExecFile = execFile): Promise<string[]> {
const refs = new Set<string>();
try {
const output = await runDocker(
["ps", "-a", "--filter", `label=${CLI_PROJECT_LABEL}`, "--format", "{{.Image}}"],
run,
);
for (const ref of normalizeImageRefs(output)) {
refs.add(ref);
}
} catch (error) {
core.warning(
`Could not list Supabase CLI containers for Docker image cache: ${error instanceof Error ? error.message : String(error)}`,
);
}
try {
const output = await runDocker(["image", "ls", "--format", "{{.Repository}}:{{.Tag}}"], run);
for (const ref of normalizeImageRefs(output).filter(isSupabaseImageRef)) {
refs.add(ref);
}
} catch (error) {
core.warning(
`Could not list Docker images for Supabase image cache: ${error instanceof Error ? error.message : String(error)}`,
);
}
return [...refs].sort();
}
export async function restoreDockerImageCache(
installedVersion: string,
registry: string,
run: ExecFile = execFile,
): Promise<void> {
const enabled = core.getBooleanInput(CACHE_INPUT);
core.setOutput(CACHE_HIT_OUTPUT, "false");
core.saveState(STATE_ENABLED, String(enabled));
if (!enabled) {
return;
}
const archivePath = getCacheArchivePath();
const primaryKey = getPrimaryCacheKey(installedVersion, registry);
mkdirSync(path.dirname(archivePath), { recursive: true });
core.saveState(STATE_PRIMARY_KEY, primaryKey);
core.saveState(STATE_ARCHIVE_PATH, archivePath);
core.saveState(STATE_CACHE_HIT, "false");
if (!(await isDockerAvailable(run))) {
core.warning("Docker is not available. Skipping Supabase Docker image cache restore.");
return;
}
let matchedKey: string | undefined;
try {
matchedKey = await cache.restoreCache([archivePath], primaryKey);
} catch (error) {
core.warning(
`Could not restore Supabase Docker image cache: ${error instanceof Error ? error.message : String(error)}`,
);
return;
}
if (!matchedKey) {
core.info("No Supabase Docker image cache found.");
return;
}
const cacheHit = matchedKey === primaryKey;
core.setOutput(CACHE_HIT_OUTPUT, String(cacheHit));
core.saveState(STATE_CACHE_HIT, String(cacheHit));
if (!existsSync(archivePath)) {
core.warning("Supabase Docker image cache was restored, but the archive is missing.");
return;
}
try {
await runDocker(["load", "-i", archivePath], run);
core.info("Loaded Supabase Docker images from cache.");
} catch (error) {
core.warning(
`Could not load Supabase Docker image cache: ${error instanceof Error ? error.message : String(error)}`,
);
}
}
export async function saveDockerImageCache(run: ExecFile = execFile): Promise<void> {
if (core.getState(STATE_ENABLED) !== "true") {
return;
}
if (core.getState(STATE_CACHE_HIT) === "true") {
core.info("Supabase Docker image cache hit. Skipping cache save.");
return;
}
const primaryKey = core.getState(STATE_PRIMARY_KEY);
const archivePath = core.getState(STATE_ARCHIVE_PATH) || getCacheArchivePath();
if (!primaryKey) {
core.warning("Supabase Docker image cache key is missing. Skipping cache save.");
return;
}
if (!(await isDockerAvailable(run))) {
core.warning("Docker is not available. Skipping Supabase Docker image cache save.");
return;
}
const imageRefs = await collectDockerImageRefs(run);
if (imageRefs.length === 0) {
core.warning("No Supabase Docker images found to cache.");
return;
}
mkdirSync(path.dirname(archivePath), { recursive: true });
try {
await runDocker(["save", "-o", archivePath, ...imageRefs], run);
} catch (error) {
core.warning(
`Could not create Supabase Docker image archive: ${error instanceof Error ? error.message : String(error)}`,
);
return;
}
try {
await cache.saveCache([archivePath], primaryKey);
core.info(`Saved ${imageRefs.length} Supabase Docker image(s) to cache.`);
} catch (error) {
core.warning(
`Could not save Supabase Docker image cache: ${error instanceof Error ? error.message : String(error)}`,
);
}
}

View File

@@ -146,10 +146,16 @@ function createPackageLock(version: string): string {
function createActionSpies(inputVersion: string, cliDir: string, expectedUrlFragment: string) {
return {
getInput: spyOn(core, "getInput").mockReturnValue(inputVersion),
getInput: spyOn(core, "getInput").mockImplementation((name: string) =>
name === "version" ? inputVersion : "",
),
getBooleanInput: spyOn(core, "getBooleanInput").mockImplementation(() => false),
setOutput: spyOn(core, "setOutput").mockImplementation(() => {}),
addPath: spyOn(core, "addPath").mockImplementation(() => {}),
exportVariable: spyOn(core, "exportVariable").mockImplementation(() => {}),
saveState: spyOn(core, "saveState").mockImplementation(() => {}),
info: spyOn(core, "info").mockImplementation(() => {}),
warning: spyOn(core, "warning").mockImplementation(() => {}),
setFailed: spyOn(core, "setFailed").mockImplementation(() => {}),
downloadTool: spyOn(tc, "downloadTool").mockImplementation(async (url: string) => {
expect(url).toContain(expectedUrlFragment);
@@ -159,16 +165,6 @@ function createActionSpies(inputVersion: string, cliDir: string, expectedUrlFrag
};
}
function mockLatestRelease(version: string) {
const response = new Response(null, {
status: 302,
headers: { location: `https://github.com/supabase/cli/releases/tag/v${version}` },
});
return spyOn(globalThis, "fetch").mockImplementation(
(async () => response) as unknown as typeof fetch,
);
}
async function getMainModule(): Promise<typeof import("./main.ts")> {
if (!mainModule) {
mainModule = await import("./main.ts");
@@ -180,7 +176,6 @@ async function getMainModule(): Promise<typeof import("./main.ts")> {
test("awaits the action entrypoint with omitted version and latest fallback", async () => {
process.env.GITHUB_WORKSPACE = repo;
const cliDir = createFakeCli("supabase 2.84.2");
mockLatestRelease("2.84.2");
let startDownload!: () => void;
let finishDownload!: () => void;
const downloadStarted = new Promise<void>((resolve) => {
@@ -190,13 +185,19 @@ test("awaits the action entrypoint with omitted version and latest fallback", as
finishDownload = () => resolve(path.join(os.tmpdir(), "supabase-cli.tar.gz"));
});
const spies = {
getInput: spyOn(core, "getInput").mockReturnValue(""),
getInput: spyOn(core, "getInput").mockImplementation((name: string) =>
name === "version" ? "" : "",
),
getBooleanInput: spyOn(core, "getBooleanInput").mockImplementation(() => false),
setOutput: spyOn(core, "setOutput").mockImplementation(() => {}),
addPath: spyOn(core, "addPath").mockImplementation(() => {}),
exportVariable: spyOn(core, "exportVariable").mockImplementation(() => {}),
saveState: spyOn(core, "saveState").mockImplementation(() => {}),
info: spyOn(core, "info").mockImplementation(() => {}),
warning: spyOn(core, "warning").mockImplementation(() => {}),
setFailed: spyOn(core, "setFailed").mockImplementation(() => {}),
downloadTool: spyOn(tc, "downloadTool").mockImplementation(async (url: string) => {
expect(url).toContain("/download/v2.84.2/supabase_");
expect(url).toContain("/latest/download/");
startDownload();
return downloadFinished;
}),
@@ -295,8 +296,7 @@ test("falls back to latest when version is omitted and no supported root lockfil
"README.md": "# app\n",
});
const cliDir = createFakeCli("supabase 2.84.2");
mockLatestRelease("2.84.2");
const spies = createActionSpies("", cliDir, "/download/v2.84.2/supabase_");
const spies = createActionSpies("", cliDir, "/latest/download/");
const { run } = await getMainModule();
await run();
@@ -309,8 +309,7 @@ test("falls back to latest when version is omitted and no supported root lockfil
test("falls back to latest when version is omitted and no workspace is available", async () => {
delete process.env.GITHUB_WORKSPACE;
const cliDir = createFakeCli("supabase 2.84.2");
mockLatestRelease("2.84.2");
const spies = createActionSpies("", cliDir, "/download/v2.84.2/supabase_");
const spies = createActionSpies("", cliDir, "/latest/download/");
const { run } = await getMainModule();
await run();
@@ -374,8 +373,7 @@ test("falls through unreadable bun.lock paths and malformed package-lock files t
mkdirSync(path.join(workspace, "bun.lock"), { recursive: true });
process.env.GITHUB_WORKSPACE = workspace;
const cliDir = createFakeCli("supabase 2.84.2");
mockLatestRelease("2.84.2");
const spies = createActionSpies("", cliDir, "/download/v2.84.2/supabase_");
const spies = createActionSpies("", cliDir, "/latest/download/");
const { run } = await getMainModule();
await run();
@@ -390,8 +388,7 @@ test("falls back to latest when a pnpm dependency entry has no concrete version"
"pnpm-lock.yaml": createPnpmLock("2.49.0", { includeVersion: false }),
});
const cliDir = createFakeCli("supabase 2.84.2");
mockLatestRelease("2.84.2");
const spies = createActionSpies("", cliDir, "/download/v2.84.2/supabase_");
const spies = createActionSpies("", cliDir, "/latest/download/");
const { run } = await getMainModule();
await run();
@@ -416,52 +413,6 @@ test("explicit version overrides detected root lockfiles", async () => {
expect(spies.setFailed).not.toHaveBeenCalled();
});
test("downloads the version-prefixed tarball for releases >= 2.99.0", async () => {
// Regression for supabase/cli#5257: from v2.99.0 onward the CLI only
// publishes supabase_<version>_<platform>_<arch>.tar.gz; the unversioned
// alias is gone.
delete process.env.GITHUB_WORKSPACE;
const cliDir = createFakeCli("supabase 2.99.0");
mockLatestRelease("2.99.0");
const spies = createActionSpies("", cliDir, "/download/v2.99.0/supabase_2.99.0_");
const { run } = await getMainModule();
await run();
expect(spies.setOutput).toHaveBeenCalledWith("version", "supabase 2.99.0");
expect(spies.exportVariable).toHaveBeenCalledWith(CLI_CONFIG_REGISTRY, "ghcr.io");
expect(spies.setFailed).not.toHaveBeenCalled();
});
test("downloads the version-prefixed tarball when explicit version >= 2.99.0 is requested", async () => {
delete process.env.GITHUB_WORKSPACE;
const cliDir = createFakeCli("supabase 2.99.0");
const fetchSpy = spyOn(globalThis, "fetch");
const spies = createActionSpies("2.99.0", cliDir, "/download/v2.99.0/supabase_2.99.0_");
const { run } = await getMainModule();
await run();
expect(fetchSpy).not.toHaveBeenCalled();
expect(spies.setOutput).toHaveBeenCalledWith("version", "supabase 2.99.0");
expect(spies.exportVariable).toHaveBeenCalledWith(CLI_CONFIG_REGISTRY, "ghcr.io");
expect(spies.setFailed).not.toHaveBeenCalled();
});
test("fails when the latest release redirect does not include a version tag", async () => {
delete process.env.GITHUB_WORKSPACE;
const cliDir = createFakeCli("supabase 2.99.0");
const response = new Response(null, { status: 302, headers: { location: "/releases" } });
spyOn(globalThis, "fetch").mockImplementation((async () => response) as unknown as typeof fetch);
const spies = createActionSpies("", cliDir, "/download/");
const { run } = await getMainModule();
await run();
expect(spies.downloadTool).not.toHaveBeenCalled();
expect(spies.setFailed).toHaveBeenCalled();
});
test("fails when the installed CLI does not report a version", async () => {
process.env.GITHUB_WORKSPACE = createWorkspace({
"package-lock.json": createPackageLock("2.46.0"),

View File

@@ -1,18 +1,18 @@
import { $, semver } from "bun";
import * as core from "@actions/core";
import * as tc from "@actions/tool-cache";
import { load as loadYaml } from "js-yaml";
import * as semver from "semver";
import { execFile as execFileCallback } from "node:child_process";
import { existsSync, readFileSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { promisify } from "node:util";
import { getGhcrImageRegistry, getImageRegistry, restoreDockerImageCache } from "./cache";
export const CLI_CONFIG_REGISTRY = "SUPABASE_INTERNAL_IMAGE_REGISTRY";
const REGISTRY_VERSION = "1.28.0";
// Starting with this release, the CLI publishes only version-prefixed tarballs
// (e.g. supabase_2.99.0_linux_amd64.tar.gz); the unversioned aliases that used
// to live alongside them are no longer uploaded. See supabase/cli#5257.
const VERSIONED_ARCHIVE_VERSION = "2.99.0";
const DEFAULT_VERSION = "latest";
const LATEST_RELEASE_URL = "https://github.com/supabase/cli/releases/latest";
const execFile = promisify(execFileCallback);
type BunLock = {
workspaces?: {
@@ -111,7 +111,7 @@ function detectVersionFromPnpmLock(workspaceRoot: string): string | null {
}
try {
const lockfile = Bun.YAML.parse(text) as PnpmLock;
const lockfile = loadYaml(text) as PnpmLock;
const rootImporter = lockfile.importers?.["."];
const dependency =
rootImporter?.dependencies?.supabase ?? rootImporter?.devDependencies?.supabase;
@@ -166,40 +166,39 @@ function resolveVersion(inputVersion: string): string {
);
}
export async function resolveLatestVersion(): Promise<string> {
const response = await fetch(LATEST_RELEASE_URL, { method: "HEAD", redirect: "manual" });
const location = response.headers.get("location");
const version = extractConcreteVersion(location ?? undefined);
if (!version) {
throw new Error(
`Could not resolve latest Supabase CLI version (status ${response.status}, location ${location ?? "<none>"})`,
);
}
return version;
}
export function getDownloadUrl(version: string): string {
const platform = getArchivePlatform(process.platform);
const arch = getArchiveArch(process.arch);
const versionedFilename = `supabase_${version}_${platform}_${arch}.tar.gz`;
const unversionedFilename = `supabase_${platform}_${arch}.tar.gz`;
const filename = `supabase_${platform}_${arch}.tar.gz`;
// v2.99.0+ and the earliest releases (pre-v1.28.0) only publish version-prefixed
// tarballs; the intermediate releases publish unversioned aliases.
if (
semver.order(version, REGISTRY_VERSION) === -1 ||
semver.order(version, VERSIONED_ARCHIVE_VERSION) >= 0
) {
return `https://github.com/supabase/cli/releases/download/v${version}/${versionedFilename}`;
if (version.toLowerCase() === "latest") {
return `https://github.com/supabase/cli/releases/latest/download/${filename}`;
}
return `https://github.com/supabase/cli/releases/download/v${version}/${unversionedFilename}`;
if (semver.compare(version, REGISTRY_VERSION) === -1) {
return `https://github.com/supabase/cli/releases/download/v${version}/supabase_${version}_${platform}_${arch}.tar.gz`;
}
return `https://github.com/supabase/cli/releases/download/v${version}/${filename}`;
}
function getSupabaseExecutable(cliPath: string): string {
const names =
process.platform === "win32" ? ["supabase.exe", "supabase.cmd", "supabase"] : ["supabase"];
for (const name of names) {
const executable = path.join(cliPath, name);
if (existsSync(executable)) {
return executable;
}
}
return path.join(cliPath, "supabase");
}
export async function determineInstalledVersion(cliPath: string): Promise<string> {
const version = (await $`${path.join(cliPath, "supabase")} --version`.text()).trim();
const { stdout } = await execFile(getSupabaseExecutable(cliPath), ["--version"]);
const version = stdout.trim();
if (!version) {
throw new Error("Could not determine installed Supabase CLI version");
}
@@ -209,18 +208,18 @@ export async function determineInstalledVersion(cliPath: string): Promise<string
export async function run(): Promise<void> {
try {
const requestedVersion = resolveVersion(core.getInput("version"));
const version =
requestedVersion.toLowerCase() === "latest" ? await resolveLatestVersion() : requestedVersion;
const version = resolveVersion(core.getInput("version"));
const tarball = await tc.downloadTool(getDownloadUrl(version));
const cliPath = await tc.extractTar(tarball);
const installedVersion = await determineInstalledVersion(cliPath);
core.setOutput("version", installedVersion);
core.addPath(cliPath);
if (semver.order(version, REGISTRY_VERSION) >= 0) {
core.exportVariable(CLI_CONFIG_REGISTRY, "ghcr.io");
if (version.toLowerCase() === "latest" || semver.compare(version, REGISTRY_VERSION) >= 0) {
core.exportVariable(CLI_CONFIG_REGISTRY, getGhcrImageRegistry());
}
await restoreDockerImageCache(installedVersion, getImageRegistry());
} catch (error) {
core.setFailed(error instanceof Error ? error.message : String(error));
}

17
src/post.ts Normal file
View File

@@ -0,0 +1,17 @@
import * as core from "@actions/core";
import { fileURLToPath } from "node:url";
import { saveDockerImageCache } from "./cache";
export async function run(): Promise<void> {
try {
await saveDockerImageCache();
} catch (error) {
core.warning(
`Supabase Docker image cache post step failed: ${error instanceof Error ? error.message : String(error)}`,
);
}
}
if (process.argv[1] === fileURLToPath(import.meta.url)) {
await run();
}

View File

@@ -1,4 +1,7 @@
{
"extends": "@tsconfig/bun/tsconfig.json",
"compilerOptions": {
"types": ["bun", "node"]
},
"include": ["src"]
}