diff --git a/docs/architecture/dependencies.md b/docs/architecture/dependencies.md index d0e718d9..bf910f01 100644 --- a/docs/architecture/dependencies.md +++ b/docs/architecture/dependencies.md @@ -38,6 +38,8 @@ Two constraints bound every choice: | parsers | `sax` | 1.6.1 | Streaming XML for the read-only XLSX reader. Checked 2026-08-04: BlueOak-1.0.0, **zero dependencies**, 80 KB unpacked, no native code, no post-install script, published 2026-07-24, 85.5M weekly downloads. It is here because **no maintained XLSX library met the requirements**: the capable one is unmaintained and ships a permanent advisory, the maintained one cannot return formula text. Streaming matters twice over: it is what keeps the 500,000-row envelope at 85 MB rather than 297 MB, and refusing a `DOCTYPE` at the parser is the entity-expansion defence section 20.9 asks for. The reasoning and the measurements are in [`adr-xlsx-parser.md`](./adr-xlsx-parser.md). | | runtime | `hono` | 4.12.33 | The HTTP surface. Chosen in architecture 8.5 because the same route handlers run on Node and on a Worker, which is what makes the Phase 6 projection a configuration change rather than a rewrite. Checked 2026-08-03: MIT, zero runtime dependencies, 1.36 MB unpacked, no native code, no post-install script, last release three days earlier. The zero-dependency part matters most: an HTTP framework is the usual place a transitive tree arrives. | +| deploy-cloudflare (**dev only**) | `wrangler`, `miniflare` | 4.119.0, 5.20260801.0-alpha | Running a Worker with D1 and R2 bindings **locally**, so the Phase 6 projection is built and tested without a Cloudflare account. Checked 2026-08-06: MIT/Apache-2.0. Dev-only is the whole justification: invariant 7 constrains what a *user* installs, and neither ever reaches a published package. `workerd` is native and ships a post-install script, declined in `pnpm-workspace.yaml` exactly as esbuild's is, because its binary arrives through `@cloudflare/workerd-` optional dependencies instead; verified by starting a Worker with live bindings with the script refused. Admitting these is what narrowed `check:no-native` to the published closure (#256), so `packages/deploy-cloudflare/test/emulator.test.ts` exists to prove the thing that bought is real. Miniflare's alpha is the version pairing with this wrangler; it is a test harness, so an alpha costs a broken test rather than a broken build. | + `node:sqlite`, `node:crypto` and `node:zlib` are used directly and are deliberately not dependencies. That is the reason the supported Node floor is 24.15: see [`docs/compatibility/sqlite-fts5.md`](../compatibility/sqlite-fts5.md). @@ -45,7 +47,13 @@ dependencies. That is the reason the supported Node floor is 24.15: see ## Adding one Before adding a dependency, check and record: last release, open CVEs, licence, install -weight, whether it ships native code, and whether it runs a post-install script. Then add a +weight, whether it ships native code, and whether it runs a post-install script. + +**Native code is judged by who installs it.** `check:no-native` guards the production closure of +the published packages, which is what a user gets. A dev-only tool that ships a binary is +allowed, and a published package that takes one is not, whatever it is for. The guard's own +tests are in `tools/arch/test/no-native.test.ts`, and the assertion that matters there is the +mutation: declare a native dependency on a published package and watch it fail. Then add a row above with the capability it provides, not the problem it solves in general. If an existing dependency or a Node builtin can do the job, that is the answer. diff --git a/package.json b/package.json index b59fa609..73864967 100644 --- a/package.json +++ b/package.json @@ -52,6 +52,7 @@ "@types/node": "24.13.3", "typescript": "7.0.2", "vitest": "4.1.10", + "wrangler": "4.119.0", "zod": "4.4.3" } } diff --git a/packages/deploy-cloudflare/package.json b/packages/deploy-cloudflare/package.json index f32f08ed..a38312db 100644 --- a/packages/deploy-cloudflare/package.json +++ b/packages/deploy-cloudflare/package.json @@ -20,7 +20,8 @@ ], "scripts": { "build": "tsc -b", - "clean": "rm -rf dist *.tsbuildinfo" + "clean": "rm -rf dist *.tsbuildinfo", + "test": "vitest run" }, "dependencies": { "@lorepack/core": "workspace:*" @@ -29,5 +30,8 @@ "type": "git", "url": "git+https://github.com/burakdede/lorepack.git", "directory": "packages/deploy-cloudflare" + }, + "devDependencies": { + "miniflare": "5.20260801.0-alpha" } } diff --git a/packages/deploy-cloudflare/test/emulator.test.ts b/packages/deploy-cloudflare/test/emulator.test.ts new file mode 100644 index 00000000..03bb4690 --- /dev/null +++ b/packages/deploy-cloudflare/test/emulator.test.ts @@ -0,0 +1,67 @@ +import { Miniflare } from 'miniflare'; +import { describe, expect, it } from 'vitest'; + +/** + * The emulator this phase is built against actually runs (#86). + * + * Phase 6 projects a build onto a Worker with D1 and R2 bindings, and every later ticket assumes + * that can be exercised locally. This is the one test that proves the assumption rather than + * inheriting it, so a broken environment says so plainly instead of surfacing as a confusing + * failure inside a projection test. + * + * It is deliberately about the *environment* rather than about Lorepack, and it is the reason + * the emulator is allowed to be a dependency at all: `check:no-native` was narrowed to the + * published closure for this (#256), so the thing it bought had better be verified. + */ + +describe('the Cloudflare emulator', () => { + it('runs a Worker with a working D1 binding and R2 bucket', async () => { + const mf = new Miniflare({ + modules: true, + script: `export default { async fetch(request, env) { + await env.DB.exec("CREATE TABLE IF NOT EXISTS probe (value TEXT)"); + await env.DB.prepare("INSERT INTO probe (value) VALUES (?)").bind("from-d1").run(); + const row = await env.DB.prepare("SELECT value FROM probe LIMIT 1").first(); + await env.BUCKET.put("probe", "from-r2"); + const object = await env.BUCKET.get("probe"); + return Response.json({ d1: row.value, r2: await object.text() }); + }}`, + d1Databases: { DB: 'probe-db' }, + r2Buckets: { BUCKET: 'probe-bucket' }, + }); + + try { + const response = await mf.dispatchFetch('http://localhost/'); + // Both bindings in one request: a Worker that starts and cannot reach its storage is not + // an environment this phase can be built on. + expect(await response.json()).toEqual({ d1: 'from-d1', r2: 'from-r2' }); + } finally { + await mf.dispose(); + } + }, 120_000); + + it('gives each database its own storage, so a candidate cannot leak into the active build', async () => { + const script = `export default { async fetch(request, env) { + await env.DB.exec("CREATE TABLE IF NOT EXISTS t (v TEXT)"); + const url = new URL(request.url); + if (url.searchParams.has('write')) { + await env.DB.prepare("INSERT INTO t (v) VALUES (?)").bind("x").run(); + } + const row = await env.DB.prepare("SELECT count(*) AS n FROM t").first(); + return Response.json({ n: row.n }); + }}`; + + const one = new Miniflare({ modules: true, script, d1Databases: { DB: 'one' } }); + const two = new Miniflare({ modules: true, script, d1Databases: { DB: 'two' } }); + try { + await one.dispatchFetch('http://localhost/?write=1'); + expect(await (await one.dispatchFetch('http://localhost/')).json()).toEqual({ n: 1 }); + // The isolation that makes testing a *candidate* projection meaningful rather than + // incidental: writing one database must be invisible to the other. + expect(await (await two.dispatchFetch('http://localhost/')).json()).toEqual({ n: 0 }); + } finally { + await one.dispose(); + await two.dispose(); + } + }, 120_000); +}); diff --git a/packages/deploy-cloudflare/vitest.config.ts b/packages/deploy-cloudflare/vitest.config.ts new file mode 100644 index 00000000..6b39bceb --- /dev/null +++ b/packages/deploy-cloudflare/vitest.config.ts @@ -0,0 +1,5 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { name: 'deploy-cloudflare', include: ['test/**/*.test.ts'] }, +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 19132bcb..14b4cea1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -28,7 +28,10 @@ importers: version: 7.0.2 vitest: specifier: 4.1.10 - version: 4.1.10(@types/node@24.13.3)(jsdom@27.0.0)(vite@8.2.0(@types/node@24.13.3)(yaml@2.9.0)) + version: 4.1.10(@types/node@24.13.3)(jsdom@27.0.0(supports-color@10.2.2))(vite@8.2.0(@types/node@24.13.3)(esbuild@0.28.1)(yaml@2.9.0)) + wrangler: + specifier: 4.119.0 + version: 4.119.0 zod: specifier: 4.4.3 version: 4.4.3 @@ -74,10 +77,10 @@ importers: version: 19.2.0(@types/react@19.2.0) '@vitejs/plugin-react': specifier: 5.0.4 - version: 5.0.4(vite@7.1.9(@types/node@24.13.3)(lightningcss@1.33.0)(yaml@2.9.0)) + version: 5.0.4(supports-color@10.2.2)(vite@7.1.9(@types/node@24.13.3)(lightningcss@1.33.0)(yaml@2.9.0)) jsdom: specifier: 27.0.0 - version: 27.0.0 + version: 27.0.0(supports-color@10.2.2) vite: specifier: 7.1.9 version: 7.1.9(@types/node@24.13.3)(lightningcss@1.33.0)(yaml@2.9.0) @@ -205,6 +208,10 @@ importers: '@lorepack/core': specifier: workspace:* version: link:../core + devDependencies: + miniflare: + specifier: 5.20260801.0-alpha + version: 5.20260801.0-alpha packages/mcp: dependencies: @@ -238,7 +245,7 @@ importers: version: 1.12.0 mdast-util-from-markdown: specifier: 2.0.2 - version: 2.0.2 + version: 2.0.2(supports-color@10.2.2) pdfjs-dist: specifier: 6.2.108 version: 6.2.108 @@ -247,13 +254,13 @@ importers: version: 9.0.1 remark-frontmatter: specifier: 5.0.0 - version: 5.0.0 + version: 5.0.0(supports-color@10.2.2) remark-gfm: specifier: 4.0.1 - version: 4.0.1 + version: 4.0.1(supports-color@10.2.2) remark-parse: specifier: 11.0.0 - version: 11.0.0 + version: 11.0.0(supports-color@10.2.2) sax: specifier: 1.6.1 version: 1.6.1 @@ -569,6 +576,53 @@ packages: '@changesets/write@0.4.0': resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} + '@cloudflare/kv-asset-handler@0.5.0': + resolution: {integrity: sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg==} + engines: {node: '>=22.0.0'} + + '@cloudflare/unenv-preset@2.16.1': + resolution: {integrity: sha512-ECxObrMfyTl5bhQf/lZCXwo5G6xX9IAUo+nDMKK4SZ8m4Jvvxp52vilxyySSWh2YTZz8+HQ07qGH/2rEom1vDw==} + peerDependencies: + unenv: 2.0.0-rc.24 + workerd: '>1.20260305.0 <2.0.0-0' + peerDependenciesMeta: + workerd: + optional: true + + '@cloudflare/workerd-darwin-64@1.20260801.1': + resolution: {integrity: sha512-wuJWbXpKvncJi1P0GKS+iYpN5tHdb7JPJJ/+6ZQe8zzovHHVMkLJPNBsgWpqeUhpM3g9qTwEKd2rglNKejuh5A==} + engines: {node: '>=16'} + cpu: [x64] + os: [darwin] + + '@cloudflare/workerd-darwin-arm64@1.20260801.1': + resolution: {integrity: sha512-kwoZiTpnhNrF3+APx84Q/oAqvJ3sU9yefGagwm/ASaH/2W19x0vghkW/r4qCoHCK0WW7EPugZ+aXjgPMRtlq1Q==} + engines: {node: '>=16'} + cpu: [arm64] + os: [darwin] + + '@cloudflare/workerd-linux-64@1.20260801.1': + resolution: {integrity: sha512-r0vAxCZH+Jih9Unm1yoyiByPNWNgawcKciOHDm5Q37ZVGOkKLsT9AtLe3yLSaul76WrKqtf+JP2n0WW32VBLJg==} + engines: {node: '>=16'} + cpu: [x64] + os: [linux] + + '@cloudflare/workerd-linux-arm64@1.20260801.1': + resolution: {integrity: sha512-zWgpdZtSozvIgzQNmQiDSF8yEOQJUkRAWNsDXXzAAoy+fCn8YUoSibj3mpFSbZRvbUldeBEaW6SCdC2VEMkhNQ==} + engines: {node: '>=16'} + cpu: [arm64] + os: [linux] + + '@cloudflare/workerd-windows-64@1.20260801.1': + resolution: {integrity: sha512-2oQz+Ksu4ji6e/+ZoYX+tWQEcxAii2p7l+iR8kx48W1llMalaufAsmVxTlk+3/vrM7D3/2c0iK448e0UQTcIMg==} + engines: {node: '>=16'} + cpu: [x64] + os: [win32] + + '@cspotcode/source-map-support@0.8.1': + resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} + engines: {node: '>=12'} + '@csstools/color-helpers@6.1.0': resolution: {integrity: sha512-064IFJdjTfUqnjpCVpMOdbr8FLQBhinbZj6yRv2An2E41O/pLEXqfFRWqGq/SxlE5PEUYTlvWsG2r8MswAVvkg==} engines: {node: '>=20.19.0'} @@ -608,6 +662,9 @@ packages: '@emnapi/core@2.0.0-alpha.3': resolution: {integrity: sha512-AZypUeJ/yByuxyS7BlSNRDOMLMlROYtjYdIAuBmJssVz1UJDSeYxLrdizhXCFYhedC5bqd/ASy8EuNXbVVXp9g==} + '@emnapi/runtime@1.11.3': + resolution: {integrity: sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==} + '@emnapi/runtime@2.0.0-alpha.3': resolution: {integrity: sha512-hFPAhMUjJD9BSyCANEISPOogeXC9Zo9ZQl7L6vKnaVsMkCtzznaW/naYypeyl0Gv5rYfWYsZbpixTMpjDJzQeA==} @@ -620,156 +677,312 @@ packages: cpu: [ppc64] os: [aix] + '@esbuild/aix-ppc64@0.28.1': + resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + '@esbuild/android-arm64@0.25.12': resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} engines: {node: '>=18'} cpu: [arm64] os: [android] + '@esbuild/android-arm64@0.28.1': + resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + '@esbuild/android-arm@0.25.12': resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} engines: {node: '>=18'} cpu: [arm] os: [android] + '@esbuild/android-arm@0.28.1': + resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + '@esbuild/android-x64@0.25.12': resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} engines: {node: '>=18'} cpu: [x64] os: [android] + '@esbuild/android-x64@0.28.1': + resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + '@esbuild/darwin-arm64@0.25.12': resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] + '@esbuild/darwin-arm64@0.28.1': + resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + '@esbuild/darwin-x64@0.25.12': resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} engines: {node: '>=18'} cpu: [x64] os: [darwin] + '@esbuild/darwin-x64@0.28.1': + resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + '@esbuild/freebsd-arm64@0.25.12': resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] + '@esbuild/freebsd-arm64@0.28.1': + resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + '@esbuild/freebsd-x64@0.25.12': resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} engines: {node: '>=18'} cpu: [x64] os: [freebsd] + '@esbuild/freebsd-x64@0.28.1': + resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + '@esbuild/linux-arm64@0.25.12': resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} engines: {node: '>=18'} cpu: [arm64] os: [linux] + '@esbuild/linux-arm64@0.28.1': + resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + '@esbuild/linux-arm@0.25.12': resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} engines: {node: '>=18'} cpu: [arm] os: [linux] + '@esbuild/linux-arm@0.28.1': + resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + '@esbuild/linux-ia32@0.25.12': resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} engines: {node: '>=18'} cpu: [ia32] os: [linux] + '@esbuild/linux-ia32@0.28.1': + resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + '@esbuild/linux-loong64@0.25.12': resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} engines: {node: '>=18'} cpu: [loong64] os: [linux] + '@esbuild/linux-loong64@0.28.1': + resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + '@esbuild/linux-mips64el@0.25.12': resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] + '@esbuild/linux-mips64el@0.28.1': + resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + '@esbuild/linux-ppc64@0.25.12': resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} engines: {node: '>=18'} cpu: [ppc64] os: [linux] + '@esbuild/linux-ppc64@0.28.1': + resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + '@esbuild/linux-riscv64@0.25.12': resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} engines: {node: '>=18'} cpu: [riscv64] os: [linux] + '@esbuild/linux-riscv64@0.28.1': + resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + '@esbuild/linux-s390x@0.25.12': resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} engines: {node: '>=18'} cpu: [s390x] os: [linux] + '@esbuild/linux-s390x@0.28.1': + resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + '@esbuild/linux-x64@0.25.12': resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} engines: {node: '>=18'} cpu: [x64] os: [linux] + '@esbuild/linux-x64@0.28.1': + resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + '@esbuild/netbsd-arm64@0.25.12': resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] + '@esbuild/netbsd-arm64@0.28.1': + resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + '@esbuild/netbsd-x64@0.25.12': resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} engines: {node: '>=18'} cpu: [x64] os: [netbsd] + '@esbuild/netbsd-x64@0.28.1': + resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + '@esbuild/openbsd-arm64@0.25.12': resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] + '@esbuild/openbsd-arm64@0.28.1': + resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + '@esbuild/openbsd-x64@0.25.12': resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] + '@esbuild/openbsd-x64@0.28.1': + resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + '@esbuild/openharmony-arm64@0.25.12': resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} engines: {node: '>=18'} cpu: [arm64] os: [openharmony] + '@esbuild/openharmony-arm64@0.28.1': + resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + '@esbuild/sunos-x64@0.25.12': resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} engines: {node: '>=18'} cpu: [x64] os: [sunos] + '@esbuild/sunos-x64@0.28.1': + resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + '@esbuild/win32-arm64@0.25.12': resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} engines: {node: '>=18'} cpu: [arm64] os: [win32] + '@esbuild/win32-arm64@0.28.1': + resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + '@esbuild/win32-ia32@0.25.12': resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} engines: {node: '>=18'} cpu: [ia32] os: [win32] + '@esbuild/win32-ia32@0.28.1': + resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + '@esbuild/win32-x64@0.25.12': resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} engines: {node: '>=18'} cpu: [x64] os: [win32] + '@esbuild/win32-x64@0.28.1': + resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + '@fontsource/ibm-plex-mono@5.2.6': resolution: {integrity: sha512-LTZJNTcpoT19fmwERZNMDI+ljHueuyhF2Qn+bICJ4Y4hxBLAAoJ2MRsGnyp0QNutW6t/25eyZpvaUK1LDrCo7Q==} @@ -788,6 +1001,168 @@ packages: peerDependencies: hono: ^4 + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.35.2': + resolution: {integrity: sha512-eEieHsMksAW4IiO5NzauESRl2D2qz3J/kwUxUrSfV06A93eEaRfMpHXyUb1mAqrR7i8U9A0GRqE9pjn6u1Jjpg==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.35.2': + resolution: {integrity: sha512-BaktuGPCeHJMARpodR8jK4uKiZrPAy9WrfQW0sdI37clracq8Bp01AYS3SZgi5FS/y5twa9t4+LIuuxQjqRrWw==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [darwin] + + '@img/sharp-freebsd-wasm32@0.35.2': + resolution: {integrity: sha512-YoAxdnd8hPUkvLHd3bWY+YA8nw3xM/RyRopYucNsWHVSan8NLVM3X2volsfoRDcXdUJPg6tXahSd7HXPK7lRnw==} + engines: {node: '>=20.9.0'} + os: [freebsd] + + '@img/sharp-libvips-darwin-arm64@1.3.1': + resolution: {integrity: sha512-4V/M3roRMTYjiwZY9IOVQOE8OyeCxFAkYmyZDrZl51uOKjibm3oeEJ4WAmLxutAfzFbC9jqUiPs2gbnGflH+7g==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.3.1': + resolution: {integrity: sha512-c0/DxItpJv2+dGhgycJBBgotdqruGYDvA79drdh0MD1dFpy7JzJ/PlXwi1H4rFf0eTy8tgbI91aHDnZIceY3jQ==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.3.1': + resolution: {integrity: sha512-JznefmcK9j1JKPz8AkQDh89kjojubyfOasWBPKfzMIhPwsgDy9evpE/naJTXXXmghS1iFwR8u/kTwh/I2/+GCw==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-arm@1.3.1': + resolution: {integrity: sha512-aGGy9aWzXgHBG7HNyQPWorZthlp7+x6fDRoPAQbGO3ThcttuTyKIx3NuSHb6zb4gBNq6/yNn9f1cy9nFKS/Vmg==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-ppc64@1.3.1': + resolution: {integrity: sha512-1EkwGNCZk6iWNCMWqrvdJ+r1j0PT1zIz60CNPhYnJlK/zyeWqlsPZIe+ocBVqPF8k/Ssee/NCk+tE9Ryrko6ng==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-riscv64@1.3.1': + resolution: {integrity: sha512-Ilays+w2bXdnxzxtQdmXR62u8o8GYa3eL4+Gr+1KiE4xperMZUslRaVPJwwPkzlHEjGfXAfRVAa/7CYCtSqsBw==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-s390x@1.3.1': + resolution: {integrity: sha512-VfBwVHQTbRoj4XlpA/KLZ7ltgMpz+4WSejFzQ+GnoImjo1PtEJ59QB2qR1xQEeRPYIkNrPIm2L4cICMvz4C2ew==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-x64@1.3.1': + resolution: {integrity: sha512-+c8ukgwU62DS54nCAjw7keOfHUkmr0B5QHEdcOqRnodF/MNXJbVI8Eopoj4B/0H8Asr65I+A4Amrn7a85/md6A==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linuxmusl-arm64@1.3.1': + resolution: {integrity: sha512-qlKb/pwbkAi1WMsJrYHk7CuDrd12s27U2QnRhFYUoJNrRCmkosMTttuRFat/DDB3IlDm5qE1TJgZ4JDnHX8Ldw==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-libvips-linuxmusl-x64@1.3.1': + resolution: {integrity: sha512-yO21HwoUVLN8Qa+/SBjQLMYwBWAVJjeGPNe+hc0OUeMeifEtJqu5a1c4HayE1nNpDih9y3/KkoltfkDodmKAlg==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-linux-arm64@0.35.2': + resolution: {integrity: sha512-af12Pnd0ZGu2HfP8NayB0kk6eC/lrfbQE6HlR4jD+34wdJ1Vw9TF6TMn6ZvffT+WgqVsl0hRbmNvz2u/23VmwA==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-arm@0.35.2': + resolution: {integrity: sha512-SE4kzF2mepn6z+6E7L6lsV8FzuLL6IPQdyX8ZiwROAG/G8td+hP/m7FsFPwidtrF19gvajuC9l6TxAVcsA4S7A==} + engines: {node: '>=20.9.0'} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-ppc64@0.35.2': + resolution: {integrity: sha512-hYSBm7zcNtDCozCxQHYZJiu63b/bXsgRZuOxCIBZsStMM9Vap47iFHdbX4kCvQsblPB/k+clhELpdQJHQLSHvg==} + engines: {node: '>=20.9.0'} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-riscv64@0.35.2': + resolution: {integrity: sha512-qQt0Kc13+Hoan/Awq/qMSQw3L+RI1NCRPgD5cUJ/1WSSmIoysLOc72jlRM3E0OHN9Yr313jgeQ2T+zW+F03QFA==} + engines: {node: '>=20.9.0'} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-s390x@0.35.2': + resolution: {integrity: sha512-E4fLLfRPzDLlEeDaTzI98OFLcv++WL5ChLLMwPoVd0CIoZQqupBSNbOisPL5am9XsbQ9T84+iiMpUvbFtkunbA==} + engines: {node: '>=20.9.0'} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-x64@0.35.2': + resolution: {integrity: sha512-gi0zFJJRLswfCZmHtJdikXPOc5u7qamSOS3NHedLqLd4W8Q0NqjdBr6TTRIgsfFjqfTsHFgdfvJ9LwqSgcHiAA==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-linuxmusl-arm64@0.35.2': + resolution: {integrity: sha512-siWbOW1u6HFnFLrp0waKyW7VEf7jYvcDWdrXEFa8AkdAQgEvuu5Fz8/Y70w9EeqAdwDtfU012BhEHHaDqvQNzg==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-linuxmusl-x64@0.35.2': + resolution: {integrity: sha512-YBqMMcjDi4QGYiSn4vNOYBhmlC4z5AXqkOUUqI2e0AFA4urNv4ESgOgwNl3K+4etQhha0twXlzeF20bbULm9Yg==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-wasm32@0.35.2': + resolution: {integrity: sha512-Mrv4JQNYVQ94xH+jzZ9r+gowleN8mv2FTgKT+PI6bx5C0G8TdNYndu161pg2i7uoBwxy2ImPMHrJOM2LZef7Bw==} + engines: {node: '>=20.9.0'} + + '@img/sharp-webcontainers-wasm32@0.35.2': + resolution: {integrity: sha512-QNV27pxs9wpApEiCfvHM1RDoP1w1+2KrUWWDPEhEwg+latvOrfuhWrHWZKwdSFwU6jh3myjw/yOCRsUIuOft3g==} + engines: {node: '>=20.9.0'} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.35.2': + resolution: {integrity: sha512-BiVRYc/t6/Vl3e1hBx0hugG4oN9Pydf4fgMSpxTQJmwGUg/YoXTWHiFeRymHfCZzifxu4F4rpk/I67D0LQ20wQ==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.35.2': + resolution: {integrity: sha512-YYEhx9PImCC7T0tI8JDMi4DB9LwLCXCU5OWNYEXAxh5Q1ShKkyC6byxzoBJ3gEFDnH2lQckWuDe70G7mB2XJog==} + engines: {node: ^20.9.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.35.2': + resolution: {integrity: sha512-imoOyBcoM/iiUr4J6VPpCNjPnjvP/Gks95898yB8YqoGGYmHYbOyCuNv9FMhFgtaiHFGbHW8bxKqRV6VjtXThQ==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [win32] + '@inquirer/external-editor@1.0.3': resolution: {integrity: sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==} engines: {node: '>=18'} @@ -813,6 +1188,9 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + '@jridgewell/trace-mapping@0.3.9': + resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} + '@manypkg/find-root@1.1.0': resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} @@ -875,6 +1253,15 @@ packages: engines: {node: '>=20'} hasBin: true + '@poppinss/colors@4.1.6': + resolution: {integrity: sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==} + + '@poppinss/dumper@0.6.5': + resolution: {integrity: sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw==} + + '@poppinss/exception@1.2.3': + resolution: {integrity: sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==} + '@rolldown/binding-android-arm64@1.2.1': resolution: {integrity: sha512-02hOeOSryYxVrOIphmLAsqnCJWxwlzFk+pEt/N/i6OgT3lShHO7xGCU5cpgchRDHboAEbSjzgGh+O/u1GswQmA==} engines: {node: ^20.19.0 || >=22.12.0} @@ -1113,6 +1500,13 @@ packages: cpu: [x64] os: [win32] + '@sindresorhus/is@7.2.0': + resolution: {integrity: sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==} + engines: {node: '>=18'} + + '@speed-highlight/core@1.2.23': + resolution: {integrity: sha512-iRoq6i6JDJP6Mt2A5JaPvzw0pgYHH6k92ij+yXiTrB7T2y9N789aWE3EHWj/5ztlJBokcCBja3iYLVdu5wgnkg==} + '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -1439,6 +1833,9 @@ packages: bidi-js@1.0.3: resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==} + blake3-wasm@2.1.5: + resolution: {integrity: sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==} + bluebird@3.4.7: resolution: {integrity: sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==} @@ -1574,6 +1971,9 @@ packages: resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} engines: {node: '>=0.12'} + error-stack-parser-es@1.0.5: + resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==} + es-module-lexer@2.3.1: resolution: {integrity: sha512-shc1dbU90Yl/xq1QrC7QRtfcwURZuVRfPhZbDoldJ1cn1gzDvBaBWlv0eFolj5+0znnPJz5TXLxsN77X/12KTA==} @@ -1582,6 +1982,11 @@ packages: engines: {node: '>=18'} hasBin: true + esbuild@0.28.1: + resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} + engines: {node: '>=18'} + hasBin: true + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -1809,6 +2214,10 @@ packages: jszip@3.10.1: resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} + kleur@4.1.5: + resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==} + engines: {node: '>=6'} + lie@3.3.0: resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} @@ -2059,6 +2468,10 @@ packages: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} engines: {node: '>=4'} + miniflare@5.20260801.0-alpha: + resolution: {integrity: sha512-AfMrnQbJg81ESsGkGhOkHBtwTqCG+mosR+3GE+qDrhF1I/ieIvgg3ICxNyBvgHBZ3iapy6cysBQHNPykkzrfgw==} + engines: {node: '>=22.0.0'} + mri@1.2.0: resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} engines: {node: '>=4'} @@ -2126,6 +2539,9 @@ packages: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} + path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} @@ -2314,6 +2730,10 @@ packages: setimmediate@1.0.5: resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + sharp@0.35.2: + resolution: {integrity: sha512-FVtFjtBCMiJS6yb5CX7Sop45WFMpeGw6oRKuJnXYgf/f1ms/D7LE/ZUSNxnW7rZ/dbslQWYkoqFHGPaDBtaK4w==} + engines: {node: '>=20.9.0'} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -2371,6 +2791,10 @@ packages: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} + supports-color@10.2.2: + resolution: {integrity: sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==} + engines: {node: '>=18'} + symbol-tree@3.2.4: resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} @@ -2429,6 +2853,13 @@ packages: undici-types@7.18.2: resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} + undici@7.28.0: + resolution: {integrity: sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==} + engines: {node: '>=20.18.1'} + + unenv@2.0.0-rc.24: + resolution: {integrity: sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==} + unified@11.0.5: resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} @@ -2628,6 +3059,33 @@ packages: engines: {node: '>=8'} hasBin: true + workerd@1.20260801.1: + resolution: {integrity: sha512-/g9JGTyqnHtoIscpBHqKD8swE2V4StBs2i69PmLiOhH45OP95jCFICl4F1hKlgN57rqfni5LiCitttoX5OOkVA==} + engines: {node: '>=16'} + hasBin: true + + wrangler@4.119.0: + resolution: {integrity: sha512-ookClf+zly4DTc8pBMNrwGQzZKH8IpIYTXkjDw3XS7ZvBQ5mLYH6eOvfD5BEpk3U63zTbv91WRlo1UeRSKXa0g==} + engines: {node: '>=22.0.0'} + hasBin: true + peerDependencies: + '@cloudflare/workers-types': ^5.20260801.1 + peerDependenciesMeta: + '@cloudflare/workers-types': + optional: true + + ws@8.21.0: + resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} + 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 + ws@8.21.1: resolution: {integrity: sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==} engines: {node: '>=10.0.0'} @@ -2666,6 +3124,12 @@ packages: yazl@3.3.1: resolution: {integrity: sha512-BbETDVWG+VcMUle37k5Fqp//7SDOK2/1+T7X8TD96M3D9G8jK5VLUdQVdVjGi8im7FGkazX7kk5hkU8X4L5Bng==} + youch-core@0.3.3: + resolution: {integrity: sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==} + + youch@4.1.0-beta.10: + resolution: {integrity: sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==} + zod@4.4.3: resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} @@ -2702,20 +3166,20 @@ snapshots: '@babel/compat-data@7.29.7': {} - '@babel/core@7.29.7': + '@babel/core@7.29.7(supports-color@10.2.2)': dependencies: '@babel/code-frame': 7.29.7 '@babel/generator': 7.29.8 '@babel/helper-compilation-targets': 7.29.7 - '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2) '@babel/helpers': 7.29.7 '@babel/parser': 7.29.8 '@babel/template': 7.29.7 - '@babel/traverse': 7.29.8 + '@babel/traverse': 7.29.8(supports-color@10.2.2) '@babel/types': 7.29.8 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 @@ -2740,19 +3204,19 @@ snapshots: '@babel/helper-globals@7.29.7': {} - '@babel/helper-module-imports@7.29.7': + '@babel/helper-module-imports@7.29.7(supports-color@10.2.2)': dependencies: - '@babel/traverse': 7.29.8 + '@babel/traverse': 7.29.8(supports-color@10.2.2) '@babel/types': 7.29.8 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))(supports-color@10.2.2)': dependencies: - '@babel/core': 7.29.7 - '@babel/helper-module-imports': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/helper-module-imports': 7.29.7(supports-color@10.2.2) '@babel/helper-validator-identifier': 7.29.7 - '@babel/traverse': 7.29.8 + '@babel/traverse': 7.29.8(supports-color@10.2.2) transitivePeerDependencies: - supports-color @@ -2773,14 +3237,14 @@ snapshots: dependencies: '@babel/types': 7.29.8 - '@babel/plugin-transform-react-jsx-self@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-react-jsx-self@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-transform-react-jsx-source@7.29.7(@babel/core@7.29.7)': + '@babel/plugin-transform-react-jsx-source@7.29.7(@babel/core@7.29.7(supports-color@10.2.2))': dependencies: - '@babel/core': 7.29.7 + '@babel/core': 7.29.7(supports-color@10.2.2) '@babel/helper-plugin-utils': 7.29.7 '@babel/runtime@7.29.7': {} @@ -2791,7 +3255,7 @@ snapshots: '@babel/parser': 7.29.8 '@babel/types': 7.29.8 - '@babel/traverse@7.29.8': + '@babel/traverse@7.29.8(supports-color@10.2.2)': dependencies: '@babel/code-frame': 7.29.7 '@babel/generator': 7.29.8 @@ -2799,7 +3263,7 @@ snapshots: '@babel/parser': 7.29.8 '@babel/template': 7.29.7 '@babel/types': 7.29.8 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) transitivePeerDependencies: - supports-color @@ -2986,6 +3450,33 @@ snapshots: human-id: 4.2.0 prettier: 2.8.8 + '@cloudflare/kv-asset-handler@0.5.0': {} + + '@cloudflare/unenv-preset@2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260801.1)': + dependencies: + unenv: 2.0.0-rc.24 + optionalDependencies: + workerd: 1.20260801.1 + + '@cloudflare/workerd-darwin-64@1.20260801.1': + optional: true + + '@cloudflare/workerd-darwin-arm64@1.20260801.1': + optional: true + + '@cloudflare/workerd-linux-64@1.20260801.1': + optional: true + + '@cloudflare/workerd-linux-arm64@1.20260801.1': + optional: true + + '@cloudflare/workerd-windows-64@1.20260801.1': + optional: true + + '@cspotcode/source-map-support@0.8.1': + dependencies: + '@jridgewell/trace-mapping': 0.3.9 + '@csstools/color-helpers@6.1.0': {} '@csstools/css-calc@3.3.0(@csstools/css-parser-algorithms@4.0.0(@csstools/css-tokenizer@4.0.0))(@csstools/css-tokenizer@4.0.0)': @@ -3016,6 +3507,11 @@ snapshots: tslib: 2.8.1 optional: true + '@emnapi/runtime@1.11.3': + dependencies: + tslib: 2.8.1 + optional: true + '@emnapi/runtime@2.0.0-alpha.3': dependencies: tslib: 2.8.1 @@ -3029,81 +3525,159 @@ snapshots: '@esbuild/aix-ppc64@0.25.12': optional: true + '@esbuild/aix-ppc64@0.28.1': + optional: true + '@esbuild/android-arm64@0.25.12': optional: true + '@esbuild/android-arm64@0.28.1': + optional: true + '@esbuild/android-arm@0.25.12': optional: true + '@esbuild/android-arm@0.28.1': + optional: true + '@esbuild/android-x64@0.25.12': optional: true + '@esbuild/android-x64@0.28.1': + optional: true + '@esbuild/darwin-arm64@0.25.12': optional: true + '@esbuild/darwin-arm64@0.28.1': + optional: true + '@esbuild/darwin-x64@0.25.12': optional: true + '@esbuild/darwin-x64@0.28.1': + optional: true + '@esbuild/freebsd-arm64@0.25.12': optional: true + '@esbuild/freebsd-arm64@0.28.1': + optional: true + '@esbuild/freebsd-x64@0.25.12': optional: true + '@esbuild/freebsd-x64@0.28.1': + optional: true + '@esbuild/linux-arm64@0.25.12': optional: true + '@esbuild/linux-arm64@0.28.1': + optional: true + '@esbuild/linux-arm@0.25.12': optional: true + '@esbuild/linux-arm@0.28.1': + optional: true + '@esbuild/linux-ia32@0.25.12': optional: true + '@esbuild/linux-ia32@0.28.1': + optional: true + '@esbuild/linux-loong64@0.25.12': optional: true + '@esbuild/linux-loong64@0.28.1': + optional: true + '@esbuild/linux-mips64el@0.25.12': optional: true + '@esbuild/linux-mips64el@0.28.1': + optional: true + '@esbuild/linux-ppc64@0.25.12': optional: true + '@esbuild/linux-ppc64@0.28.1': + optional: true + '@esbuild/linux-riscv64@0.25.12': optional: true + '@esbuild/linux-riscv64@0.28.1': + optional: true + '@esbuild/linux-s390x@0.25.12': optional: true + '@esbuild/linux-s390x@0.28.1': + optional: true + '@esbuild/linux-x64@0.25.12': optional: true + '@esbuild/linux-x64@0.28.1': + optional: true + '@esbuild/netbsd-arm64@0.25.12': optional: true + '@esbuild/netbsd-arm64@0.28.1': + optional: true + '@esbuild/netbsd-x64@0.25.12': optional: true + '@esbuild/netbsd-x64@0.28.1': + optional: true + '@esbuild/openbsd-arm64@0.25.12': optional: true + '@esbuild/openbsd-arm64@0.28.1': + optional: true + '@esbuild/openbsd-x64@0.25.12': optional: true + '@esbuild/openbsd-x64@0.28.1': + optional: true + '@esbuild/openharmony-arm64@0.25.12': optional: true + '@esbuild/openharmony-arm64@0.28.1': + optional: true + '@esbuild/sunos-x64@0.25.12': optional: true + '@esbuild/sunos-x64@0.28.1': + optional: true + '@esbuild/win32-arm64@0.25.12': optional: true + '@esbuild/win32-arm64@0.28.1': + optional: true + '@esbuild/win32-ia32@0.25.12': optional: true + '@esbuild/win32-ia32@0.28.1': + optional: true + '@esbuild/win32-x64@0.25.12': optional: true + '@esbuild/win32-x64@0.28.1': + optional: true + '@fontsource/ibm-plex-mono@5.2.6': {} '@fontsource/ibm-plex-sans@5.2.6': {} @@ -3116,6 +3690,112 @@ snapshots: dependencies: hono: 4.12.33 + '@img/colour@1.1.0': {} + + '@img/sharp-darwin-arm64@0.35.2': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.3.1 + optional: true + + '@img/sharp-darwin-x64@0.35.2': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.3.1 + optional: true + + '@img/sharp-freebsd-wasm32@0.35.2': + dependencies: + '@img/sharp-wasm32': 0.35.2 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.3.1': + optional: true + + '@img/sharp-libvips-darwin-x64@1.3.1': + optional: true + + '@img/sharp-libvips-linux-arm64@1.3.1': + optional: true + + '@img/sharp-libvips-linux-arm@1.3.1': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.3.1': + optional: true + + '@img/sharp-libvips-linux-riscv64@1.3.1': + optional: true + + '@img/sharp-libvips-linux-s390x@1.3.1': + optional: true + + '@img/sharp-libvips-linux-x64@1.3.1': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.3.1': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.3.1': + optional: true + + '@img/sharp-linux-arm64@0.35.2': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.3.1 + optional: true + + '@img/sharp-linux-arm@0.35.2': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.3.1 + optional: true + + '@img/sharp-linux-ppc64@0.35.2': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.3.1 + optional: true + + '@img/sharp-linux-riscv64@0.35.2': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.3.1 + optional: true + + '@img/sharp-linux-s390x@0.35.2': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.3.1 + optional: true + + '@img/sharp-linux-x64@0.35.2': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.3.1 + optional: true + + '@img/sharp-linuxmusl-arm64@0.35.2': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.3.1 + optional: true + + '@img/sharp-linuxmusl-x64@0.35.2': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.3.1 + optional: true + + '@img/sharp-wasm32@0.35.2': + dependencies: + '@emnapi/runtime': 1.11.3 + optional: true + + '@img/sharp-webcontainers-wasm32@0.35.2': + dependencies: + '@img/sharp-wasm32': 0.35.2 + optional: true + + '@img/sharp-win32-arm64@0.35.2': + optional: true + + '@img/sharp-win32-ia32@0.35.2': + optional: true + + '@img/sharp-win32-x64@0.35.2': + optional: true + '@inquirer/external-editor@1.0.3(@types/node@24.13.3)': dependencies: chardet: 2.2.0 @@ -3142,6 +3822,11 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping@0.3.9': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + '@manypkg/find-root@1.1.0': dependencies: '@babel/runtime': 7.29.7 @@ -3212,6 +3897,18 @@ snapshots: dependencies: playwright: 1.62.1 + '@poppinss/colors@4.1.6': + dependencies: + kleur: 4.1.5 + + '@poppinss/dumper@0.6.5': + dependencies: + '@poppinss/colors': 4.1.6 + '@sindresorhus/is': 7.2.0 + supports-color: 10.2.2 + + '@poppinss/exception@1.2.3': {} + '@rolldown/binding-android-arm64@1.2.1': optional: true @@ -3340,6 +4037,10 @@ snapshots: '@rollup/rollup-win32-x64-msvc@4.62.4': optional: true + '@sindresorhus/is@7.2.0': {} + + '@speed-highlight/core@1.2.23': {} + '@standard-schema/spec@1.1.0': {} '@tanstack/query-core@5.90.2': {} @@ -3524,11 +4225,11 @@ snapshots: '@typescript/typescript-win32-x64@7.0.2': optional: true - '@vitejs/plugin-react@5.0.4(vite@7.1.9(@types/node@24.13.3)(lightningcss@1.33.0)(yaml@2.9.0))': + '@vitejs/plugin-react@5.0.4(supports-color@10.2.2)(vite@7.1.9(@types/node@24.13.3)(lightningcss@1.33.0)(yaml@2.9.0))': dependencies: - '@babel/core': 7.29.7 - '@babel/plugin-transform-react-jsx-self': 7.29.7(@babel/core@7.29.7) - '@babel/plugin-transform-react-jsx-source': 7.29.7(@babel/core@7.29.7) + '@babel/core': 7.29.7(supports-color@10.2.2) + '@babel/plugin-transform-react-jsx-self': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) + '@babel/plugin-transform-react-jsx-source': 7.29.7(@babel/core@7.29.7(supports-color@10.2.2)) '@rolldown/pluginutils': 1.0.0-beta.38 '@types/babel__core': 7.20.5 react-refresh: 0.17.0 @@ -3545,13 +4246,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.1 - '@vitest/mocker@4.1.10(vite@8.2.0(@types/node@24.13.3)(yaml@2.9.0))': + '@vitest/mocker@4.1.10(vite@8.2.0(@types/node@24.13.3)(esbuild@0.28.1)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.10 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 8.2.0(@types/node@24.13.3)(yaml@2.9.0) + vite: 8.2.0(@types/node@24.13.3)(esbuild@0.28.1)(yaml@2.9.0) '@vitest/pretty-format@4.1.10': dependencies: @@ -3619,6 +4320,8 @@ snapshots: dependencies: require-from-string: 2.0.2 + blake3-wasm@2.1.5: {} + bluebird@3.4.7: {} braces@3.0.3: @@ -3688,9 +4391,11 @@ snapshots: whatwg-mimetype: 5.0.0 whatwg-url: 15.1.0 - debug@4.4.3: + debug@4.4.3(supports-color@10.2.2): dependencies: ms: 2.1.3 + optionalDependencies: + supports-color: 10.2.2 decimal.js@10.6.0: {} @@ -3731,6 +4436,8 @@ snapshots: entities@6.0.1: {} + error-stack-parser-es@1.0.5: {} + es-module-lexer@2.3.1: {} esbuild@0.25.12: @@ -3762,6 +4469,35 @@ snapshots: '@esbuild/win32-ia32': 0.25.12 '@esbuild/win32-x64': 0.25.12 + esbuild@0.28.1: + 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 + escalade@3.2.0: {} escape-string-regexp@5.0.0: {} @@ -3888,17 +4624,17 @@ snapshots: dependencies: whatwg-encoding: 3.1.1 - http-proxy-agent@7.0.2: + http-proxy-agent@7.0.2(supports-color@10.2.2): dependencies: agent-base: 7.1.4 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) transitivePeerDependencies: - supports-color - https-proxy-agent@7.0.6: + https-proxy-agent@7.0.6(supports-color@10.2.2): dependencies: agent-base: 7.1.4 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) transitivePeerDependencies: - supports-color @@ -3955,15 +4691,15 @@ snapshots: dependencies: argparse: 2.0.1 - jsdom@27.0.0: + jsdom@27.0.0(supports-color@10.2.2): dependencies: '@asamuzakjp/dom-selector': 6.8.1 cssstyle: 5.3.7 data-urls: 6.0.1 decimal.js: 10.6.0 html-encoding-sniffer: 4.0.0 - http-proxy-agent: 7.0.2 - https-proxy-agent: 7.0.6 + http-proxy-agent: 7.0.2(supports-color@10.2.2) + https-proxy-agent: 7.0.6(supports-color@10.2.2) is-potential-custom-element-name: 1.0.1 parse5: 7.3.0 rrweb-cssom: 0.8.0 @@ -3999,6 +4735,8 @@ snapshots: readable-stream: 2.3.8 setimmediate: 1.0.5 + kleur@4.1.5: {} + lie@3.3.0: dependencies: immediate: 3.0.6 @@ -4100,14 +4838,14 @@ snapshots: unist-util-is: 6.0.1 unist-util-visit-parents: 6.0.2 - mdast-util-from-markdown@2.0.2: + mdast-util-from-markdown@2.0.2(supports-color@10.2.2): dependencies: '@types/mdast': 4.0.4 '@types/unist': 3.0.3 decode-named-character-reference: 1.3.0 devlop: 1.1.0 mdast-util-to-string: 4.0.0 - micromark: 4.0.2 + micromark: 4.0.2(supports-color@10.2.2) micromark-util-decode-numeric-character-reference: 2.0.2 micromark-util-decode-string: 2.0.1 micromark-util-normalize-identifier: 2.0.1 @@ -4117,12 +4855,12 @@ snapshots: transitivePeerDependencies: - supports-color - mdast-util-frontmatter@2.0.1: + mdast-util-frontmatter@2.0.1(supports-color@10.2.2): dependencies: '@types/mdast': 4.0.4 devlop: 1.1.0 escape-string-regexp: 5.0.0 - mdast-util-from-markdown: 2.0.2 + mdast-util-from-markdown: 2.0.2(supports-color@10.2.2) mdast-util-to-markdown: 2.1.2 micromark-extension-frontmatter: 2.0.0 transitivePeerDependencies: @@ -4136,51 +4874,51 @@ snapshots: mdast-util-find-and-replace: 3.0.2 micromark-util-character: 2.1.1 - mdast-util-gfm-footnote@2.1.0: + mdast-util-gfm-footnote@2.1.0(supports-color@10.2.2): dependencies: '@types/mdast': 4.0.4 devlop: 1.1.0 - mdast-util-from-markdown: 2.0.2 + mdast-util-from-markdown: 2.0.2(supports-color@10.2.2) mdast-util-to-markdown: 2.1.2 micromark-util-normalize-identifier: 2.0.1 transitivePeerDependencies: - supports-color - mdast-util-gfm-strikethrough@2.0.0: + mdast-util-gfm-strikethrough@2.0.0(supports-color@10.2.2): dependencies: '@types/mdast': 4.0.4 - mdast-util-from-markdown: 2.0.2 + mdast-util-from-markdown: 2.0.2(supports-color@10.2.2) mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color - mdast-util-gfm-table@2.0.0: + mdast-util-gfm-table@2.0.0(supports-color@10.2.2): dependencies: '@types/mdast': 4.0.4 devlop: 1.1.0 markdown-table: 3.0.4 - mdast-util-from-markdown: 2.0.2 + mdast-util-from-markdown: 2.0.2(supports-color@10.2.2) mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color - mdast-util-gfm-task-list-item@2.0.0: + mdast-util-gfm-task-list-item@2.0.0(supports-color@10.2.2): dependencies: '@types/mdast': 4.0.4 devlop: 1.1.0 - mdast-util-from-markdown: 2.0.2 + mdast-util-from-markdown: 2.0.2(supports-color@10.2.2) mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color - mdast-util-gfm@3.1.0: + mdast-util-gfm@3.1.0(supports-color@10.2.2): dependencies: - mdast-util-from-markdown: 2.0.2 + mdast-util-from-markdown: 2.0.2(supports-color@10.2.2) mdast-util-gfm-autolink-literal: 2.0.1 - mdast-util-gfm-footnote: 2.1.0 - mdast-util-gfm-strikethrough: 2.0.0 - mdast-util-gfm-table: 2.0.0 - mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-gfm-footnote: 2.1.0(supports-color@10.2.2) + mdast-util-gfm-strikethrough: 2.0.0(supports-color@10.2.2) + mdast-util-gfm-table: 2.0.0(supports-color@10.2.2) + mdast-util-gfm-task-list-item: 2.0.0(supports-color@10.2.2) mdast-util-to-markdown: 2.1.2 transitivePeerDependencies: - supports-color @@ -4386,10 +5124,10 @@ snapshots: micromark-util-types@2.0.2: {} - micromark@4.0.2: + micromark@4.0.2(supports-color@10.2.2): dependencies: '@types/debug': 4.1.13 - debug: 4.4.3 + debug: 4.4.3(supports-color@10.2.2) decode-named-character-reference: 1.3.0 devlop: 1.1.0 micromark-core-commonmark: 2.0.3 @@ -4415,6 +5153,18 @@ snapshots: min-indent@1.0.1: {} + miniflare@5.20260801.0-alpha: + dependencies: + '@cspotcode/source-map-support': 0.8.1 + sharp: 0.35.2 + undici: 7.28.0 + workerd: 1.20260801.1 + ws: 8.21.0 + youch: 4.1.0-beta.10 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + mri@1.2.0: {} ms@2.1.3: {} @@ -4461,6 +5211,8 @@ snapshots: path-key@3.1.1: {} + path-to-regexp@6.3.0: {} + path-type@4.0.0: {} pathe@2.0.3: {} @@ -4560,30 +5312,30 @@ snapshots: hast-util-from-html: 2.0.3 unified: 11.0.5 - remark-frontmatter@5.0.0: + remark-frontmatter@5.0.0(supports-color@10.2.2): dependencies: '@types/mdast': 4.0.4 - mdast-util-frontmatter: 2.0.1 + mdast-util-frontmatter: 2.0.1(supports-color@10.2.2) micromark-extension-frontmatter: 2.0.0 unified: 11.0.5 transitivePeerDependencies: - supports-color - remark-gfm@4.0.1: + remark-gfm@4.0.1(supports-color@10.2.2): dependencies: '@types/mdast': 4.0.4 - mdast-util-gfm: 3.1.0 + mdast-util-gfm: 3.1.0(supports-color@10.2.2) micromark-extension-gfm: 3.0.0 - remark-parse: 11.0.0 + remark-parse: 11.0.0(supports-color@10.2.2) remark-stringify: 11.0.0 unified: 11.0.5 transitivePeerDependencies: - supports-color - remark-parse@11.0.0: + remark-parse@11.0.0(supports-color@10.2.2): dependencies: '@types/mdast': 4.0.4 - mdast-util-from-markdown: 2.0.2 + mdast-util-from-markdown: 2.0.2(supports-color@10.2.2) micromark-util-types: 2.0.2 unified: 11.0.5 transitivePeerDependencies: @@ -4680,6 +5432,38 @@ snapshots: setimmediate@1.0.5: {} + sharp@0.35.2: + dependencies: + '@img/colour': 1.1.0 + detect-libc: 2.1.2 + semver: 7.8.5 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.35.2 + '@img/sharp-darwin-x64': 0.35.2 + '@img/sharp-freebsd-wasm32': 0.35.2 + '@img/sharp-libvips-darwin-arm64': 1.3.1 + '@img/sharp-libvips-darwin-x64': 1.3.1 + '@img/sharp-libvips-linux-arm': 1.3.1 + '@img/sharp-libvips-linux-arm64': 1.3.1 + '@img/sharp-libvips-linux-ppc64': 1.3.1 + '@img/sharp-libvips-linux-riscv64': 1.3.1 + '@img/sharp-libvips-linux-s390x': 1.3.1 + '@img/sharp-libvips-linux-x64': 1.3.1 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.1 + '@img/sharp-libvips-linuxmusl-x64': 1.3.1 + '@img/sharp-linux-arm': 0.35.2 + '@img/sharp-linux-arm64': 0.35.2 + '@img/sharp-linux-ppc64': 0.35.2 + '@img/sharp-linux-riscv64': 0.35.2 + '@img/sharp-linux-s390x': 0.35.2 + '@img/sharp-linux-x64': 0.35.2 + '@img/sharp-linuxmusl-arm64': 0.35.2 + '@img/sharp-linuxmusl-x64': 0.35.2 + '@img/sharp-webcontainers-wasm32': 0.35.2 + '@img/sharp-win32-arm64': 0.35.2 + '@img/sharp-win32-ia32': 0.35.2 + '@img/sharp-win32-x64': 0.35.2 + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -4723,6 +5507,8 @@ snapshots: dependencies: min-indent: 1.0.1 + supports-color@10.2.2: {} + symbol-tree@3.2.4: {} term-size@2.2.1: {} @@ -4788,6 +5574,12 @@ snapshots: undici-types@7.18.2: {} + undici@7.28.0: {} + + unenv@2.0.0-rc.24: + dependencies: + pathe: 2.0.3 + unified@11.0.5: dependencies: '@types/unist': 3.0.3 @@ -4856,7 +5648,7 @@ snapshots: lightningcss: 1.33.0 yaml: 2.9.0 - vite@8.2.0(@types/node@24.13.3)(yaml@2.9.0): + vite@8.2.0(@types/node@24.13.3)(esbuild@0.28.1)(yaml@2.9.0): dependencies: lightningcss: 1.33.0 picomatch: 4.0.5 @@ -4865,13 +5657,14 @@ snapshots: tinyglobby: 0.2.17 optionalDependencies: '@types/node': 24.13.3 + esbuild: 0.28.1 fsevents: 2.3.3 yaml: 2.9.0 - vitest@4.1.10(@types/node@24.13.3)(jsdom@27.0.0)(vite@8.2.0(@types/node@24.13.3)(yaml@2.9.0)): + vitest@4.1.10(@types/node@24.13.3)(jsdom@27.0.0(supports-color@10.2.2))(vite@8.2.0(@types/node@24.13.3)(esbuild@0.28.1)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.10 - '@vitest/mocker': 4.1.10(vite@8.2.0(@types/node@24.13.3)(yaml@2.9.0)) + '@vitest/mocker': 4.1.10(vite@8.2.0(@types/node@24.13.3)(esbuild@0.28.1)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.10 '@vitest/runner': 4.1.10 '@vitest/snapshot': 4.1.10 @@ -4888,11 +5681,11 @@ snapshots: tinyexec: 1.2.4 tinyglobby: 0.2.17 tinyrainbow: 3.1.1 - vite: 8.2.0(@types/node@24.13.3)(yaml@2.9.0) + vite: 8.2.0(@types/node@24.13.3)(esbuild@0.28.1)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 24.13.3 - jsdom: 27.0.0 + jsdom: 27.0.0(supports-color@10.2.2) transitivePeerDependencies: - msw @@ -4926,6 +5719,32 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 + workerd@1.20260801.1: + optionalDependencies: + '@cloudflare/workerd-darwin-64': 1.20260801.1 + '@cloudflare/workerd-darwin-arm64': 1.20260801.1 + '@cloudflare/workerd-linux-64': 1.20260801.1 + '@cloudflare/workerd-linux-arm64': 1.20260801.1 + '@cloudflare/workerd-windows-64': 1.20260801.1 + + wrangler@4.119.0: + dependencies: + '@cloudflare/kv-asset-handler': 0.5.0 + '@cloudflare/unenv-preset': 2.16.1(unenv@2.0.0-rc.24)(workerd@1.20260801.1) + blake3-wasm: 2.1.5 + esbuild: 0.28.1 + miniflare: 5.20260801.0-alpha + path-to-regexp: 6.3.0 + unenv: 2.0.0-rc.24 + workerd: 1.20260801.1 + optionalDependencies: + fsevents: 2.3.3 + transitivePeerDependencies: + - bufferutil + - utf-8-validate + + ws@8.21.0: {} + ws@8.21.1: {} xml-name-validator@5.0.0: {} @@ -4946,6 +5765,19 @@ snapshots: dependencies: buffer-crc32: 1.0.0 + youch-core@0.3.3: + dependencies: + '@poppinss/exception': 1.2.3 + error-stack-parser-es: 1.0.5 + + youch@4.1.0-beta.10: + dependencies: + '@poppinss/colors': 4.1.6 + '@poppinss/dumper': 0.6.5 + '@speed-highlight/core': 1.2.23 + cookie: 1.1.1 + youch-core: 0.3.3 + zod@4.4.3: {} zwitch@2.0.4: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 3c031a63..60032e55 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -15,6 +15,17 @@ packages: # contributor's machine, which is the promise `check:no-native` exists to keep. allowBuilds: esbuild: false + sharp: set this to true or false + # Same story as esbuild, verified 2026-08-06. `workerd` is the Cloudflare runtime Miniflare + # embeds; it is a dev dependency of the deployment work only and never reaches a user. Its + # binary arrives through `@cloudflare/workerd-` optional dependencies rather than + # through the script, so declining it costs nothing: `wrangler --version` and a Worker with + # live D1 and R2 bindings both work with it refused. + workerd: false + +minimumReleaseAgeExclude: + - miniflare@5.20260801.0-alpha + - wrangler@4.119.0 # pdfjs-dist declares `@napi-rs/canvas` as an optionalDependency and reaches for it at # runtime if it is present. It is only needed to *render* a page, which Lorepack never does: diff --git a/scripts/check-no-native.mjs b/scripts/check-no-native.mjs index 69d46fdf..ff9aea70 100755 --- a/scripts/check-no-native.mjs +++ b/scripts/check-no-native.mjs @@ -2,7 +2,7 @@ // Guards the zero-surprise install promise: no native add-ons, no install scripts. // See AGENTS.md section 9 and architecture section 6.1. import { execFileSync } from 'node:child_process'; -import { existsSync, readdirSync, readFileSync, statSync } from 'node:fs'; +import { existsSync, readFileSync } from 'node:fs'; import { join } from 'node:path'; const BANNED_PACKAGES = [ @@ -30,36 +30,86 @@ for (const file of ours) { } } -// 2. No banned native package anywhere in the installed tree. -function scan(dir, depth = 0) { - if (depth > 6 || !existsSync(dir)) return; - for (const entry of readdirSync(dir)) { - const full = join(dir, entry); - if (entry === 'node_modules') { - scan(full, depth + 1); - continue; - } - if (entry.startsWith('@')) { - scan(full, depth + 1); - continue; +/** + * 2. No banned native package in what a **user** installs. + * + * Scoped to the production dependency closure of the published packages, not to the whole + * `node_modules` tree, and that is a deliberate narrowing rather than a relaxation (#256). + * + * The promise is invariant 7: *no Python, Docker, compiler toolchain, native add-on, model + * download, API key or account. Ever.* It is a promise about **someone installing Lorepack**. + * A build tool in a contributor's tree breaks none of it, and the whole-tree version of this + * check could not tell the two apart: it refused Miniflare, which is a dev-only Cloudflare + * emulator that never reaches a published package, on the strength of a transitive `sharp`. + * + * Narrowing makes the guard say something true and checkable instead of something broader and + * wrong. What it must never lose is the part that matters, so the mutation to run against any + * future edit here is: **add a native dependency to a published package and watch this fail.** + * `test/no-native.test.ts` does exactly that. + */ +function publishedClosure() { + // `shell: true` on Windows, where `pnpm` is a `.cmd` shim that `execFileSync` cannot execute + // directly. Without it this passed on macOS and Linux and failed on `windows-latest` with a + // spawn error carrying no useful message, which is the shape of most Windows CI surprises. + const listed = JSON.parse( + execFileSync( + 'pnpm', + ['list', '--prod', '--depth', 'Infinity', '--json', '-r', '--filter', './packages/*'], + { + encoding: 'utf8', + maxBuffer: 64 * 1024 * 1024, + ...(process.platform === 'win32' ? { shell: true } : {}), + }, + ), + ); + + /** name -> resolved path, for every production dependency reachable from a published package. */ + const reached = new Map(); + const walk = (dependencies) => { + for (const [name, node] of Object.entries(dependencies ?? {})) { + // A workspace link is one of ours; its own dependencies are walked, not the link. + if (typeof node?.version === 'string' && node.version.startsWith('link:')) { + walk(node.dependencies); + continue; + } + if (reached.has(name)) continue; + reached.set(name, node?.path ?? null); + walk(node?.dependencies); } - let st; - try { - st = statSync(full); - } catch { - continue; + }; + for (const workspace of listed) { + if (workspace.private === true) continue; + walk(workspace.dependencies); + } + return reached; +} + +/** + * Direct production dependencies, read from the manifests rather than from the installed tree. + * + * Both halves are needed and they catch different things. A manifest read catches a native + * dependency the moment it is **declared**, before anyone installs, which is when a reviewer is + * looking at the diff. The installed closure below catches one that arrives **transitively**, + * which no manifest mentions. + */ +for (const file of ours) { + const pkg = JSON.parse(readFileSync(file, 'utf8')); + if (pkg.private === true || !file.startsWith('packages/')) continue; + for (const name of Object.keys(pkg.dependencies ?? {})) { + if (BANNED_PACKAGES.includes(name)) { + problems.push(`${file} declares ${name}, which ships native code`); } - if (!st.isDirectory()) continue; - const name = dir.includes('node_modules/@') - ? `${dir.slice(dir.lastIndexOf('@'))}/${entry}` - : entry; - if (BANNED_PACKAGES.includes(name)) problems.push(`installed tree contains ${name} (${full})`); - if (existsSync(join(full, 'binding.gyp'))) - problems.push(`native add-on (binding.gyp) at ${full}`); - scan(join(full, 'node_modules'), depth + 1); } } -scan('node_modules'); + +for (const [name, path] of publishedClosure()) { + if (BANNED_PACKAGES.includes(name)) { + problems.push(`a published package depends on ${name}, which ships native code`); + } + if (path !== null && existsSync(join(path, 'binding.gyp'))) { + problems.push(`native add-on (binding.gyp) in ${name}, reachable from a published package`); + } +} if (problems.length > 0) { console.error( diff --git a/tools/arch/test/no-native.test.ts b/tools/arch/test/no-native.test.ts new file mode 100644 index 00000000..961196cd --- /dev/null +++ b/tools/arch/test/no-native.test.ts @@ -0,0 +1,71 @@ +import { execFileSync } from 'node:child_process'; +import { readFileSync, writeFileSync } from 'node:fs'; +import { join } from 'node:path'; +import { afterEach, describe, expect, it } from 'vitest'; + +/** + * The guard that keeps invariant 7 checkable (#256). + * + * `check:no-native` was narrowed from "no native code anywhere in `node_modules`" to "no native + * code a **user** would install". That is a real narrowing, and the only thing that makes it + * safe is this file: the whole-tree version could not tell a build tool in a contributor's + * checkout from a native dependency shipped to users, and refused both. + * + * So the assertion is the mutation, not the happy path. A test that only ran the check and + * expected zero would pass just as happily against a check that had stopped looking. + */ + +const root = join(import.meta.dirname, '..', '..', '..'); +const manifest = join(root, 'packages', 'sdk', 'package.json'); +const original = readFileSync(manifest, 'utf8'); + +function check(): { code: number; output: string } { + try { + const output = execFileSync('node', ['scripts/check-no-native.mjs'], { + cwd: root, + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'pipe'], + }); + return { code: 0, output }; + } catch (error) { + const failure = error as { status?: number; stdout?: string; stderr?: string }; + return { code: failure.status ?? 1, output: `${failure.stdout ?? ''}${failure.stderr ?? ''}` }; + } +} + +afterEach(() => { + writeFileSync(manifest, original); +}); + +describe('the no-native guard', () => { + it('passes on the repository as it stands', () => { + expect(check().code).toBe(0); + }); + + /** + * The one that matters. A published package taking a native dependency is the thing + * invariant 7 forbids, and it must fail whatever else the check learns to ignore. + * + * Declared rather than installed: the check reads the production closure from the manifests, + * so this needs no `pnpm install` and cannot leave a native package behind if it fails. + */ + it('fails when a published package declares a native dependency', () => { + const declared = JSON.parse(original) as { dependencies?: Record }; + declared.dependencies = { ...declared.dependencies, sharp: '0.34.4' }; + writeFileSync(manifest, `${JSON.stringify(declared, null, 2)}\n`); + + const result = check(); + expect(result.code).not.toBe(0); + expect(result.output).toContain('sharp'); + }); + + it('still refuses an install hook in one of our own packages', () => { + const declared = JSON.parse(original) as { scripts?: Record }; + declared.scripts = { ...declared.scripts, postinstall: 'echo hello' }; + writeFileSync(manifest, `${JSON.stringify(declared, null, 2)}\n`); + + const result = check(); + expect(result.code).not.toBe(0); + expect(result.output).toContain('postinstall'); + }); +});