diff --git a/.github/workflows/adr-governance.yml b/.github/workflows/adr-governance.yml new file mode 100644 index 0000000..7afac4c --- /dev/null +++ b/.github/workflows/adr-governance.yml @@ -0,0 +1,34 @@ +name: ADR Governance + +on: + pull_request: + paths: + - "docs/adr/**" + - ".adr-kit.yaml" + - "scripts/adr-governance.py" + - ".github/workflows/adr-governance.yml" + push: + branches: + - main + paths: + - "docs/adr/**" + - ".adr-kit.yaml" + - "scripts/adr-governance.py" + - ".github/workflows/adr-governance.yml" + +permissions: + contents: read + +jobs: + adr-governance: + name: Check ADR governance + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Check ADR governance + run: python3 scripts/adr-governance.py diff --git a/.github/workflows/deploy-worker-preview.yml b/.github/workflows/deploy-worker-preview.yml new file mode 100644 index 0000000..870ea21 --- /dev/null +++ b/.github/workflows/deploy-worker-preview.yml @@ -0,0 +1,44 @@ +name: Deploy Worker Preview + +on: + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: deploy-worker-preview + cancel-in-progress: false + +jobs: + deploy-preview: + name: Deploy preview Worker + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' + environment: production + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + cache-dependency-path: worker/package-lock.json + + - name: Install dependencies + working-directory: worker + run: npm ci + + - name: Validate preview config + working-directory: worker + run: npm run assert:preview + + - name: Deploy preview Worker + working-directory: worker + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + run: npm run deploy:preview diff --git a/.github/workflows/deploy-worker-production.yml b/.github/workflows/deploy-worker-production.yml new file mode 100644 index 0000000..87d2fe0 --- /dev/null +++ b/.github/workflows/deploy-worker-production.yml @@ -0,0 +1,44 @@ +name: Deploy Worker Production + +on: + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: deploy-worker-production + cancel-in-progress: false + +jobs: + deploy-production: + name: Deploy production Worker + runs-on: ubuntu-latest + if: github.ref == 'refs/heads/main' + environment: production + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + cache-dependency-path: worker/package-lock.json + + - name: Install dependencies + working-directory: worker + run: npm ci + + - name: Validate production config + working-directory: worker + run: npm run assert:production + + - name: Deploy production Worker + working-directory: worker + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + run: npm run deploy:production diff --git a/.github/workflows/worker-check.yml b/.github/workflows/worker-check.yml new file mode 100644 index 0000000..70f671e --- /dev/null +++ b/.github/workflows/worker-check.yml @@ -0,0 +1,44 @@ +name: Worker Check + +on: + pull_request: + paths: + - "worker/**" + - ".github/workflows/worker-check.yml" + - ".github/workflows/deploy-worker-preview.yml" + - ".github/workflows/deploy-worker-production.yml" + push: + branches: + - main + paths: + - "worker/**" + - ".github/workflows/worker-check.yml" + - ".github/workflows/deploy-worker-preview.yml" + - ".github/workflows/deploy-worker-production.yml" + +permissions: + contents: read + +jobs: + check: + name: Check worker + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + cache-dependency-path: worker/package-lock.json + + - name: Install dependencies + working-directory: worker + run: npm ci + + - name: Check worker + working-directory: worker + run: npm run check diff --git a/README.md b/README.md index 6b14782..ebc3022 100644 --- a/README.md +++ b/README.md @@ -9,6 +9,7 @@ This repository contains: - **llms.txt** - Main index file following the [llms.txt standard](https://llmstxt.org/) - **Markdown documentation** - Clean, comprehensive content about Autonomi's architecture, features, and developer experience - **Page variants** - `.md` versions of key autonomi.com pages for AI consumption +- **Cloudflare Worker config** - Source-controlled Worker code/config in [`worker/`](worker/README.markdown), with manual deployment workflows documented in the Worker runbook ## How It Works @@ -20,6 +21,123 @@ Files appear to live on autonomi.com but are actually served from this GitHub re - Single source of truth - Clean content without HTML wrappers +## Public Serving Policy + +The Cloudflare Worker serves `.md`, `/llms.txt`, and `/llms-full.txt` from this repo at canonical `autonomi.com` URLs, except for internal repository prefixes. + +Public by default: + +- `.md` files outside internal prefixes +- `/llms.txt` +- `/llms-full.txt` + +Internal prefixes not served from GitHub raw content: + +- `/worker/` +- `/.github/` + +To publish machine-readable content, add a `.md` file outside the internal prefixes. To keep operational or repository-internal documentation out of canonical `autonomi.com` serving, place it under an internal prefix or use a non-served extension. See ADR-0006 for the decision record. + +## Publishing and Managing Content + +This repo is for machine-readable, LLM-parsable, AI-optimised content served from the `autonomi.com` domain. + +It provides Markdown/plain-text versions of key `autonomi.com` pages, and any other content the team wants available at stable `autonomi.com` URLs. These files are maintained in GitHub in parallel with the human-readable HTML pages managed in Framer. + +For pages that have a Markdown counterpart in this repo, the `.md` URL is the LLM-friendly version of the page. For example, a human-facing page on `autonomi.com` can have a Markdown counterpart such as `https://autonomi.com/overview.md` when `overview.md` exists in this repository. + +This repo also provides: + +- `llms.txt`: a concise index for AI assistants and crawlers +- `llms-full.txt`: a fuller context file for AI assistants that want more inline detail +- Markdown versions of selected public pages and reference material +- foundational whitepapers in both Markdown and PDF form +- source-controlled Worker code/config for serving the content at `autonomi.com` + +For a normal content update: + +1. Edit the existing public content file, or add a new public `.md` file outside internal prefixes. +2. Update `llms.txt` so it includes every public content URL that should be discoverable. +3. Update `llms-full.txt` so it reflects the current public content set and messaging. +4. Open a PR. +5. Review the Markdown and the final `autonomi.com` URL it will map to. +6. Merge the PR to `main`. +7. Check the live URL after merge if needed. + +After a content PR is merged to `main`, the Cloudflare Worker serves the updated content automatically from GitHub raw content. There is usually no separate deployment step for content-only changes. Served documentation responses use `Cache-Control: public, max-age=300`, so changes may take up to about five minutes to appear at `autonomi.com`. + +When public content is added, removed, renamed, or substantially changed: + +- add new public URLs to `llms.txt` +- remove deleted public URLs from `llms.txt` +- update `llms-full.txt` to match the current content and messaging +- check whether the content should be linked from an existing section or whether a new section is needed +- keep URLs stable where possible, because agents and external tools may cache or reference them + +Public content is served when it matches one of these rules: + +- `.md` files outside internal prefixes +- `/llms.txt` +- `/llms-full.txt` + +Examples: + +- `overview.md` is served at `https://autonomi.com/overview.md` +- `whitepapers/autonomous-network.md` is served at `https://autonomi.com/whitepapers/autonomous-network.md` +- `llms.txt` is served at `https://autonomi.com/llms.txt` +- `llms-full.txt` is served at `https://autonomi.com/llms-full.txt` + +### Whitepapers + +Foundational whitepapers are kept in two forms: + +- Markdown versions for LLM-readable content served from `autonomi.com` +- PDF versions for stable, shareable, human-readable original documents + +When adding or updating a whitepaper: + +1. Keep the Markdown version under `whitepapers/`. +2. Keep the corresponding PDF in the same directory when a PDF counterpart exists. +3. For whitepapers that have a PDF counterpart, link from the Markdown page to the PDF using an explicit GitHub raw URL. +4. Add or update the Markdown URL in `llms.txt`. +5. Update `llms-full.txt` so the whitepaper set and summary remain current. + +The Worker does not currently serve `.pdf` files from `autonomi.com`. Whitepaper PDF links should use explicit GitHub raw URLs, for example: + +`https://raw.githubusercontent.com/maidsafe/autonomi-llm-docs/main/whitepapers/Autonomous-Network.pdf` + +Do not use relative PDF links such as `Autonomous-Network.pdf` from a served Markdown page, because that would resolve against `autonomi.com` and fall through to Framer rather than serving the file from this repo. + +Under the current policy, any `.md` file outside internal prefixes can be served from `autonomi.com`. This includes repo-level and governance Markdown such as `README.md` and `docs/adr/*.md`; those files are intentionally visible as public repository context. + +Operational or maintenance material that should not be published as canonical Autonomi documentation should live under an internal prefix, or use a non-served extension such as `.markdown`. + +Internal prefixes not served from GitHub raw content: + +- `/worker/` +- `/.github/` + +Use internal locations for: + +- Worker source and runbooks +- GitHub workflow files or workflow documentation +- repository-maintenance notes +- operational material that is useful in GitHub but should not appear as public `autonomi.com` documentation + +Changing content is different from changing serving behaviour. + +Content-only changes normally need only a PR merge. Worker or serving-policy changes need additional review and, after merge, a manual Worker deployment. Treat these as serving changes: + +- editing `worker/src/index.js` +- changing Wrangler config +- changing deploy workflows +- adding new public file types beyond `.md`, `/llms.txt`, or `/llms-full.txt` +- serving PDFs from `autonomi.com` instead of GitHub raw URLs +- changing internal prefixes or routing policy +- changing cache behaviour or response headers + +Serving-policy changes should be reflected in Worker tests and checked against ADR-0006. PDF-linking behaviour is documented in ADR-0004 and should be revisited if the Worker is changed to serve PDFs directly from `autonomi.com`. + ## Who This Is For **Primary audience:** AI assistants (Claude, ChatGPT, etc.) helping developers build on Autonomi diff --git a/docs/adr/ADR-0001-adopt-architecture-decision-records.md b/docs/adr/ADR-0001-adopt-architecture-decision-records.md index 1fc921a..8f78062 100644 --- a/docs/adr/ADR-0001-adopt-architecture-decision-records.md +++ b/docs/adr/ADR-0001-adopt-architecture-decision-records.md @@ -1,6 +1,6 @@ # ADR-0001: Adopt Architecture Decision Records -- **Status:** Proposed +- **Status:** Accepted - **Date:** 2026-06-24 - **Decision owners:** Jim Collinson - **Reviewers:** Jim Collinson diff --git a/docs/adr/ADR-0002-serve-docs-via-cloudflare-worker-and-github.md b/docs/adr/ADR-0002-serve-docs-via-cloudflare-worker-and-github.md index 0614f17..9535fd0 100644 --- a/docs/adr/ADR-0002-serve-docs-via-cloudflare-worker-and-github.md +++ b/docs/adr/ADR-0002-serve-docs-via-cloudflare-worker-and-github.md @@ -1,14 +1,14 @@ # ADR-0002: Serve documentation at autonomi.com via a Cloudflare Worker backed by GitHub -- **Status:** Proposed +- **Status:** Accepted - **Date:** 2026-06-24 - **Decision owners:** Jim Collinson - **Reviewers:** Jim Collinson (retrospective — author attestation) - **Supersedes:** none - **Superseded by:** none -- **Related:** ADR-0001 (ADR adoption); ADR-0003 (`llms.txt`); ADR-0004 (PDF serving); planned ADR-0005 (bring Worker source under version control) +- **Related:** ADR-0001 (ADR adoption); ADR-0003 (`llms.txt`); ADR-0004 (PDF serving); ADR-0005 (Worker source management) -> **Retrospective ADR.** This record reconstructs a decision made earlier in development, before the ADR process existed in this repo. It is backfilled and proposed on 2026-06-24 (accepted on merge of this PR), on the basis of the decision owner's attestation and direct inspection of the deployed system; it was not produced by contemporaneous review. +> **Retrospective ADR.** This record reconstructs a decision made earlier in development, before the ADR process existed in this repo. It was backfilled on 2026-06-24 and accepted by the decision owner during PR #6 review, on the basis of the decision owner's attestation and direct inspection of the deployed system; it was not produced by contemporaneous review. ## Context @@ -47,9 +47,11 @@ If the GitHub fetch for a matched path does **not** return OK (e.g. the file doe For interception to work, the relevant `autonomi.com` DNS records must be **Proxied** (orange-cloud) in Cloudflare rather than DNS-only (grey-cloud), so traffic passes through Cloudflare where the Worker can act on it. DNS-only records bypass the Worker entirely and were the cause of early interception failures during development. -### Current management state (as of this record) +### Management state and later refinement -The Worker source is currently authored and edited **only via the Cloudflare web UI**; it is not yet committed to or managed from any Git repository. The source reproduced in discussion is a copy, not a tracked artefact — the deployed Worker is whatever exists in the Cloudflare dashboard. Bringing the Worker under version control in this repo (with the Cloudflare UI demoted to a break-glass fallback) is planned as separate work and will be recorded in its own ADR (ADR-0005). This ADR documents the serving architecture as currently deployed; it does not decide the management approach. +At the time this architecture was first deployed, the Worker source was authored and edited only via the Cloudflare web UI. This ADR records the serving architecture: a Cloudflare Worker in front of `autonomi.com`, GitHub raw content for machine-readable documentation, and Framer fallthrough for normal site traffic. + +Worker source management was decided separately in ADR-0005. ADR-0005 makes this repository the source of truth for Worker source code, Wrangler configuration, deployment workflows, and the operational runbook, with the Cloudflare dashboard reserved for observability, rollback, and break-glass recovery. ## Consequences @@ -68,7 +70,7 @@ The Worker source is currently authored and edited **only via the Cloudflare web - The Worker's routing logic (which paths it intercepts) is an architectural surface that must be kept correct; a mistake can shadow or leak Framer routes. - DNS proxy status is load-bearing: flipping a record to DNS-only silently breaks doc serving. - Two sources of truth for one domain (Framer + GitHub) can confuse contributors who don't know the split. -- **The Worker source is currently unversioned and Cloudflare-UI-only**: no history, no review, no rollback except by hand, and a single point of accidental change. This risk is the motivation for the planned ADR-0005. +- At initial deployment, Worker source was unversioned and Cloudflare-UI-only. ADR-0005 addresses this by moving normal Worker source/config/deploy management into this repository while keeping Cloudflare available for observability, rollback, and break-glass recovery. ### Neutral / Operational @@ -82,7 +84,7 @@ The Worker source is currently authored and edited **only via the Cloudflare web - `autonomi.com/llms.txt` and `autonomi.com/llms-full.txt` resolve to repo content. - Normal site pages still resolve to Framer. - Required `autonomi.com` records are Proxied in Cloudflare (owner-confirmed; not inspectable from the repo). -- Review trigger: any change to the Worker's routing rules, the hosting of the marketing site, the DNS proxy posture, or the move of the Worker under version control (ADR-0005) requires revisiting this ADR. +- Review trigger: any change to the Worker's routing rules, the hosting of the marketing site, the DNS proxy posture, or the Worker source-management model (ADR-0005) requires revisiting this ADR. ## Notes for AI-assisted work diff --git a/worker/.gitignore b/worker/.gitignore new file mode 100644 index 0000000..a933f10 --- /dev/null +++ b/worker/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +.wrangler/ +.dev.vars diff --git a/worker/.prettierignore b/worker/.prettierignore new file mode 100644 index 0000000..504afef --- /dev/null +++ b/worker/.prettierignore @@ -0,0 +1,2 @@ +node_modules/ +package-lock.json diff --git a/worker/README.markdown b/worker/README.markdown new file mode 100644 index 0000000..10285ab --- /dev/null +++ b/worker/README.markdown @@ -0,0 +1,119 @@ +# Autonomi Markdown Proxy Worker + +This directory contains source-controlled Cloudflare Worker code and deployment configuration for the existing production Worker `autonomi-md-proxy`. + +## Current production inventory + +- Worker name: `autonomi-md-proxy` +- Production route: `autonomi.com/*` +- Zone: `autonomi.com` +- Compatibility date: `2025-12-09` +- `workers_dev`: disabled for production +- Connected bindings: none +- Observability: logs enabled with 100% sampling; traces disabled in the confirmed production inventory +- Behaviour: serve public `.md`, `/llms.txt`, and `/llms-full.txt` paths from `maidsafe/autonomi-llm-docs` on GitHub, excluding internal prefixes `/worker/` and `/.github/`, and falling back to Framer for misses and all other paths + +## Local setup + +Use Node.js 22 or newer. + +```sh +cd worker +npm ci +npm run check +``` + +`npm run check` performs formatting, JavaScript syntax, runtime tests, config assertions, and Wrangler dry-run validation for both preview and production configs. It does not deploy. + +## Serving policy + +The public serving policy follows ADR-0006: + +- `.md` files outside internal prefixes are served from GitHub raw content. +- `/llms.txt` and `/llms-full.txt` are served from GitHub raw content. +- `/worker/` and `/.github/` are internal prefixes and fall through to Framer rather than being served from GitHub raw content. + +Keep operational documentation under internal prefixes or use a non-served extension. This runbook uses `.markdown` so it cannot be exposed by older deployed Worker versions that predate the `/worker/` exclusion rule. + +## GitHub secrets + +Manual deploy workflows require these repository, organization, or `production` environment secrets: + +- `CLOUDFLARE_API_TOKEN` +- `CLOUDFLARE_ACCOUNT_ID` + +The Cloudflare API token should be scoped only as broadly as needed for this Worker, including Workers script edit access and route access for the `autonomi.com` zone. + +Because this token can update production Cloudflare resources, both deploy workflows use the GitHub `production` environment approval gate before they can access the token. The workflow files reference that environment, but the environment's required reviewers, admin bypass, and self-review settings are GitHub repository settings outside this source tree. Operators must confirm those settings before the first deploy. + +## Preview deploy + +Preview deploys are manual only and run only from the `main` branch. They use the GitHub `production` environment approval gate because they use the same Cloudflare account credentials as production deploys. + +From GitHub Actions, run **Deploy Worker Preview**. Locally, with Cloudflare credentials exported: + +```sh +cd worker +npm run deploy:preview +``` + +This uses `wrangler.preview.jsonc`, deploys Worker name `autonomi-md-proxy-preview`, enables `workers_dev`, and does not attach a production route. The deploy script asserts that the preview config has no `route` or `routes` before deployment. + +## Production deploy + +Production deploys are manual only and run only from the `main` branch. Merging a PR must not deploy production. + +From GitHub Actions, run **Deploy Worker Production**. The job uses the GitHub `production` environment, so configure and confirm required reviewers/protection there before use. Locally, with Cloudflare credentials exported: + +```sh +cd worker +npm run deploy:production +``` + +This uses `wrangler.jsonc` and deploys Worker name `autonomi-md-proxy` to route `autonomi.com/*`. + +The deploy script asserts the production Worker name and route before deployment. + +## Smoke tests + +After a preview deploy, use the workers.dev URL printed by Wrangler: + +```sh +curl -i "$PREVIEW_URL/llms.txt" +curl -i "$PREVIEW_URL/llms-full.txt" +curl -i "$PREVIEW_URL/overview.md" +curl -i "$PREVIEW_URL/worker/README.md" +curl -i "$PREVIEW_URL/" +``` + +Preview smoke tests validate GitHub proxy paths and non-GitHub-serving behaviour: public documentation paths should be served from GitHub when present, while internal-prefix paths, missing Markdown/LLM paths, and non-matching paths should not return GitHub raw content. The preview Worker is not attached to the production `autonomi.com/*` route, so its workers.dev URL cannot validate that fallthrough reaches Framer. + +After a production deploy or rollback, use the routed `autonomi.com` URLs to validate both GitHub proxy paths and Framer fallthrough: + +```sh +curl -i https://autonomi.com/llms.txt +curl -i https://autonomi.com/llms-full.txt +curl -i https://autonomi.com/overview.md +curl -i https://autonomi.com/worker/README.md +curl -i https://autonomi.com/ +``` + +Expected production/route results: + +- `/llms.txt` and `/llms-full.txt` return `Content-Type: text/plain; charset=utf-8` and `Cache-Control: public, max-age=300` when present in GitHub. +- `.md` paths return `Content-Type: text/markdown; charset=utf-8` and `Cache-Control: public, max-age=300` when present in GitHub. +- Internal-prefix paths such as `/worker/README.md` fall through to Framer rather than serving GitHub raw content. +- Missing markdown/LLM paths and non-matching paths fall through to Framer unchanged. + +## Rollback + +Preferred rollback options: + +1. Re-run **Deploy Worker Production** from the last known-good commit. +2. Use the Cloudflare dashboard Worker deployment rollback for `autonomi-md-proxy`. + +Run the production smoke tests after rollback. + +## Manual Cloudflare zone dependency + +The Browser Integrity Check exception for `/llms.txt`, `/llms-full.txt`, and `.md` paths is existing Cloudflare zone-level configuration. It is intentionally not managed by this first PR. Keep that exception in place manually and track source-controlling it as a follow-up. diff --git a/worker/package-lock.json b/worker/package-lock.json new file mode 100644 index 0000000..397a47c --- /dev/null +++ b/worker/package-lock.json @@ -0,0 +1,1530 @@ +{ + "name": "autonomi-md-proxy-worker", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "autonomi-md-proxy-worker", + "version": "0.1.0", + "devDependencies": { + "jsonc-parser": "3.3.1", + "prettier": "3.8.4", + "wrangler": "4.104.0" + } + }, + "node_modules/@cloudflare/kv-asset-handler": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.5.0.tgz", + "integrity": "sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg==", + "dev": true, + "license": "MIT OR Apache-2.0", + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@cloudflare/unenv-preset": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.16.1.tgz", + "integrity": "sha512-ECxObrMfyTl5bhQf/lZCXwo5G6xX9IAUo+nDMKK4SZ8m4Jvvxp52vilxyySSWh2YTZz8+HQ07qGH/2rEom1vDw==", + "dev": true, + "license": "MIT OR Apache-2.0", + "peerDependencies": { + "unenv": "2.0.0-rc.24", + "workerd": ">1.20260305.0 <2.0.0-0" + }, + "peerDependenciesMeta": { + "workerd": { + "optional": true + } + } + }, + "node_modules/@cloudflare/workerd-darwin-64": { + "version": "1.20260623.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260623.1.tgz", + "integrity": "sha512-MvDoIsRTsUJRzAl1/4hDXL839piyyjCeYatBHWgMc12Go7nHxkgbRih+1GJImEiKACSentu410bOupcutqFbpg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/@cloudflare/workerd-darwin-arm64": { + "version": "1.20260623.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260623.1.tgz", + "integrity": "sha512-sNqHQvHPMOj5/BJOadtEZekRPSG5qQ0/ulC30ZRHRLnmx6tj5O4Wb3Nf0oznnI0pmjXhbv6b7+TOpDkaFMjbBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/@cloudflare/workerd-linux-64": { + "version": "1.20260623.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260623.1.tgz", + "integrity": "sha512-XYTWqTlZlCXqG+Po6awjXtlxw73hb3C39B/PP0sb4H9NI3V0eynq8Q7rXNe7DHJs2pWRfDJihQzpayQvpwf5wQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/@cloudflare/workerd-linux-arm64": { + "version": "1.20260623.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260623.1.tgz", + "integrity": "sha512-PC5UDKA8oQB3Gek/Y+ysovdHNjp55CihOQZd7F9xPwpkv9qTBB0mhyHnfoG2YHtW1bb9CNhuwiThaNxegpE4mg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/@cloudflare/workerd-windows-64": { + "version": "1.20260623.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260623.1.tgz", + "integrity": "sha512-OTHLCVYyN0pEfrajpjjnrGg5zA1GDnpNYmMz3x2ESFtH/oXRODsUQBllP7oJpJvMURF3rXSYwAhMojaftGry8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.1.tgz", + "integrity": "sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz", + "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz", + "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz", + "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz", + "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz", + "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz", + "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz", + "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz", + "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz", + "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz", + "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz", + "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz", + "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz", + "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz", + "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz", + "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz", + "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz", + "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz", + "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz", + "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz", + "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz", + "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz", + "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz", + "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz", + "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz", + "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz", + "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", + "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", + "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@poppinss/colors": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@poppinss/colors/-/colors-4.1.6.tgz", + "integrity": "sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==", + "dev": true, + "license": "MIT", + "dependencies": { + "kleur": "^4.1.5" + } + }, + "node_modules/@poppinss/dumper": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/@poppinss/dumper/-/dumper-0.6.5.tgz", + "integrity": "sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@poppinss/colors": "^4.1.5", + "@sindresorhus/is": "^7.0.2", + "supports-color": "^10.0.0" + } + }, + "node_modules/@poppinss/exception": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@poppinss/exception/-/exception-1.2.3.tgz", + "integrity": "sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sindresorhus/is": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.2.0.tgz", + "integrity": "sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@speed-highlight/core": { + "version": "1.2.17", + "resolved": "https://registry.npmjs.org/@speed-highlight/core/-/core-1.2.17.tgz", + "integrity": "sha512-Z92FwKpCtfaW1V0jTU/fh3QzYEZN8wDwrzRIBoADCJfn4mJCNcJN/XegifX7BDrQ8/h9Xh/JnbyMchL0FqXrkg==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/blake3-wasm": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz", + "integrity": "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==", + "dev": true, + "license": "MIT" + }, + "node_modules/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/error-stack-parser-es": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz", + "integrity": "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/esbuild": { + "version": "0.28.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz", + "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.1", + "@esbuild/android-arm": "0.28.1", + "@esbuild/android-arm64": "0.28.1", + "@esbuild/android-x64": "0.28.1", + "@esbuild/darwin-arm64": "0.28.1", + "@esbuild/darwin-x64": "0.28.1", + "@esbuild/freebsd-arm64": "0.28.1", + "@esbuild/freebsd-x64": "0.28.1", + "@esbuild/linux-arm": "0.28.1", + "@esbuild/linux-arm64": "0.28.1", + "@esbuild/linux-ia32": "0.28.1", + "@esbuild/linux-loong64": "0.28.1", + "@esbuild/linux-mips64el": "0.28.1", + "@esbuild/linux-ppc64": "0.28.1", + "@esbuild/linux-riscv64": "0.28.1", + "@esbuild/linux-s390x": "0.28.1", + "@esbuild/linux-x64": "0.28.1", + "@esbuild/netbsd-arm64": "0.28.1", + "@esbuild/netbsd-x64": "0.28.1", + "@esbuild/openbsd-arm64": "0.28.1", + "@esbuild/openbsd-x64": "0.28.1", + "@esbuild/openharmony-arm64": "0.28.1", + "@esbuild/sunos-x64": "0.28.1", + "@esbuild/win32-arm64": "0.28.1", + "@esbuild/win32-ia32": "0.28.1", + "@esbuild/win32-x64": "0.28.1" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/kleur": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", + "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/miniflare": { + "version": "4.20260623.0", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20260623.0.tgz", + "integrity": "sha512-p2YTeH01jMiMOjO4v9hb/+GJndja5LCxecGOWCaT9F414PRXgdddLDsK6MnqhGBB5tlU/WoBYIG1XZte5pQzOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "0.8.1", + "sharp": "0.34.5", + "undici": "7.28.0", + "workerd": "1.20260623.1", + "ws": "8.21.0", + "youch": "4.1.0-beta.10" + }, + "bin": { + "miniflare": "bootstrap.js" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/prettier": { + "version": "3.8.4", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.4.tgz", + "integrity": "sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", + "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.2", + "semver": "^7.7.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.5", + "@img/sharp-darwin-x64": "0.34.5", + "@img/sharp-libvips-darwin-arm64": "1.2.4", + "@img/sharp-libvips-darwin-x64": "1.2.4", + "@img/sharp-libvips-linux-arm": "1.2.4", + "@img/sharp-libvips-linux-arm64": "1.2.4", + "@img/sharp-libvips-linux-ppc64": "1.2.4", + "@img/sharp-libvips-linux-riscv64": "1.2.4", + "@img/sharp-libvips-linux-s390x": "1.2.4", + "@img/sharp-libvips-linux-x64": "1.2.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", + "@img/sharp-libvips-linuxmusl-x64": "1.2.4", + "@img/sharp-linux-arm": "0.34.5", + "@img/sharp-linux-arm64": "0.34.5", + "@img/sharp-linux-ppc64": "0.34.5", + "@img/sharp-linux-riscv64": "0.34.5", + "@img/sharp-linux-s390x": "0.34.5", + "@img/sharp-linux-x64": "0.34.5", + "@img/sharp-linuxmusl-arm64": "0.34.5", + "@img/sharp-linuxmusl-x64": "0.34.5", + "@img/sharp-wasm32": "0.34.5", + "@img/sharp-win32-arm64": "0.34.5", + "@img/sharp-win32-ia32": "0.34.5", + "@img/sharp-win32-x64": "0.34.5" + } + }, + "node_modules/supports-color": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/undici": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.28.0.tgz", + "integrity": "sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/unenv": { + "version": "2.0.0-rc.24", + "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.24.tgz", + "integrity": "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pathe": "^2.0.3" + } + }, + "node_modules/workerd": { + "version": "1.20260623.1", + "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260623.1.tgz", + "integrity": "sha512-9SJsdTSsehhqc26TUJIzyi1XgyYeqFym4hinZnWoAP1BkhEoMQ5Ygz7Xw9T+2ecU+y409JBEScBgWTdZ06mBrg==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "bin": { + "workerd": "bin/workerd" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "@cloudflare/workerd-darwin-64": "1.20260623.1", + "@cloudflare/workerd-darwin-arm64": "1.20260623.1", + "@cloudflare/workerd-linux-64": "1.20260623.1", + "@cloudflare/workerd-linux-arm64": "1.20260623.1", + "@cloudflare/workerd-windows-64": "1.20260623.1" + } + }, + "node_modules/wrangler": { + "version": "4.104.0", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.104.0.tgz", + "integrity": "sha512-xCfbg2Oj93Bc7EMryFaSeRGDgV96dzrWoaK5q2q5XLEvumO4mysNP/1MDue0GUozEJAI6Z6vrGyYPLmfET/0sg==", + "dev": true, + "license": "MIT OR Apache-2.0", + "dependencies": { + "@cloudflare/kv-asset-handler": "0.5.0", + "@cloudflare/unenv-preset": "2.16.1", + "blake3-wasm": "2.1.5", + "esbuild": "0.28.1", + "miniflare": "4.20260623.0", + "path-to-regexp": "6.3.0", + "unenv": "2.0.0-rc.24", + "workerd": "1.20260623.1" + }, + "bin": { + "cf-wrangler": "bin/cf-wrangler.js", + "wrangler": "bin/wrangler.js", + "wrangler2": "bin/wrangler.js" + }, + "engines": { + "node": ">=22.0.0" + }, + "optionalDependencies": { + "fsevents": "2.3.3" + }, + "peerDependencies": { + "@cloudflare/workers-types": "^4.20260623.1" + }, + "peerDependenciesMeta": { + "@cloudflare/workers-types": { + "optional": true + } + } + }, + "node_modules/ws": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "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/youch": { + "version": "4.1.0-beta.10", + "resolved": "https://registry.npmjs.org/youch/-/youch-4.1.0-beta.10.tgz", + "integrity": "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@poppinss/colors": "^4.1.5", + "@poppinss/dumper": "^0.6.4", + "@speed-highlight/core": "^1.2.7", + "cookie": "^1.0.2", + "youch-core": "^0.3.3" + } + }, + "node_modules/youch-core": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/youch-core/-/youch-core-0.3.3.tgz", + "integrity": "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@poppinss/exception": "^1.2.2", + "error-stack-parser-es": "^1.0.5" + } + } + } +} diff --git a/worker/package.json b/worker/package.json new file mode 100644 index 0000000..3d4c47f --- /dev/null +++ b/worker/package.json @@ -0,0 +1,24 @@ +{ + "name": "autonomi-md-proxy-worker", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "format": "prettier --write . \"../.github/workflows/*.yml\"", + "format:check": "prettier --check . \"../.github/workflows/*.yml\"", + "check:syntax": "node --check src/index.js", + "test": "node --test", + "assert:preview": "node scripts/assert-config.mjs preview", + "assert:production": "node scripts/assert-config.mjs production", + "check:preview": "wrangler deploy --dry-run --config wrangler.preview.jsonc", + "check:production": "wrangler deploy --dry-run --config wrangler.jsonc", + "check": "npm run format:check && npm run check:syntax && npm test && npm run assert:preview && npm run assert:production && npm run check:preview && npm run check:production", + "deploy:preview": "npm run assert:preview && wrangler deploy --config wrangler.preview.jsonc", + "deploy:production": "npm run assert:production && wrangler deploy --config wrangler.jsonc" + }, + "devDependencies": { + "jsonc-parser": "3.3.1", + "prettier": "3.8.4", + "wrangler": "4.104.0" + } +} diff --git a/worker/scripts/assert-config.mjs b/worker/scripts/assert-config.mjs new file mode 100644 index 0000000..eedbcf4 --- /dev/null +++ b/worker/scripts/assert-config.mjs @@ -0,0 +1,70 @@ +import fs from "node:fs"; +import { parse } from "jsonc-parser"; + +const mode = process.argv[2]; + +if (mode !== "preview" && mode !== "production") { + console.error("Usage: node scripts/assert-config.mjs "); + process.exit(1); +} + +const configPath = + mode === "preview" ? "wrangler.preview.jsonc" : "wrangler.jsonc"; +const configText = fs.readFileSync(configPath, "utf8"); +const parseErrors = []; +const config = parse(configText, parseErrors, { allowTrailingComma: true }); + +if (parseErrors.length > 0) { + console.error(`${configPath}: invalid JSONC`); + process.exit(1); +} + +function assert(condition, message) { + if (!condition) { + console.error(`${configPath}: ${message}`); + process.exit(1); + } +} + +assert(config.main === "src/index.js", "main must remain src/index.js"); +assert( + config.compatibility_date === "2025-12-09", + "compatibility_date must remain 2025-12-09", +); +assert( + config.observability?.enabled === true, + "observability.enabled must remain true", +); +assert( + config.observability?.head_sampling_rate === 1, + "observability.head_sampling_rate must remain 1", +); + +if (mode === "preview") { + assert(config.name === "autonomi-md-proxy-preview", "preview name changed"); + assert(config.workers_dev === true, "preview must use workers_dev"); + assert(config.route === undefined, "preview must not define route"); + assert(config.routes === undefined, "preview must not define routes"); + assert( + config.custom_domain === undefined, + "preview must not define custom_domain", + ); +} else { + assert(config.name === "autonomi-md-proxy", "production name changed"); + assert(config.workers_dev === false, "production must disable workers_dev"); + assert(Array.isArray(config.routes), "production must define routes array"); + assert( + config.routes.length === 1, + "production must define exactly one route", + ); + assert( + config.routes[0].pattern === "autonomi.com/*", + "production route pattern must remain autonomi.com/*", + ); + assert( + config.routes[0].zone_name === "autonomi.com", + "production route zone_name must remain autonomi.com", + ); +} + +console.log(`${configPath}: ${mode} config assertions passed`); diff --git a/worker/src/index.js b/worker/src/index.js new file mode 100644 index 0000000..f7510e3 --- /dev/null +++ b/worker/src/index.js @@ -0,0 +1,117 @@ +const INTERNAL_PREFIXES = ["/worker/", "/.github/"]; +const MAX_POLICY_DECODE_PASSES = 16; +const HAS_PERCENT_ENCODED_BYTE = /%[0-9a-fA-F]{2}/; +const PERCENT_ENCODED_BYTE = /%([0-9a-fA-F]{2})/g; + +function decodePercentEncodedBytes(pathname) { + return pathname.replace(PERCENT_ENCODED_BYTE, (_, hex) => + String.fromCharCode(Number.parseInt(hex, 16)), + ); +} + +function normalizePathname(pathname) { + const segments = []; + + for (const segment of pathname.replaceAll("\\", "/").split("/")) { + if (segment === "" || segment === ".") { + continue; + } + + if (segment === "..") { + segments.pop(); + continue; + } + + segments.push(segment); + } + + const normalized = `/${segments.join("/")}`; + return pathname.endsWith("/") && normalized !== "/" + ? `${normalized}/` + : normalized; +} + +function hasInternalPrefix(pathname) { + return INTERNAL_PREFIXES.some((prefix) => pathname.startsWith(prefix)); +} + +function isInternalCandidate(pathname) { + return ( + hasInternalPrefix(pathname) || + hasInternalPrefix(normalizePathname(pathname)) + ); +} + +function isInternalOrUnsafePath(pathname) { + let current = pathname; + + if (isInternalCandidate(current)) { + return true; + } + + for (let pass = 0; pass < MAX_POLICY_DECODE_PASSES; pass += 1) { + if (!HAS_PERCENT_ENCODED_BYTE.test(current)) { + return false; + } + + current = decodePercentEncodedBytes(current); + + if (isInternalCandidate(current)) { + return true; + } + } + + // Fail closed rather than spending unbounded CPU decoding adversarial paths. + return HAS_PERCENT_ENCODED_BYTE.test(current); +} + +function shouldProxy(pathname) { + const publicDocsPath = + pathname.endsWith(".md") || + pathname === "/llms.txt" || + pathname === "/llms-full.txt"; + + return publicDocsPath && !isInternalOrUnsafePath(pathname); +} + +function contentTypeFor(pathname) { + return pathname.endsWith(".md") + ? "text/markdown; charset=utf-8" + : "text/plain; charset=utf-8"; +} + +export default { + async fetch(request) { + const url = new URL(request.url); + + // Intercept public .md files, llms.txt, and llms-full.txt. + if (shouldProxy(url.pathname)) { + // Try to fetch from GitHub + const githubUrl = `https://raw.githubusercontent.com/maidsafe/autonomi-llm-docs/main${url.pathname}`; + + try { + const ghResponse = await fetch(githubUrl); + + // If file exists in GitHub, serve it + if (ghResponse.ok) { + // Determine content type based on extension + const contentType = contentTypeFor(url.pathname); + + return new Response(ghResponse.body, { + headers: { + "Content-Type": contentType, + "Cache-Control": "public, max-age=300", + }, + }); + } + } catch { + // If GitHub is unreachable, pass through to Framer (graceful fallback) + } + + // If not in GitHub, pass through to Framer (graceful fallback) + } + + // Everything else passes to Framer unchanged + return fetch(request); + }, +}; diff --git a/worker/test/index.test.mjs b/worker/test/index.test.mjs new file mode 100644 index 0000000..c8feea9 --- /dev/null +++ b/worker/test/index.test.mjs @@ -0,0 +1,254 @@ +import assert from "node:assert/strict"; +import test from "node:test"; + +import worker from "../src/index.js"; + +const GITHUB_PREFIX = + "https://raw.githubusercontent.com/maidsafe/autonomi-llm-docs/main"; + +async function withFetchMock(handler, pathname) { + const originalFetch = globalThis.fetch; + const calls = []; + + globalThis.fetch = async (input) => { + const url = typeof input === "string" ? input : input.url; + calls.push(url); + return handler(url, input); + }; + + try { + const response = await worker.fetch( + new Request(`https://autonomi.com${pathname}`), + ); + return { calls, response }; + } finally { + globalThis.fetch = originalFetch; + } +} + +function fallbackResponse(url) { + return new Response(`fallback:${new URL(url).pathname}`, { + status: 203, + }); +} + +function encodeRepeatedly(value, times) { + let encoded = value; + + for (let index = 0; index < times; index += 1) { + encoded = encodeURIComponent(encoded); + } + + return encoded; +} + +test("proxies public markdown paths from GitHub", async () => { + const { calls, response } = await withFetchMock((url) => { + assert.equal(url, `${GITHUB_PREFIX}/overview.md`); + return new Response("overview", { status: 200 }); + }, "/overview.md"); + + assert.deepEqual(calls, [`${GITHUB_PREFIX}/overview.md`]); + assert.equal(response.status, 200); + assert.equal(await response.text(), "overview"); + assert.equal( + response.headers.get("Content-Type"), + "text/markdown; charset=utf-8", + ); + assert.equal(response.headers.get("Cache-Control"), "public, max-age=300"); +}); + +test("proxies llms.txt as plain text", async () => { + const { response } = await withFetchMock((url) => { + assert.equal(url, `${GITHUB_PREFIX}/llms.txt`); + return new Response("llms", { status: 200 }); + }, "/llms.txt"); + + assert.equal(await response.text(), "llms"); + assert.equal( + response.headers.get("Content-Type"), + "text/plain; charset=utf-8", + ); + assert.equal(response.headers.get("Cache-Control"), "public, max-age=300"); +}); + +test("proxies llms-full.txt as plain text", async () => { + const { response } = await withFetchMock((url) => { + assert.equal(url, `${GITHUB_PREFIX}/llms-full.txt`); + return new Response("llms-full", { status: 200 }); + }, "/llms-full.txt"); + + assert.equal(await response.text(), "llms-full"); + assert.equal( + response.headers.get("Content-Type"), + "text/plain; charset=utf-8", + ); + assert.equal(response.headers.get("Cache-Control"), "public, max-age=300"); +}); + +test("falls through for worker internal markdown paths", async () => { + const { calls, response } = await withFetchMock( + fallbackResponse, + "/worker/README.md", + ); + + assert.deepEqual(calls, ["https://autonomi.com/worker/README.md"]); + assert.equal(response.status, 203); + assert.equal(await response.text(), "fallback:/worker/README.md"); +}); + +test("falls through for GitHub workflow markdown paths", async () => { + const { calls, response } = await withFetchMock( + fallbackResponse, + "/.github/PULL_REQUEST_TEMPLATE.md", + ); + + assert.deepEqual(calls, [ + "https://autonomi.com/.github/PULL_REQUEST_TEMPLATE.md", + ]); + assert.equal(response.status, 203); + assert.equal( + await response.text(), + "fallback:/.github/PULL_REQUEST_TEMPLATE.md", + ); +}); + +test("falls through for encoded internal-prefix bypass attempts", async (t) => { + const cases = [ + ["encoded worker slash", "/worker%2FREADME.md"], + ["encoded worker backslash", "/worker%5CREADME.md"], + ["encoded worker letters", "/%77orker/README.md"], + ["encoded .github dot", "/%2Egithub/PULL_REQUEST_TEMPLATE.md"], + ["encoded .github backslash", "/.github%5CPULL_REQUEST_TEMPLATE.md"], + ["encoded .github letters", "/.%67ithub/PULL_REQUEST_TEMPLATE.md"], + ["encoded dot segment to worker", "/public%2F..%2Fworker%2FREADME.md"], + [ + "encoded backslash dot segment to worker", + "/public%5C..%5Cworker%5CREADME.md", + ], + [ + "encoded dot segment to .github", + "/public%2F..%2F.github%2FPULL_REQUEST_TEMPLATE.md", + ], + [ + "encoded backslash dot segment to .github", + "/public%5C..%5C.github%5CPULL_REQUEST_TEMPLATE.md", + ], + ]; + + for (const [name, pathname] of cases) { + await t.test(name, async () => { + const requestUrl = new URL(`https://autonomi.com${pathname}`).href; + const { calls, response } = await withFetchMock((url) => { + assert.ok( + !url.startsWith(GITHUB_PREFIX), + `encoded internal path fetched GitHub raw: ${url}`, + ); + + return fallbackResponse(url); + }, pathname); + + assert.deepEqual(calls, [requestUrl]); + assert.equal(response.status, 203); + assert.equal( + await response.text(), + `fallback:${new URL(requestUrl).pathname}`, + ); + }); + } +}); + +test("falls through for repeatedly encoded internal-prefix bypass attempts", async (t) => { + const cases = [ + ["deeply encoded worker letters", "/%252525252577orker/README.md"], + [ + "deeply encoded .github dot", + "/%25252525252Egithub/PULL_REQUEST_TEMPLATE.md", + ], + ["deeply encoded worker slash", "/worker%25252525252FREADME.md"], + [ + "deeply encoded dot segment to worker", + "/public%25252525252F..%25252525252Fworker%25252525252FREADME.md", + ], + [ + "deeply encoded dot segment to .github", + "/public%25252525252F..%25252525252F.github%25252525252FPULL_REQUEST_TEMPLATE.md", + ], + ]; + + for (const [name, pathname] of cases) { + await t.test(name, async () => { + const requestUrl = new URL(`https://autonomi.com${pathname}`).href; + const { calls, response } = await withFetchMock((url) => { + assert.ok( + !url.startsWith(GITHUB_PREFIX), + `deeply encoded internal path fetched GitHub raw: ${url}`, + ); + + return fallbackResponse(url); + }, pathname); + + assert.deepEqual(calls, [requestUrl]); + assert.equal(response.status, 203); + assert.equal( + await response.text(), + `fallback:${new URL(requestUrl).pathname}`, + ); + }); + } +}); + +test("falls through for over-budget repeated percent encoding", async () => { + const repeatedEncodedLetter = encodeRepeatedly("%70", 16); + const pathname = `/${repeatedEncodedLetter}ublic.md`; + const requestUrl = new URL(`https://autonomi.com${pathname}`).href; + const { calls, response } = await withFetchMock((url) => { + assert.ok( + !url.startsWith(GITHUB_PREFIX), + `over-budget encoded path fetched GitHub raw: ${url}`, + ); + + return fallbackResponse(url); + }, pathname); + + assert.deepEqual(calls, [requestUrl]); + assert.equal(response.status, 203); + assert.equal( + await response.text(), + `fallback:${new URL(requestUrl).pathname}`, + ); +}); + +test("falls through when GitHub returns a miss", async () => { + const { calls, response } = await withFetchMock((url) => { + if (url === `${GITHUB_PREFIX}/missing.md`) { + return new Response("not found", { status: 404 }); + } + + return fallbackResponse(url); + }, "/missing.md"); + + assert.deepEqual(calls, [ + `${GITHUB_PREFIX}/missing.md`, + "https://autonomi.com/missing.md", + ]); + assert.equal(response.status, 203); + assert.equal(await response.text(), "fallback:/missing.md"); +}); + +test("falls through when GitHub fetch throws", async () => { + const { calls, response } = await withFetchMock((url) => { + if (url === `${GITHUB_PREFIX}/transient.md`) { + throw new Error("upstream unavailable"); + } + + return fallbackResponse(url); + }, "/transient.md"); + + assert.deepEqual(calls, [ + `${GITHUB_PREFIX}/transient.md`, + "https://autonomi.com/transient.md", + ]); + assert.equal(response.status, 203); + assert.equal(await response.text(), "fallback:/transient.md"); +}); diff --git a/worker/wrangler.jsonc b/worker/wrangler.jsonc new file mode 100644 index 0000000..51be318 --- /dev/null +++ b/worker/wrangler.jsonc @@ -0,0 +1,17 @@ +{ + "$schema": "./node_modules/wrangler/config-schema.json", + "name": "autonomi-md-proxy", + "main": "src/index.js", + "compatibility_date": "2025-12-09", + "workers_dev": false, + "routes": [ + { + "pattern": "autonomi.com/*", + "zone_name": "autonomi.com", + }, + ], + "observability": { + "enabled": true, + "head_sampling_rate": 1, + }, +} diff --git a/worker/wrangler.preview.jsonc b/worker/wrangler.preview.jsonc new file mode 100644 index 0000000..30a3638 --- /dev/null +++ b/worker/wrangler.preview.jsonc @@ -0,0 +1,11 @@ +{ + "$schema": "./node_modules/wrangler/config-schema.json", + "name": "autonomi-md-proxy-preview", + "main": "src/index.js", + "compatibility_date": "2025-12-09", + "workers_dev": true, + "observability": { + "enabled": true, + "head_sampling_rate": 1, + }, +}