diff --git a/.gitignore b/.gitignore index 6c36532e9..e3c7bb441 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,5 @@ wrangler.jsonc !.husky/pre-commit .smoke -.svelte-kit \ No newline at end of file +.svelte-kit + diff --git a/CHANGELOG.md b/CHANGELOG.md index faf447a8a..a9b0bdfdd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,182 @@ +## v0.41.2 + +###    🚀 Features + +- **cloudflare**: Cloudflare Advanced Certificate Pack resource  -  by **John Royal** in https://github.com/sam-goodwin/alchemy/issues/487 [(b3a2f)](https://github.com/sam-goodwin/alchemy/commit/b3a2f425) + +#####     [View changes on GitHub](https://github.com/sam-goodwin/alchemy/compare/v0.41.1...v0.41.2) + +--- + +## v0.41.1 + +###    🐞 Bug Fixes + +- **cloudflare**: Consistent ports for miniflare dev server  -  by **John Royal** in https://github.com/sam-goodwin/alchemy/issues/496 [(5564c)](https://github.com/sam-goodwin/alchemy/commit/5564cb91) + +#####     [View changes on GitHub](https://github.com/sam-goodwin/alchemy/compare/v0.41.0...v0.41.1) + +--- + +## v0.41.0 + +###    🚀 Features + +- **cloudflare**: Containers  -  by **Sam Goodwin** in https://github.com/sam-goodwin/alchemy/issues/476 [(43e88)](https://github.com/sam-goodwin/alchemy/commit/43e88e95) +- **github**: Add repo webhook resource  -  by **Justin Bennett** in https://github.com/sam-goodwin/alchemy/issues/477 [(2c997)](https://github.com/sam-goodwin/alchemy/commit/2c997d6f) + +#####     [View changes on GitHub](https://github.com/sam-goodwin/alchemy/compare/v0.40.1...v0.41.0) + +--- + +## v0.40.1 + +*No significant changes* + +#####     [View changes on GitHub](https://github.com/sam-goodwin/alchemy/compare/v0.40.0...v0.40.1) + +--- + +## v0.40.0 + +###    🚀 Features + +- **cloudflare**: Miniflare dev server  -  by **John Royal** in https://github.com/sam-goodwin/alchemy/issues/396 [(3d219)](https://github.com/sam-goodwin/alchemy/commit/3d21941c) + +###    🐞 Bug Fixes + +- **cloudflare**: DOStateStore undefined fix  -  by **John Royal** in https://github.com/sam-goodwin/alchemy/issues/480 [(7d909)](https://github.com/sam-goodwin/alchemy/commit/7d9095e0) + +#####     [View changes on GitHub](https://github.com/sam-goodwin/alchemy/compare/v0.39.1...v0.40.0) + +--- + +## v0.39.1 + +###    🐞 Bug Fixes + +- **core**: Stage scope not being adopted  -  by **Michael K** in https://github.com/sam-goodwin/alchemy/issues/469 [(b949a)](https://github.com/sam-goodwin/alchemy/commit/b949aade) + +#####     [View changes on GitHub](https://github.com/sam-goodwin/alchemy/compare/v0.39.0...v0.39.1) + +--- + +## v0.39.0 + +###    🚀 Features + +- **cloudflare**: Support Worker.domains for custom domains  -  by **Sam Goodwin** in https://github.com/sam-goodwin/alchemy/issues/468 [(7a357)](https://github.com/sam-goodwin/alchemy/commit/7a357763) + +#####     [View changes on GitHub](https://github.com/sam-goodwin/alchemy/compare/v0.38.1...v0.39.0) + +--- + +## v0.38.1 + +###    🐞 Bug Fixes + +- **cloudflare**: Do state store fails to upload  -  by **John Royal** in https://github.com/sam-goodwin/alchemy/issues/465 [(ca966)](https://github.com/sam-goodwin/alchemy/commit/ca966235) + +#####     [View changes on GitHub](https://github.com/sam-goodwin/alchemy/compare/v0.38.0...v0.38.1) + +--- + +## v0.38.0 + +###    🚀 Features + +- **cli**: + - Complete cli overhaul with trpc-cli, zod, and clack/prompts  -  by **Aman Varshney** in https://github.com/sam-goodwin/alchemy/issues/405 [(dea9e)](https://github.com/sam-goodwin/alchemy/commit/dea9ed1e) +- **cloudflare**: + - Pin default worker compatibility date to build time  -  by **Sam Goodwin** in https://github.com/sam-goodwin/alchemy/issues/460 [(10035)](https://github.com/sam-goodwin/alchemy/commit/100355b0) + - Add URL support to WorkerStub  -  by **Sam Goodwin** in https://github.com/sam-goodwin/alchemy/issues/464 [(4fda9)](https://github.com/sam-goodwin/alchemy/commit/4fda99da) +- **core**: + - Replace Resource  -  by **Michael K** in https://github.com/sam-goodwin/alchemy/issues/417 [(27133)](https://github.com/sam-goodwin/alchemy/commit/271331e1) + +#####     [View changes on GitHub](https://github.com/sam-goodwin/alchemy/compare/v0.37.2...v0.38.0) + +--- + +## v0.37.2 + +###    🐞 Bug Fixes + +- **cloudflare**: Defensively resolve __dirname and worker.ts > worker.js in DOStateStore  -  by **Sam Goodwin** in https://github.com/sam-goodwin/alchemy/issues/452 [(c63fd)](https://github.com/sam-goodwin/alchemy/commit/c63fdd60) + +#####     [View changes on GitHub](https://github.com/sam-goodwin/alchemy/compare/v0.37.1...v0.37.2) + +--- + +## v0.37.1 + +###    🚀 Features + +- **cloudflare**: Relax Durable Object RPC type constraint  -  by **Sam Goodwin** in https://github.com/sam-goodwin/alchemy/issues/445 [(107e7)](https://github.com/sam-goodwin/alchemy/commit/107e79de) + +###    🐞 Bug Fixes + +- **cloudflare**: DOStateStore init uploads a worker and not a version  -  by **Sam Goodwin** in https://github.com/sam-goodwin/alchemy/issues/447 [(30cc6)](https://github.com/sam-goodwin/alchemy/commit/30cc6424) + +#####     [View changes on GitHub](https://github.com/sam-goodwin/alchemy/compare/v0.37.0...v0.37.1) + +--- + +## v0.37.0 + +###    🚀 Features + +- **cloudflare**: Add `run_worker_first: string[]` option  -  by **Rahul Mishra** in https://github.com/sam-goodwin/alchemy/issues/440 [(d4b0d)](https://github.com/sam-goodwin/alchemy/commit/d4b0de34) +- **stripe**: Price meter support  -  by **Nick Balestra-Foster** in https://github.com/sam-goodwin/alchemy/issues/410 [(9315d)](https://github.com/sam-goodwin/alchemy/commit/9315d742) + +###    🐞 Bug Fixes + +- **cloudflare**: + - Adopt DO that have migration tags  -  by **Sam Goodwin** in https://github.com/sam-goodwin/alchemy/issues/437 [(bcbd7)](https://github.com/sam-goodwin/alchemy/commit/bcbd7fdb) + - Website resource respects cwd prop for wrangler.jsonc placement  -  by **John Royal** in https://github.com/sam-goodwin/alchemy/issues/443 [(bef17)](https://github.com/sam-goodwin/alchemy/commit/bef17985) + +#####     [View changes on GitHub](https://github.com/sam-goodwin/alchemy/compare/v0.36.0...v0.37.0) + +--- + +## v0.36.0 + +###    🚀 Features + +- **docker**: Docker provider  -  by **Pavitra Golchha** in https://github.com/sam-goodwin/alchemy/issues/189 [(6f973)](https://github.com/sam-goodwin/alchemy/commit/6f973983) + +###    🐞 Bug Fixes + +- **cli**: Improve package manager handling in create-alchemy  -  by **Nico Baier** in https://github.com/sam-goodwin/alchemy/issues/423 [(d0c7c)](https://github.com/sam-goodwin/alchemy/commit/d0c7ce83) +- **core**: Allow colors in CI environments, only disable for NO_COLOR  -  by **Sam Goodwin** in https://github.com/sam-goodwin/alchemy/issues/429 [(a194a)](https://github.com/sam-goodwin/alchemy/commit/a194ab5a) + +#####     [View changes on GitHub](https://github.com/sam-goodwin/alchemy/compare/v0.35.1...v0.36.0) + +--- + +## v0.35.1 + +###    🐞 Bug Fixes + +- **fs**: Better support for windows file system  -  by **Michael K** in https://github.com/sam-goodwin/alchemy/issues/430 [(8dd9f)](https://github.com/sam-goodwin/alchemy/commit/8dd9f196) + +#####     [View changes on GitHub](https://github.com/sam-goodwin/alchemy/compare/v0.35.0...v0.35.1) + +--- + +## v0.35.0 + +###    🐞 Bug Fixes + +- **cloudflare**: + - Set force=true when deleting a Worker  -  by **Sam Goodwin** in https://github.com/sam-goodwin/alchemy/issues/432 [(2b21d)](https://github.com/sam-goodwin/alchemy/commit/2b21d41e) + - Call wfp endpoint when deleting workers  -  by **Sam Goodwin** in https://github.com/sam-goodwin/alchemy/issues/434 [(d109d)](https://github.com/sam-goodwin/alchemy/commit/d109d984) +- **core**: + - Ensure alchemy providers are globally registered  -  by **Sam Goodwin** in https://github.com/sam-goodwin/alchemy/issues/433 [(b0528)](https://github.com/sam-goodwin/alchemy/commit/b05284f6) + +#####     [View changes on GitHub](https://github.com/sam-goodwin/alchemy/compare/v0.34.3...v0.35.0) + +--- + ## v0.34.3 ###    🚀 Features diff --git a/alchemy-web/docs/concepts/adoption.md b/alchemy-web/docs/concepts/adoption.md index 3aebb3146..c3d611d14 100644 --- a/alchemy-web/docs/concepts/adoption.md +++ b/alchemy-web/docs/concepts/adoption.md @@ -1,5 +1,5 @@ --- -order: 10 +order: 11 title: Adoption description: Learn how to adopt existing infrastructure with Alchemy resources instead of failing when resources already exist. --- diff --git a/alchemy-web/docs/concepts/destroy.md b/alchemy-web/docs/concepts/destroy.md index e91594d5c..2c423637a 100644 --- a/alchemy-web/docs/concepts/destroy.md +++ b/alchemy-web/docs/concepts/destroy.md @@ -68,3 +68,8 @@ if (this.phase === "delete") { return this.destroy(); } ``` + +## Related Concepts + +- **[Replace](./replace.md)** - How to replace resources that can not be updated + diff --git a/alchemy-web/docs/concepts/replace.md b/alchemy-web/docs/concepts/replace.md new file mode 100644 index 000000000..4d0916ca1 --- /dev/null +++ b/alchemy-web/docs/concepts/replace.md @@ -0,0 +1,57 @@ +--- +order: 10 +title: Replace +description: Learn how to safely replace infrastructure resources with Alchemy. Understand the risks and best practices for resource replacement. +--- + +# Replace + +It is some times impossible to UPDATE a resource, e.g. you cannot rename a R2 Bucket name. +In these cases, you need to perform a REPLACE operation to: + +1. create a new version of the Resource and update references +2. delete the old version of the Resource (or leave it orphaned) + +## Trigger Replacement + +During the **update phase**, you can trigger a replacement by calling `this.replace()`: + +```typescript +// Implementation pattern +if (this.phase === "update") { + if (this.output.name !== props.name) { + // trigger replace and terminate this `"update"` phase + this.replace(); + // (unreachable code) + } else { + return updateResource(); + } +} +``` + +## Create new + +After you call `this.replace()`, the `"update"` phase will terminate and be re-invoked with `"create"` (to create the new resource). + +```ts +if (this.phase === "create") { + return createNewResource(); +} +``` + +## Delete old + +After all downstream dependencies have been updated and you finally call `app.finalize()`, Alchemy will then invoke the `"delete"` phase on the old resource. + +```ts +const app = await alchemy("app"); + +// ... create resources + +await app.finalize(); // finalize scopes by deleting "orphaned" and "replaced" resources +``` + +## Related Concepts + +- **[Destroy](./destroy.md)** - How to destroy resources +- **[Scope](./scope.md)** - Scope lifecycle diff --git a/alchemy-web/docs/guides/cloudflare-worker.md b/alchemy-web/docs/guides/cloudflare-worker.md index f328eedfd..c5716eeb3 100644 --- a/alchemy-web/docs/guides/cloudflare-worker.md +++ b/alchemy-web/docs/guides/cloudflare-worker.md @@ -175,6 +175,32 @@ const frontend = await Worker("frontend", { }); ``` +## Self-Binding + +A worker can bind to itself using `Self` or `WorkerRef`: + +```ts +import { Worker, Self, WorkerRef } from "alchemy/cloudflare"; + +// Using Self +const workerWithSelf = await Worker("my-worker", { + name: "my-worker", + entrypoint: "./src/worker.ts", + bindings: { + SELF: Self, + }, +}); + +// Using WorkerRef with the worker's own ID +const workerWithRef = await Worker("my-worker", { + name: "my-worker", + entrypoint: "./src/worker.ts", + bindings: { + SELF: WorkerRef("my-worker"), + }, +}); +``` + ## Circular Worker Bindings When workers need to bind to each other (circular dependency), use `WorkerStub` to break the cycle: @@ -331,6 +357,26 @@ const worker = await Worker("api", { > [!TIP] > See the [Route](../providers/cloudflare/route.md) for more information. +## Custom Domains + +Bind custom domains directly to your worker for a simpler routing setup: + +```ts +import { Worker } from "alchemy/cloudflare"; + +const worker = await Worker("api", { + name: "api-worker", + entrypoint: "./src/api.ts", + domains: ["api.example.com", "admin.example.com"], +}); + +// Access the created domains +console.log(worker.domains); // Array of created CustomDomain resources +``` + +> [!TIP] +> See the [Routes and Domains](https://developers.cloudflare.com/workers/configuration/routing/#what-is-best-for-me) Cloudflare docs to help decide between when to use a Route vs a Domain. + ## Workers for Platforms Deploy workers to dispatch namespaces for multi-tenant architectures using Cloudflare's Workers for Platforms: diff --git a/alchemy-web/docs/guides/dev.md b/alchemy-web/docs/guides/dev.md new file mode 100644 index 000000000..d7ac3aab9 --- /dev/null +++ b/alchemy-web/docs/guides/dev.md @@ -0,0 +1,174 @@ +--- +order: 7 +title: Development Mode +description: Learn how to use Alchemy's development mode to run your application locally. +--- + +# Development Mode (Beta) + +Alchemy's development mode provides a powerful local development experience for Cloudflare Workers, featuring hot reloading, local resource emulation, and seamless integration with remote Cloudflare services. + +> **Note:** Development mode is currently in beta. Some features may not work as expected. + +## Overview + +To run Alchemy in development mode, use the `--dev` flag when running your `alchemy.run.ts` script: + +```bash +bun run alchemy.run.ts --dev +npx tsx alchemy.run.ts --dev +``` + +This starts Alchemy in development mode, which will: + +- Emulate Cloudflare Workers and associated resources locally using Miniflare +- Hot reload Workers when you make changes to your code + +### Watching Your Alchemy Configuration + +Alchemy does not watch your `alchemy.run.ts` file for changes. To automatically apply changes to your configuration, you can the watch mode associated with your runtime environment. For example: + +```bash +# Using bun's watch mode +bun run --watch alchemy.run.ts + +# Using Node.js watch mode +npx tsx --watch alchemy.run.ts +``` + +Development mode is enabled automatically when the `--watch` flag is detected. + +### Programmatic Configuration + +You can also enable dev mode programmatically by setting the `dev` option: + +```typescript +const app = await alchemy("my-app", { + dev: true +}); +``` + +## Configuration + +When running in dev mode, Alchemy runs your Cloudflare Workers locally using Miniflare, and will be available on a randomly selected port. You can specify the port by setting the `port` property on the `Worker` resource: + +```typescript +const worker = await Worker("my-worker", { + entrypoint: "worker.ts", + dev: { + port: 3000 + } +}); + +console.log(worker.url); // http://localhost:3000 +``` + +## Website Development + +When using the `Website` resource in development mode, you can specify a custom development command that Alchemy will run locally: + +```typescript +const website = await Website("my-website", { + dev: { + command: "npm run dev", + url: "http://localhost:5173", + } +}); +``` + +If no command is specified, Alchemy will automatically detect and run the appropriate dev command based on your project's package manager: + +- **bun**: `bun vite dev` +- **npm**: `npx vite dev` +- **pnpm**: `pnpm vite dev` +- **yarn**: `yarn vite dev` + +### Vite Integration + +For projects using Vite, Alchemy integrates with the [Cloudflare Vite plugin](https://developers.cloudflare.com/workers/development-testing/vite/) to provide enhanced local development capabilities. This integration enables better support for certain binding types when running locally. + +To enable Vite integration, configure your `vite.config.ts` with the Cloudflare plugin: + +```typescript +import { cloudflare } from "@cloudflare/vite-plugin"; +import { defineConfig } from "vite"; + +export default defineConfig({ + plugins: [ + cloudflare({ + persistState: process.env.ALCHEMY_CLOUDFLARE_PERSIST_PATH + ? { + path: process.env.ALCHEMY_CLOUDFLARE_PERSIST_PATH, + } + : undefined, + }), + ], +}); +``` + +The Vite integration provides improved support for the following binding types (marked with ✅ in the "Vite" column of the supported resources table below). + +## Bindings + +By default, Alchemy emulates resources such as [D1 Databases](../providers/cloudflare/d1-database.md), [KV Namespaces](../providers/cloudflare/kv-namespace.md), and [R2 Buckets](../providers/cloudflare/bucket.md) locally. + +Alchemy also supports [remote bindings](https://developers.cloudflare.com/workers/development-testing/#remote-bindings) for select resources. For resources that allow either local or remote execution, you can set the `dev` property on the resource to `{ remote: true }`: + +```typescript +const db = await D1Database("my-db", { + dev: { remote: true } +}); + +const kv = await KVNamespace("my-kv", { + dev: { remote: true } +}); + +const r2 = await R2Bucket("my-r2", { + dev: { remote: true } +}); +``` + +Some resources only support remote execution, such as [AI Gateways](../providers/cloudflare/ai-gateway.md). These resources will automatically be run remotely, so usage will be billed the same as if you were running them in production. + +### Supported Resources + +The following bindings are supported in dev mode: + +| Resource | Local | Remote | Vite | +|----------|-------|--------|------| +| AI | ❌ | ✅ | ❌ | +| Analytics Engine | ✅ | ❌ | ❌ | +| Assets | ✅ | ❌ | ❌ | +| Browser Rendering | ❌ | ✅ | ❌ | +| D1 Database | ✅ | ✅ | ✅ | +| Dispatch Namespace | ❌ | ✅ | ❌ | +| Durable Object Namespace | ✅ | ❌ | ❌ | +| Hyperdrive | ✅ | ❌ | ❌ | +| Images | ✅ | ✅ | ❌ | +| JSON | ✅ | ❌ | ❌ | +| KV Namespace | ✅ | ✅ | ✅ | +| Pipeline | ✅ | ❌ | ❌ | +| Queue | ✅ | ✅ | ❌ | +| R2 Bucket | ✅ | ✅ | ✅ | +| Secret | ✅ | ❌ | ❌ | +| Secret Key | ❌ | ❌ | ❌ | +| Service | ✅ | ✅ | ❌ | +| Vectorize Index | ❌ | ✅ | ❌ | +| Version Metadata | ✅ | ❌ | ❌ | +| Workflow | ✅ | ❌ | ❌ | +| Text | ✅ | ❌ | ❌ | + +## Limitations + +- Hot reloading for Workers is only supported when the `entrypoint` property is set. To hot reload an inline script, you must use an external watcher to monitor your `alchemy.run.ts` file. +- Local Workers can push to remote queues, but cannot consume from them. +- Hyperdrive support is experimental. Hyperdrive configurations that use Cloudflare Access are not supported, and only configurations provisioned in the same `alchemy.run.ts` file will work. This is a [limitation from Cloudflare that is actively being worked on](https://developers.cloudflare.com/workers/development-testing/#unsupported-remote-bindings). + +## Best Practices + +1. **Use local resources for development** - Faster iteration and no API costs +2. **Test with remote resources** - Validate integration before deployment +3. **Leverage hot reloading** - Use entrypoint files for automatic rebuilds +4. **Monitor build output** - Watch for compilation errors and warnings +5. **Configure Worker ports explicitly** - Avoid conflicts in multi-worker setups +6. **Use external watchers** - For automatic restarts when configuration changes diff --git a/alchemy-web/docs/providers/cloudflare/certificate-pack.md b/alchemy-web/docs/providers/cloudflare/certificate-pack.md new file mode 100644 index 000000000..7de91adbd --- /dev/null +++ b/alchemy-web/docs/providers/cloudflare/certificate-pack.md @@ -0,0 +1,205 @@ +--- +title: Cloudflare Certificate Pack +description: Learn how to create and manage Cloudflare Advanced Certificate Packs for flexible SSL/TLS certificates with multiple Certificate Authorities and custom configurations. +--- + +# Certificate Pack + +The Certificate Pack resource lets you create and manage [Cloudflare Advanced Certificate Packs](https://developers.cloudflare.com/api/resources/ssl/subresources/certificate_packs/) for flexible SSL/TLS certificates with multiple Certificate Authority options. + +**Important Requirements:** +- **Advanced Certificate Manager (ACM) must be activated:** Before using Certificate Packs, you must activate ACM in your Cloudflare dashboard. Navigate to your domain > **SSL/TLS** > **Edge Certificates** and click **Activate** for Advanced Certificate Manager. This requires a $10/month subscription per domain. +- Requires a paid Cloudflare plan (not available on Free plans) +- Certificate provisioning can take up to 10 minutes +- Most properties are immutable after creation (only `cloudflareBranding` can be updated) + +## Basic Example + +Create a basic certificate pack with Let's Encrypt for your domain. + +```ts +import { Zone, CertificatePack } from "alchemy/cloudflare"; + +const zone = await Zone("my-zone", { + name: "example.com", +}); + +const basicCert = await CertificatePack("my-cert", { + zone: zone, + certificateAuthority: "lets_encrypt", + hosts: ["example.com", "www.example.com"], + validationMethod: "txt", + validityDays: 90, +}); +``` + +## Enterprise Certificate with Google Trust Services + +Create an enterprise-grade certificate with Google Trust Services and Cloudflare branding. + +```ts +const enterpriseCert = await CertificatePack("enterprise-cert", { + zone: "example.com", // Can use zone ID string or Zone resource + certificateAuthority: "google", + hosts: ["example.com", "*.example.com", "api.example.com"], + validationMethod: "txt", + validityDays: 365, + cloudflareBranding: true, +}); +``` + +## Wildcard Certificate with SSL.com + +Create a wildcard certificate using SSL.com with email validation. + +```ts +const wildcardCert = await CertificatePack("wildcard-cert", { + zone: myZone, + certificateAuthority: "ssl_com", + hosts: ["example.com", "*.example.com"], + validationMethod: "email", + validityDays: 365, +}); +``` + +## Multi-Domain Certificate + +Create a certificate covering multiple subdomains with Let's Encrypt. + +```ts +const multiDomainCert = await CertificatePack("multi-cert", { + zone: "example.com", + certificateAuthority: "lets_encrypt", + hosts: [ + "example.com", + "www.example.com", + "api.example.com", + "admin.example.com", + "blog.example.com" + ], + validationMethod: "http", + validityDays: 90, +}); +``` + +## Properties + +| Property | Type | Required | Description | +|----------|------|----------|-------------| +| `zone` | `string \| Zone` | Yes | Zone resource or zone ID where the certificate will be created | +| `certificateAuthority` | `"google" \| "lets_encrypt" \| "ssl_com"` | Yes | Certificate Authority to use for issuing the certificate | +| `hosts` | `string[]` | Yes | List of hostnames (max 50, must include zone apex) | +| `validationMethod` | `"txt" \| "http" \| "email"` | Yes | Domain ownership validation method | +| `validityDays` | `14 \| 30 \| 90 \| 365` | Yes | Certificate validity period in days | +| `cloudflareBranding` | `boolean` | No | Add Cloudflare branding subdomain as Common Name (default: false) | +| `type` | `"advanced"` | No | Certificate type (only "advanced" supported, default: "advanced") | +| `delete` | `boolean` | No | Whether to delete the certificate pack on destroy (default: true) | + +## Certificate Authorities + +### Let's Encrypt (`lets_encrypt`) +- **Cost:** Free +- **Best for:** Basic SSL needs, development environments +- **Validity:** Shorter periods (14, 30, 90 days) +- **Features:** Standard domain validation + +### Google Trust Services (`google`) +- **Cost:** Paid +- **Best for:** Enterprise applications, production environments +- **Validity:** Up to 365 days +- **Features:** Enhanced validation, enterprise support + +### SSL.com (`ssl_com`) +- **Cost:** Commercial +- **Best for:** Commercial applications requiring extended validation +- **Validity:** Up to 365 days +- **Features:** Extended validation options, commercial support + +## Validation Methods + +### TXT Record (`txt`) +- Add DNS TXT record to prove domain ownership +- Most reliable method for automation +- Works with all domain configurations + +### HTTP File (`http`) +- Upload verification file to domain's web server +- Requires web server access +- Good for domains with existing websites + +### Email (`email`) +- Receive validation email at admin addresses +- Requires access to domain admin email +- Manual validation process + +## Important Notes + +### Immutable Properties +Most certificate pack properties cannot be changed after creation. To modify these properties, you must delete and recreate the certificate pack: + +- Certificate Authority (`certificateAuthority`) +- Hostnames (`hosts`) +- Validation Method (`validationMethod`) +- Validity Period (`validityDays`) +- Type (`type`) + +### Updateable Properties +Only `cloudflareBranding` can be updated after creation: + +```ts +// Update to enable Cloudflare branding +const updatedCert = await CertificatePack("my-cert", { + zone: zone, + certificateAuthority: "lets_encrypt", // Must match original + hosts: ["example.com", "www.example.com"], // Must match original + validationMethod: "txt", // Must match original + validityDays: 90, // Must match original + cloudflareBranding: true, // Only this can change +}); +``` + +### Host Requirements +- Maximum 50 hostnames per certificate pack +- Must include the zone apex (root domain) +- Supports wildcards (e.g., `*.example.com`) +- Cannot be empty + +### Provisioning Time +- Certificate packs take time to provision and become active +- Full deployment can take up to 10 minutes +- Monitor the `status` property to track progress + +### Subscription Requirements +Advanced Certificate Packs require a paid Cloudflare plan. Free plans cannot create certificate packs and will receive subscription-related errors. + +## Status Values + +Certificate packs progress through various status values during their lifecycle: + +- `initializing` - Certificate pack creation in progress +- `pending_validation` - Waiting for domain validation +- `pending_issuance` - Certificate being issued by CA +- `pending_deployment` - Certificate being deployed to edge +- `active` - Certificate is live and serving traffic +- `expired` - Certificate has expired +- `deleted` - Certificate pack has been deleted + +Error states include `*_timed_out` variants when operations exceed time limits. + +## Helper Functions + +### Wait for Certificate to Become Active + +```ts +import { waitForCertificatePackActive } from "alchemy/cloudflare/certificate-pack"; + +// Wait for certificate to become active (up to 10 minutes) +const finalStatus = await waitForCertificatePackActive( + api, + zone.id, + certificatePack.id, + 10 * 60 * 1000 // 10 minutes timeout +); + +console.log(`Certificate pack is now: ${finalStatus}`); +``` \ No newline at end of file diff --git a/alchemy-web/docs/providers/cloudflare/container.md b/alchemy-web/docs/providers/cloudflare/container.md new file mode 100644 index 000000000..b0abb405a --- /dev/null +++ b/alchemy-web/docs/providers/cloudflare/container.md @@ -0,0 +1,142 @@ +--- +title: Container +description: Deploy Docker containers on Cloudflare's global network +--- + +# Container + +A Container is a running Docker image running in Cloudflare's global network, managed by a Cloudflare Durable Object. + +> [!CAUTION] +> Cloudflare Containers is still in [Beta](https://blog.cloudflare.com/containers-are-available-in-public-beta-for-simple-global-and-programmable/). + +You'll need: + +1. a `Dockerfile` for your Container +2. an `alchemy.run.ts` to deploy to Cloudflare +3. a `MyContainer` class to own a running Container Instance +4. a `worker.ts` that exports `fetch` and routes requests to Container Instances + +## Container Class + +A Container's lifecycle is managed by a Durable Object class that you define. + +We recommend using the `Container` class from `@cloudflare/containers` since it takes care of the basic container lifecycle for you: + +```ts +import { Container } from "@cloudflare/containers"; +import type { worker } from "../alchemy.run.ts"; + +export class MyContainer extends Container { + declare env: typeof worker.Env; + + defaultPort = 8080; // The default port for the container to listen on + sleepAfter = "3m"; // Sleep the container if no requests are made in this timeframe + + envVars = { + MESSAGE: "I was passed in via the container class!", + }; + + override onStart() { + console.log("Container successfully started"); + } + + override onStop() { + console.log("Container successfully shut down"); + } + + override onError(error: unknown) { + console.log("Container error:", error); + } +} +``` + +## Container Resource + +Now, create a `Container` Resource in your `alchemy.run.ts` file and connect it to your `MyContainer` class: + +```ts +import { Container, Worker } from "alchemy/cloudflare"; +import { Image } from "alchemy/docker"; +// import the type of your Container's implementation +import type { MyContainer } from "./src/container.ts"; + +const container = await Container("my-container", { + className: "MyContainer", // <- and ^ +}); +``` + +This will build your Dockerfile and prepare it for publishing to Cloudflare's Image Registry. + +> [!TIP] +> The default behavior is effectively `docker build . -t my-container` but you can customize the configuration: +> +> ```ts +> const container = await Container("my-container", { +> className: "MyContainer", +> name: "your-container", +> tag: "some-tag", +> build: { +> context: import.meta.dir, +> dockerfile: "Dockerfile.dev", +> }, +> }); +> ``` + +## Bind to Worker + +To deploy the `Container` to Cloudflare, you need to bind it to a `Worker`: + +```ts +export const worker = await Worker("my-worker", { + name: "my-worker", + entrypoint: "./src/worker.ts", + bindings: { + MY_CONTAINER: container, + }, +}); +``` + +> [!NOTE] +> Binding a Container to a Worker will also bind a Durable Object Namespace to the Worker. + +## Route Requests + +To route requests, have your Worker's `fetch` handler resolve a Durable Object instance and proxy the `request` to it: + +```ts +import { getContainer } from "@cloudflare/containers"; +import type { worker } from "../alchemy.run.ts"; + +// the class must be exported for Cloudflare +export { MyContainer } from "./container.ts"; + +export default { + async fetch(request: Request, env: typeof worker.Env): Promise { + const container = getContainer(env.CONTAINER, "container"); + return container.fetch(request); + }, +}; +``` + +> [!TIP] +> Notice how the type of our Worker environment is inferred with `typeof worker.Env`, see the [Type-safe Bindings](../../concepts/bindings.md#type-safe-bindings) documentation for more information. + +## Complex Routing + +Cloudflare's unique design allows you to implement your own routing strategies in pure JavaScript. + +### Round-Robin + +For example, you can round-robin requests across a fixed pool by simply generating a random instance ID between 0 and the number of instances: + +```ts +export async function loadBalance( + binding: DurableObjectNamespace, + instances = 3 +): Promise> { + const containerId = binding.idFromName(`instance-${rand(0, instances)}`); + const container = binding.get(containerId); + return container.fetch(request); +} +``` diff --git a/alchemy-web/docs/providers/cloudflare/custom-domain.md b/alchemy-web/docs/providers/cloudflare/custom-domain.md index 92b037773..a003713b7 100644 --- a/alchemy-web/docs/providers/cloudflare/custom-domain.md +++ b/alchemy-web/docs/providers/cloudflare/custom-domain.md @@ -7,9 +7,43 @@ description: Learn how to configure and manage Custom Domains for your Cloudflar The CustomDomain resource lets you attach a [custom domain](https://developers.cloudflare.com/workers/configuration/routing/custom-domains/) to a Cloudflare Worker. -## Minimal Example +## Worker Domains -Bind a domain to a worker: +The simplest way to bind custom domains is directly on the Worker: + +```ts +import { Worker } from "alchemy/cloudflare"; + +const worker = await Worker("api", { + name: "api-worker", + entrypoint: "./src/api.ts", + domains: ["api.example.com", "admin.example.com"], +}); + +// Access the created domains +console.log(worker.domains); // Array of created CustomDomain resources +``` + +With additional options: + +```ts +const worker = await Worker("api", { + name: "api-worker", + entrypoint: "./src/api.ts", + domains: [ + { + domainName: "api.example.com", + zoneId: "YOUR_ZONE_ID", // Optional - will be inferred if not provided + adopt: true, // Adopt existing domain if it exists + }, + "admin.example.com", // Zone ID will be inferred + ], +}); +``` + +## CustomDomain Resource + +You can also create custom domains independently: ```ts import { Worker, CustomDomain } from "alchemy/cloudflare"; @@ -26,7 +60,7 @@ const domain = await CustomDomain("api-domain", { }); ``` -## With Environment +### With Environment Bind a domain to a specific worker environment: @@ -40,3 +74,6 @@ const domain = await CustomDomain("staging-domain", { environment: "staging", }); ``` + +> [!TIP] +> See the [Routes and Domains](https://developers.cloudflare.com/workers/configuration/routing/#what-is-best-for-me) Cloudflare docs to help decide between when to use a Route vs a Domain. diff --git a/alchemy-web/docs/providers/cloudflare/worker.md b/alchemy-web/docs/providers/cloudflare/worker.md index 8f812fd4f..d36ae4185 100644 --- a/alchemy-web/docs/providers/cloudflare/worker.md +++ b/alchemy-web/docs/providers/cloudflare/worker.md @@ -174,6 +174,32 @@ const frontend = await Worker("frontend", { }); ``` +## Self-Binding + +A worker can bind to itself using `Self` or `WorkerRef`: + +```ts +import { Worker, Self, WorkerRef } from "alchemy/cloudflare"; + +// Using Self +const workerWithSelf = await Worker("my-worker", { + name: "my-worker", + entrypoint: "./src/worker.ts", + bindings: { + SELF: Self, + }, +}); + +// Using WorkerRef with the worker's own ID +const workerWithRef = await Worker("my-worker", { + name: "my-worker", + entrypoint: "./src/worker.ts", + bindings: { + SELF: WorkerRef("my-worker"), + }, +}); +``` + ## Circular Worker Bindings When workers need to bind to each other (circular dependency), use `WorkerStub` to break the cycle: @@ -330,6 +356,26 @@ const worker = await Worker("api", { > [!TIP] > See the [Route](./route.md) for more information. +## Custom Domains + +Bind custom domains directly to your worker for a simpler routing setup: + +```ts +import { Worker } from "alchemy/cloudflare"; + +const worker = await Worker("api", { + name: "api-worker", + entrypoint: "./src/api.ts", + domains: ["api.example.com", "admin.example.com"], +}); + +// Access the created domains +console.log(worker.domains); // Array of created CustomDomain resources +``` + +> [!TIP] +> See the [Routes and Domains](https://developers.cloudflare.com/workers/configuration/routing/#what-is-best-for-me) Cloudflare docs to help decide between when to use a Route vs a Domain. + ## Workers for Platforms Deploy workers to dispatch namespaces for multi-tenant architectures using Cloudflare's Workers for Platforms: diff --git a/alchemy-web/docs/providers/docker/container.md b/alchemy-web/docs/providers/docker/container.md new file mode 100644 index 000000000..68c63caa5 --- /dev/null +++ b/alchemy-web/docs/providers/docker/container.md @@ -0,0 +1,86 @@ +--- +title: Container +description: Deploy and manage Docker containers with Alchemy +--- + +# Container + +The `Container` resource allows you to create and manage Docker containers using Alchemy. + +## Usage + +```typescript +import * as docker from "alchemy/docker"; + +const myContainer = await docker.Container("my-container", { + image: "nginx:latest", + name: "web-server", + ports: [{ external: 80, internal: 80 }], + start: true +}); +``` + +## Properties + +| Name | Type | Required | Description | +|------|------|----------|--------------| +| `image` | `RemoteImage \| string` | Yes | Docker image to use for the container | +| `name` | `string` | No | Name for the container | +| `command` | `string[]` | No | Command to run in the container | +| `environment` | `Record` | No | Environment variables for the container | +| `ports` | `{ external: number \| string, internal: number \| string, protocol?: "tcp" \| "udp" }[]` | No | Port mappings from host to container | +| `volumes` | `{ hostPath: string, containerPath: string, readOnly?: boolean }[]` | No | Volume mappings from host paths to container paths | +| `networks` | `{ name: string, aliases?: string[] }[]` | No | Networks to connect to | +| `restart` | `"no" \| "always" \| "on-failure" \| "unless-stopped"` | No | Restart policy | +| `removeOnExit` | `boolean` | No | Whether to remove the container when it exits | +| `start` | `boolean` | No | Start the container after creation | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| `id` | `string` | The ID of the container | +| `state` | `"created" \| "running" \| "paused" \| "stopped" \| "exited"` | The current state of the container | +| `createdAt` | `number` | Time when the container was created | + +## Example + +```typescript +import * as docker from "alchemy/docker"; + +// Create a Docker network +const network = await docker.Network("app-network", { + name: "microservices-network" +}); + +// Pull the Redis image +const redisImage = await docker.RemoteImage("redis-image", { + name: "redis", + tag: "alpine" +}); + +// Run Redis container +const redis = await docker.Container("redis", { + image: redisImage.imageRef, + name: "redis", + networks: [{ name: network.name }], + start: true +}); + +// Run the application container +const app = await docker.Container("app", { + image: "my-node-app:latest", + name: "web-app", + ports: [{ external: 3000, internal: 3000 }], + networks: [{ name: network.name }], + environment: { + REDIS_HOST: "redis", + NODE_ENV: "production" + }, + volumes: [ + { hostPath: "./logs", containerPath: "/app/logs" } + ], + restart: "always", + start: true +}); +``` diff --git a/alchemy-web/docs/providers/docker/image.md b/alchemy-web/docs/providers/docker/image.md new file mode 100644 index 000000000..eaa36f538 --- /dev/null +++ b/alchemy-web/docs/providers/docker/image.md @@ -0,0 +1,82 @@ +--- +title: Image +description: Build and manage Docker images with Alchemy +--- + +# Image + +The `Image` resource allows you to build and manage Docker images from local Dockerfiles using Alchemy. + +## Usage + +```typescript +import * as docker from "alchemy/docker"; + +const myImage = await docker.Image("app-image", { + name: "my-app", + tag: "v1.0", + build: { + context: "./app" + } +}); +``` + +## Properties + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `name` | `string` | Yes | Docker image name | +| `tag` | `string` | No | Tag for the image (defaults to "latest") | +| `build` | `{ context: string, dockerfile?: string, target?: string, buildArgs?: Record }` | Yes | Build configuration | +| `build.context` | `string` | Yes | Path to the build context (directory containing Dockerfile) | +| `build.dockerfile` | `string` | No | Path to the Dockerfile (relative to context, defaults to "Dockerfile") | +| `build.target` | `string` | No | Target stage to build in multi-stage Dockerfiles | +| `build.buildArgs` | `Record` | No | Build arguments to pass to Docker build | +| `skipPush` | `boolean` | No | Skip pushing the image to a registry (default: true) | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| `imageRef` | `string` | Full image reference (name:tag) | +| `imageId` | `string` | Docker image ID | +| `createdAt` | `number` | Time when the image was built | + +## Example + +```typescript +import * as docker from "alchemy/docker"; + +// Build a Docker image from a Dockerfile +const appImage = await docker.Image("app-image", { + name: "my-node-app", + tag: "1.0", + build: { + context: "./app", + dockerfile: "Dockerfile.prod", + buildArgs: { + NODE_ENV: "production", + API_VERSION: "v2" + } + } +}); + +// Use the built image in a container +const appContainer = await docker.Container("app", { + image: appImage, + ports: [{ external: 3000, internal: 3000 }], + restart: "always", + start: true +}); + +// For multi-stage builds, you can target a specific stage +const builderImage = await docker.Image("builder", { + name: "app-builder", + tag: "latest", + build: { + context: "./app", + target: "builder" // Target the 'builder' stage in a multi-stage Dockerfile + }, + skipPush: true +}); +``` diff --git a/alchemy-web/docs/providers/docker/index.md b/alchemy-web/docs/providers/docker/index.md new file mode 100644 index 000000000..c8f193122 --- /dev/null +++ b/alchemy-web/docs/providers/docker/index.md @@ -0,0 +1,108 @@ +--- +title: Docker Provider +description: Deploy and manage Docker resources using Alchemy +--- + +# Docker Provider + +The Docker provider allows you to create, manage, and orchestrate Docker resources directly from your Alchemy applications. With this provider, you can pull images, run containers, create networks, and more, all using the familiar Alchemy Resource syntax. + +## Resources + +The Docker provider includes the following resources: + +- [RemoteImage](./remote-image.md) - Pull and manage Docker images +- [Image](./image.md) - Build Docker images from local Dockerfiles +- [Container](./container.md) - Run and manage Docker containers +- [Network](./network.md) - Create and manage Docker networks +- [Volume](./volume.md) - Create and manage persistent Docker volumes + +## Example + +Here's a complete example of using the Docker provider to create a web application with Redis, custom images, and persistent volumes: + +```typescript +import * as docker from "alchemy/docker"; + +// Create a Docker network +const network = await docker.Network("app-network", { + name: "my-application-network" +}); + +// Create a persistent volume for Redis data +const redisVolume = await docker.Volume("redis-data", { + name: "redis-data", + labels: [ + { name: "app", value: "my-application" }, + { name: "service", value: "redis" } + ] +}); + +// Pull Redis image +const redisImage = await docker.RemoteImage("redis-image", { + name: "redis", + tag: "alpine" +}); + +// Run Redis container with persistent volume +const redis = await docker.Container("redis", { + image: redisImage.imageRef, + name: "redis", + networks: [{ name: network.name }], + volumes: [ + { + hostPath: redisVolume.name, + containerPath: "/data" + } + ], + start: true +}); + +// Build a custom application image from local Dockerfile +const appImage = await docker.Image("app-image", { + name: "my-web-app", + tag: "latest", + build: { + context: "./app", + buildArgs: { + NODE_ENV: "production" + } + } +}); + +// Create a volume for application logs +const logsVolume = await docker.Volume("logs-volume", { + name: "app-logs", + labels: { + "com.example.environment": "production", + "com.example.backup": "daily" + } +}); + +// Run the application container +const app = await docker.Container("app", { + image: appImage, // Using the custom built image + name: "web-app", + ports: [{ external: 3000, internal: 3000 }], + networks: [{ name: network.name }], + volumes: [ + { + hostPath: logsVolume.name, + containerPath: "/app/logs" + } + ], + environment: { + REDIS_HOST: "redis", + NODE_ENV: "production" + }, + restart: "always", + start: true +}); + +// Output the URL +export const url = `http://localhost:3000`; +``` + +## Additional Resources + +For more complex examples, see the [Docker Example](https://github.com/sam-goodwin/alchemy/tree/main/examples/docker) in the Alchemy repository. diff --git a/alchemy-web/docs/providers/docker/network.md b/alchemy-web/docs/providers/docker/network.md new file mode 100644 index 000000000..7121c0bee --- /dev/null +++ b/alchemy-web/docs/providers/docker/network.md @@ -0,0 +1,120 @@ +--- +title: Network +description: Create and manage Docker networks with Alchemy +--- + +# Network + +The `Network` resource allows you to create and manage Docker networks using Alchemy, enabling container-to-container communication. + +## Usage + +```typescript +import * as docker from "alchemy/docker"; + +const network = await docker.Network("app-network", { + name: "app-network" +}); +``` + +## Properties + +| Name | Type | Required | Description | +|------|------|----------|--------------| +| `name` | `string` | Yes | Network name | +| `driver` | `"bridge" \| "host" \| "none" \| "overlay" \| "macvlan" \| string` | No | Network driver to use | +| `enableIPv6` | `boolean` | No | Enable IPv6 on the network | +| `labels` | `Record` | No | Network-scoped alias for containers | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| `id` | `string` | Network ID | +| `createdAt` | `number` | Time when the network was created | + +## Example + +```typescript +import * as docker from "alchemy/docker"; + +// Create a simple bridge network +const appNetwork = await docker.Network("app-network", { + name: "app-network" +}); + +// Create a custom network with driver +const overlayNetwork = await docker.Network("overlay-network", { + name: "overlay-network", + driver: "overlay", + enableIPv6: true, + labels: { + "com.example.description": "Network for application services" + } +}); + +// Create containers connected to the network +const service1 = await docker.Container("service1", { + image: "service1:latest", + name: "service1", + networks: [{ name: appNetwork.name }], + start: true +}); + +const service2 = await docker.Container("service2", { + image: "service2:latest", + name: "service2", + networks: [{ name: appNetwork.name }], + environment: { + // Service discovery using container names + SERVICE1_URL: `http://service1:8080` + }, + start: true +}); +``` + +## Network Communication + +When containers are connected to the same Docker network, they can communicate with each other using the container names as hostnames. This built-in DNS resolution simplifies service discovery in multi-container applications. + +```typescript +const service1 = await docker.Container("service1", { + image: "service1:latest", + name: "service1", + networks: [{ name: appNetwork.name }], + start: true +}); + +const service2 = await docker.Container("service2", { + image: "service2:latest", + name: "service2", + networks: [{ name: appNetwork.name }], + environment: { + // Service discovery using container names + SERVICE1_URL: `http://service1:8080` + }, + start: true +}); +``` + +Or, you can set aliases for the container to make it accessible by multiple names: + +```typescript +const service1 = await docker.Container("service1", { + image: "service1:latest", + name: "service1", + networks: [{ name: appNetwork.name, aliases: ["api"] }], + start: true +}); + +const service2 = await docker.Container("service2", { + image: "service2:latest", + name: "service2", + networks: [{ name: appNetwork.name }], + environment: { + // Service discovery using container names + SERVICE1_URL: `http://api:8080` + }, + start: true +}); +``` diff --git a/alchemy-web/docs/providers/docker/remote-image.md b/alchemy-web/docs/providers/docker/remote-image.md new file mode 100644 index 000000000..b924ebb41 --- /dev/null +++ b/alchemy-web/docs/providers/docker/remote-image.md @@ -0,0 +1,56 @@ +--- +title: RemoteImage +description: Pull and manage Docker images with Alchemy +--- + +# RemoteImage + +The `RemoteImage` resource allows you to pull and manage Docker images using Alchemy. + +## Usage + +```typescript +import * as docker from "alchemy/docker"; + +const myImage = await docker.RemoteImage("nginx", { + name: "nginx", + tag: "latest", +}); +``` + +## Properties + +| Name | Type | Required | Description | +|------|------|----------|--------------| +| `name` | `string` | Yes | Docker image name (e.g., "nginx") | +| `tag` | `string` | No | Tag for the image (e.g., "latest" or "1.19-alpine") | +| `alwaysPull` | `boolean` | No | Always attempt to pull the image, even if it exists locally | + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| `imageRef` | `string` | Full image reference (name:tag) | +| `createdAt` | `number` | Time when the image was created or pulled | + +## Example + +```typescript +import * as docker from "alchemy/docker"; + +// Pull the nginx image +const nginxImage = await docker.RemoteImage("nginx", { + name: "nginx", + tag: "latest" +}); + +// Pull a specific version of Node.js +const nodeImage = await docker.RemoteImage("node-app", { + name: "node", + tag: "16-alpine", + alwaysPull: true +}); + +// The full image reference can be used when creating containers +console.log(`Pulled image: ${nginxImage.imageRef}`); +``` diff --git a/alchemy-web/docs/providers/docker/volume.md b/alchemy-web/docs/providers/docker/volume.md new file mode 100644 index 000000000..94e35508c --- /dev/null +++ b/alchemy-web/docs/providers/docker/volume.md @@ -0,0 +1,121 @@ +--- +title: Volume +description: Create and manage Docker volumes with Alchemy +--- + +# Volume + +The `Volume` resource allows you to create and manage persistent Docker volumes using Alchemy. + +## Usage + +```typescript +import * as docker from "alchemy/docker"; + +const myVolume = await docker.Volume("data-volume", { + name: "app-data", + driver: "local" +}); +``` + +## Properties + +| Name | Type | Required | Description | +|------|------|----------|-------------| +| `name` | `string` | Yes | Docker volume name | +| `driver` | `string` | No | Volume driver to use (defaults to "local") | +| `driverOpts` | `Record` | No | Driver-specific options | +| `labels` | `VolumeLabel[] \| Record` | No | Custom metadata labels for the volume | + +The `VolumeLabel` interface has the following structure: +```typescript +interface VolumeLabel { + name: string; // Label name + value: string; // Label value +} +``` + +## Outputs + +| Name | Type | Description | +|------|------|-------------| +| `id` | `string` | Volume ID (same as name for Docker volumes) | +| `mountpoint` | `string` | Volume mountpoint path on the host | +| `createdAt` | `number` | Time when the volume was created | + +## Example + +```typescript +import * as docker from "alchemy/docker"; + +// Create a simple Docker volume for persistent data +const dataVolume = await docker.Volume("data-volume", { + name: "postgres-data" +}); + +// Create a Docker volume with custom driver options +const dbVolume = await docker.Volume("db-data", { + name: "mysql-data", + driver: "local", + driverOpts: { + "type": "nfs", + "o": "addr=10.0.0.1,rw", + "device": ":/path/to/dir" + } +}); + +// Create a volume with labels (array format) +const logsVolume = await docker.Volume("logs-volume", { + name: "app-logs", + labels: [ + { name: "com.example.environment", value: "production" }, + { name: "com.example.created-by", value: "alchemy" } + ] +}); + +// Create a volume with labels (record format) +const configVolume = await docker.Volume("config-volume", { + name: "app-config", + labels: { + "com.example.environment": "staging", + "com.example.created-by": "alchemy" + } +}); + +// Use volumes with a container +const dbContainer = await docker.Container("database", { + image: "postgres:14", + name: "postgres", + volumes: [ + { + hostPath: dataVolume.name, // Reference the volume by name + containerPath: "/var/lib/postgresql/data" + }, + { + hostPath: logsVolume.name, + containerPath: "/var/log/postgresql", + readOnly: false + } + ], + environment: { + POSTGRES_PASSWORD: "secret" + }, + restart: "always", + start: true +}); +``` + +## Using Docker Volumes for Persistence + +Docker volumes are the preferred mechanism for persisting data generated by and used by Docker containers. Their benefits include: + +1. **Data Persistence**: Data stored in volumes persists even when containers are stopped or removed +2. **Performance**: Better performance than bind mounts, especially on Windows and macOS +3. **Portability**: Volumes can be easily backed up, restored, and migrated +4. **Driver Support**: Support for various storage backends through volume drivers + +When using Docker volumes with Alchemy, it's a common pattern to: +1. Create volumes with meaningful names +2. Assign metadata using labels +3. Reference volumes in containers by name +4. Configure volume permissions with the `readOnly` flag when mounting diff --git a/alchemy-web/docs/providers/github/index.md b/alchemy-web/docs/providers/github/index.md index b4fdf3169..faf7e022c 100644 --- a/alchemy-web/docs/providers/github/index.md +++ b/alchemy-web/docs/providers/github/index.md @@ -8,12 +8,13 @@ GitHub is a web-based version control and collaboration platform that provides G - [Comment](./comment.md) - Create and manage comments on issues and pull requests - [RepositoryEnvironment](./repository-environment.md) - Create and manage deployment environments with protection rules +- [RepositoryWebhook](./repository-webhook.md) - Create and manage repository webhooks for event notifications - [Secret](./secret.md) - Create and manage GitHub Actions and Dependabot secrets ## Example Usage ```ts -import { Comment, RepositoryEnvironment, GitHubSecret } from "alchemy/github"; +import { Comment, RepositoryEnvironment, RepositoryWebhook, GitHubSecret } from "alchemy/github"; // Create a repository environment const prodEnv = await RepositoryEnvironment("production", { @@ -32,6 +33,15 @@ const prodEnv = await RepositoryEnvironment("production", { }, }); +// Create a webhook for CI/CD notifications +const webhook = await RepositoryWebhook("ci-webhook", { + owner: "my-org", + repository: "my-repo", + url: "https://ci.example.com/webhook", + secret: alchemy.secret("GITHUB_WEBHOOK_SECRET"), + events: ["push", "pull_request", "release"], +}); + // Create a secret for the environment const secret = await GitHubSecret("deploy-key", { owner: "my-org", diff --git a/alchemy-web/docs/providers/github/repository-webhook.md b/alchemy-web/docs/providers/github/repository-webhook.md new file mode 100644 index 000000000..325d89daa --- /dev/null +++ b/alchemy-web/docs/providers/github/repository-webhook.md @@ -0,0 +1,186 @@ +# RepositoryWebhook + +Manage GitHub repository webhooks with automatic lifecycle management. + +Webhooks allow external services to be notified when certain events happen in a repository. This resource manages the full lifecycle of repository webhooks including creation, updates, and deletion. + +## Basic Usage + +Create a simple webhook for push events: + +```ts +import { RepositoryWebhook } from "alchemy/github"; + +const webhook = await RepositoryWebhook("my-webhook", { + owner: "my-org", + repository: "my-repo", + url: "https://my-service.com/github-webhook", + events: ["push"] +}); +``` + +## With Secret Validation + +Add webhook secret for payload validation: + +```ts +import { RepositoryWebhook } from "alchemy/github"; +import { alchemy } from "alchemy"; + +const webhook = await RepositoryWebhook("secure-webhook", { + owner: "my-org", + repository: "my-repo", + url: "https://ci.example.com/webhook", + secret: alchemy.secret("GITHUB_WEBHOOK_SECRET"), + events: ["push", "pull_request", "release"], + contentType: "application/json" +}); +``` + +## Multiple Events + +Listen to multiple GitHub events: + +```ts +import { RepositoryWebhook } from "alchemy/github"; + +const ciWebhook = await RepositoryWebhook("ci-webhook", { + owner: "my-org", + repository: "my-repo", + url: "https://ci.example.com/webhook", + events: [ + "push", + "pull_request", + "release", + "issues", + "issue_comment" + ] +}); +``` + +## All Events + +Create a webhook that listens to all repository events: + +```ts +import { RepositoryWebhook } from "alchemy/github"; + +const monitoringWebhook = await RepositoryWebhook("monitoring-webhook", { + owner: "my-org", + repository: "my-repo", + url: "https://monitoring.internal.com/github", + events: ["*"], // Listen to all events + insecureSsl: true, // For internal services with self-signed certs + contentType: "application/x-www-form-urlencoded" +}); +``` + +## Custom SSL Configuration + +For internal services or development environments: + +```ts +import { RepositoryWebhook } from "alchemy/github"; + +const devWebhook = await RepositoryWebhook("dev-webhook", { + owner: "my-org", + repository: "my-repo", + url: "https://localhost:3000/webhook", + insecureSsl: true, // Skip SSL verification + active: false, // Create inactive webhook + events: ["push", "pull_request"] +}); +``` + +## Properties + +| Property | Type | Required | Description | +|----------|------|----------|-------------| +| `owner` | string | ✅ | Repository owner (user or organization) | +| `repository` | string | ✅ | Repository name | +| `url` | string | ✅ | The URL to which the payloads will be delivered | +| `secret` | string | | Webhook secret for payload validation | +| `contentType` | `"application/json"` \| `"application/x-www-form-urlencoded"` | | The media type used to serialize the payloads (default: `"application/json"`) | +| `insecureSsl` | boolean | | Determines whether the SSL certificate of the host for url will be verified (default: `false`) | +| `active` | boolean | | Determines if notifications are sent when the webhook is triggered (default: `true`) | +| `events` | string[] | | Determines what events the hook is triggered for (default: `["push"]`) | +| `token` | string | | Optional GitHub API token (overrides environment variable) | + +## Returns + +| Property | Type | Description | +|----------|------|-------------| +| `id` | string | Resource identifier | +| `webhookId` | number | The numeric ID of the webhook in GitHub | +| `url` | string | The webhook URL that was configured | +| `createdAt` | string | Time at which the webhook was created | +| `updatedAt` | string | Time at which the webhook was last updated | +| `pingUrl` | string | The ping URL for testing the webhook | +| `testUrl` | string | The test URL for the webhook | + +## GitHub Events + +Common GitHub webhook events you can listen to: + +- `push` - Any Git push to a Repository +- `pull_request` - Pull request activity +- `issues` - Issue activity +- `issue_comment` - Issue comment activity +- `release` - Release activity +- `create` - Branch or tag created +- `delete` - Branch or tag deleted +- `fork` - Repository forked +- `star` - Repository starred +- `watch` - Repository watched +- `workflow_run` - GitHub Actions workflow run +- `*` - All events + +For a complete list of available events, see the [GitHub Webhooks documentation](https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads). + +## Authentication + +The resource requires a GitHub token with appropriate permissions: + +```bash +export GITHUB_TOKEN="ghp_your_token_here" +``` + +The token must have: +- `repo` scope for private repositories +- `public_repo` scope for public repositories +- Admin access to the repository to manage webhooks + +## Error Handling + +The resource handles common GitHub API errors: + +- **403 Forbidden**: Token lacks admin rights to the repository +- **422 Unprocessable Entity**: Invalid webhook configuration (bad URL, invalid events, etc.) +- **404 Not Found**: Repository doesn't exist or token lacks access + +## Updates + +When updating a webhook, you can change: +- Webhook URL +- Events list +- Secret +- Content type +- SSL verification settings +- Active status + +The webhook ID remains the same during updates. + +## Security + +- Always use HTTPS URLs for webhook endpoints +- Use webhook secrets to validate payload authenticity +- Consider using `insecureSsl: false` (default) for production +- Regularly rotate webhook secrets + +## Best Practices + +1. **Use secrets**: Always configure webhook secrets for payload validation +2. **Specific events**: Only listen to events your application needs +3. **Error handling**: Implement proper error handling in your webhook receiver +4. **Testing**: Use the `pingUrl` to test webhook connectivity +5. **Monitoring**: Monitor webhook delivery success rates in GitHub \ No newline at end of file diff --git a/alchemy-web/docs/providers/stripe/price.md b/alchemy-web/docs/providers/stripe/price.md index 380b2ef3c..3689e9857 100644 --- a/alchemy-web/docs/providers/stripe/price.md +++ b/alchemy-web/docs/providers/stripe/price.md @@ -153,3 +153,33 @@ const cappedUsagePrice = await Price("api-calls-capped", { ], }); ``` + +## Billing Meters + +For advanced usage tracking, you can associate a price with a Stripe Billing Meter: + +```ts +import { Price } from "alchemy/stripe"; + +// First create a meter (not shown - requires Meter resource) +// const meter = await Meter("api-usage-meter", { ... }); + +const meteredPrice = await Price("api-usage-with-meter", { + product: "prod_xyz", + currency: "usd", + billingScheme: "tiered", + tiersMode: "graduated", + recurring: { + interval: "month", + usageType: "metered", // Required for meter association + meter: "meter_123abc" // Associate with billing meter + }, + tiers: [ + { upTo: 10000, unitAmountDecimal: "0" }, + { upTo: 25000, unitAmountDecimal: "0.002" }, + { upTo: "inf", flatAmountDecimal: "3000" } + ], +}); +``` + +**Note**: Meters can only be associated with prices that have `recurring.usageType = 'metered'`. diff --git a/alchemy/.gitignore b/alchemy/.gitignore index 16675cf6c..fee38e27d 100644 --- a/alchemy/.gitignore +++ b/alchemy/.gitignore @@ -1,2 +1,4 @@ -bin/* -!bin/*.ts \ No newline at end of file +bin/alchemy.mjs +!templates/**/wrangler.jsonc +.nuxt +workers/*.js diff --git a/alchemy/bin/alchemy.ts b/alchemy/bin/alchemy.ts index 0659da664..6f9180cad 100644 --- a/alchemy/bin/alchemy.ts +++ b/alchemy/bin/alchemy.ts @@ -1,93 +1,88 @@ #!/usr/bin/env node -import { parseArgs } from "node:util"; -import { createAlchemy } from "./create-alchemy.ts"; -import { bootstrapS3 } from "./bootstrap-s3.ts"; -// Parse command-line arguments. We allow unknown flags because different -// sub-commands may accept different sets. -const { values, positionals } = parseArgs({ - allowPositionals: true, - // We keep the option list flat – sub-commands will decide which ones they care about. - options: { - template: { type: "string" }, - yes: { type: "boolean", short: "y" }, - overwrite: { type: "boolean" }, - help: { type: "boolean", short: "h" }, - version: { type: "boolean", short: "v" }, - region: { type: "string" }, - prefix: { type: "string" }, - }, -}); - -// First positional is the sub-command (e.g. `create`) -const command = positionals.shift(); - -const usage = `Usage: alchemy [options] - -Available commands: - create Scaffold a new project - bootstrap Bootstrap cloud resources for alchemy - -Bootstrap options: - --region AWS region (defaults to AWS profile default) - --prefix S3 bucket name prefix (default: alchemy-state) -`; +import { createCli, trpcServer, zod as z } from "trpc-cli"; +import { createAlchemy } from "./commands/create.ts"; +import { getPackageVersion } from "./services/get-package-version.ts"; +import { + PackageManagerSchema, + ProjectNameSchema, + TemplateSchema, + type CreateInput, +} from "./types.ts"; -if (!command) { - console.error(usage); - process.exit(1); -} +const t = trpcServer.initTRPC.create(); -switch (command) { - case "create": { - // The first remaining positional is treated as the project name. - const name = positionals.shift(); - - // If the user explicitly requested help or version, forward the flags even - // when no project name is provided so the underlying handler can display - // the appropriate information. - if (values.help || values.version) { - await createAlchemy({ +const router = t.router({ + create: t.procedure + .meta({ + description: "Create a new Alchemy project", + }) + .input( + z.tuple([ + ProjectNameSchema.optional(), + z + .object({ + template: TemplateSchema.optional(), + packageManager: PackageManagerSchema.optional(), + yes: z + .boolean() + .optional() + .default(false) + .describe("Skip prompts and use defaults"), + overwrite: z + .boolean() + .optional() + .default(false) + .describe("Overwrite existing directory"), + bun: z + .boolean() + .optional() + .default(false) + .describe("Use Bun as the package manager"), + npm: z + .boolean() + .optional() + .default(false) + .describe("Use npm as the package manager"), + pnpm: z + .boolean() + .optional() + .default(false) + .describe("Use pnpm as the package manager"), + yarn: z + .boolean() + .optional() + .default(false) + .describe("Use Yarn as the package manager"), + install: z + .boolean() + .optional() + .describe("Install dependencies after scaffolding"), + }) + .optional() + .default({}), + ]), + ) + .mutation(async ({ input }) => { + const [name, options] = input; + const isTest = process.env.NODE_ENV === "test"; + const combinedInput: CreateInput = { name, - template: values.template as string | undefined, - yes: values.yes as boolean | undefined, - overwrite: values.overwrite as boolean | undefined, - help: values.help as boolean | undefined, - version: values.version as boolean | undefined, - }); - break; - } - - await createAlchemy({ - name, - template: values.template as string | undefined, - yes: values.yes as boolean | undefined, - overwrite: values.overwrite as boolean | undefined, - help: values.help as boolean | undefined, - version: values.version as boolean | undefined, - }); - break; - } + ...options, + yes: isTest || options.yes, + }; + await createAlchemy(combinedInput); + }), +}); - case "bootstrap": { - const subcommand = positionals.shift(); +export type AppRouter = typeof router; - if (subcommand === "s3") { - await bootstrapS3({ - region: values.region as string | undefined, - prefix: values.prefix as string | undefined, - help: values.help as boolean | undefined, - }); - } else { - console.error(`Unknown bootstrap subcommand: ${subcommand || "(none)"}`); - console.error("Available bootstrap subcommands:"); - console.error(" s3 Create S3 bucket for state storage"); - process.exit(1); - } - break; - } +const cli = createCli({ + router, + name: "alchemy", + version: getPackageVersion(), + description: + "🧪 Welcome to Alchemy! Creating infrastructure as code with JavaScript and TypeScript.", +}); - default: - console.error(`Unknown command: ${command}`); - process.exit(1); -} +cli.run(); diff --git a/alchemy/bin/commands/create.ts b/alchemy/bin/commands/create.ts new file mode 100644 index 000000000..95989fe81 --- /dev/null +++ b/alchemy/bin/commands/create.ts @@ -0,0 +1,263 @@ +import { + cancel, + confirm, + intro, + isCancel, + log, + note, + outro, + select, + spinner, + text, +} from "@clack/prompts"; +import * as fs from "fs-extra"; +import { existsSync } from "node:fs"; +import { join, resolve } from "node:path"; +import pc from "picocolors"; + +import { throwWithContext } from "../errors.ts"; +import { detectPackageManager } from "../services/package-manager.ts"; +import { copyTemplate } from "../services/template-manager.ts"; +import type { CreateInput, ProjectContext, TemplateType } from "../types.ts"; +import { ProjectNameSchema, TEMPLATE_DEFINITIONS } from "../types.ts"; + +const isTest = process.env.NODE_ENV === "test"; + +async function createProjectContext( + cliOptions: CreateInput, +): Promise { + const detectedPm = detectPackageManager(); + const options = { yes: isTest, ...cliOptions }; + + let name: string; + if (options.name) { + const result = ProjectNameSchema.safeParse(options.name); + if (!result.success) { + throw new Error( + `Invalid project name: ${result.error.errors[0]?.message}`, + ); + } + name = options.name; + log.info(`Using project name: ${pc.yellow(name)}`); + } else { + const nameResult = await text({ + message: "What is your project name?", + placeholder: "my-alchemy-app", + validate: (value) => { + const result = ProjectNameSchema.safeParse(value); + return result.success ? undefined : result.error.errors[0]?.message; + }, + }); + + if (isCancel(nameResult)) { + cancel(pc.red("Operation cancelled.")); + process.exit(0); + } + + name = nameResult; + } + + let selectedTemplate: TemplateType; + if (options.template) { + selectedTemplate = options.template; + log.info(`Using template: ${pc.yellow(selectedTemplate)}`); + } else { + const templateResult = await select({ + message: "Which template would you like to use?", + options: TEMPLATE_DEFINITIONS.map((t) => ({ + label: t.description, + value: t.name as TemplateType, + })), + }); + + if (isCancel(templateResult)) { + cancel(pc.red("Operation cancelled.")); + process.exit(0); + } + + selectedTemplate = templateResult; + } + + const templateDefinition = TEMPLATE_DEFINITIONS.find( + (t) => t.name === selectedTemplate, + ); + if (!templateDefinition) { + throw new Error( + `Template '${pc.yellow(selectedTemplate)}' not found. Available templates: ${TEMPLATE_DEFINITIONS.map((t) => pc.cyan(t.name)).join(", ")}`, + ); + } + + const path = resolve(process.cwd(), name); + let packageManager = options.packageManager || detectedPm; + + // Override package manager if specific flags are provided + if (options.bun) packageManager = "bun"; + else if (options.npm) packageManager = "npm"; + else if (options.pnpm) packageManager = "pnpm"; + else if (options.yarn) packageManager = "yarn"; + + let shouldInstall = true; + if (options.install !== undefined) { + shouldInstall = options.install; + log.info( + `Dependencies installation: ${pc.yellow(shouldInstall ? "enabled" : "disabled")}`, + ); + } else if (!options.yes) { + const installResult = await confirm({ + message: "Install dependencies?", + initialValue: true, + }); + + if (isCancel(installResult)) { + cancel(pc.red("Operation cancelled.")); + process.exit(0); + } + + shouldInstall = installResult; + } + + return { + name, + path, + template: selectedTemplate, + packageManager, + isTest, + options: { + ...options, + install: shouldInstall, + }, + }; +} + +async function handleDirectoryOverwrite( + context: ProjectContext, +): Promise { + if (!existsSync(context.path)) { + return; + } + + let shouldOverwrite = false; + + if (context.options.overwrite) { + shouldOverwrite = true; + log.warn( + `Directory ${pc.yellow(context.name)} already exists. Overwriting due to ${pc.cyan("--overwrite")} flag.`, + ); + } else { + const overwriteResult = await confirm({ + message: `Directory ${pc.yellow(context.name)} already exists. Overwrite?`, + initialValue: false, + }); + + if (isCancel(overwriteResult)) { + cancel(pc.red("Operation cancelled.")); + process.exit(0); + } + + shouldOverwrite = overwriteResult; + } + + if (!shouldOverwrite) { + cancel(pc.red("Operation cancelled.")); + process.exit(0); + } + + const s = spinner(); + s.start(`Removing existing directory: ${pc.yellow(context.path)}`); + try { + await fs.rm(context.path, { recursive: true, force: true }); + s.stop(`Directory ${pc.yellow(context.path)} removed.`); + } catch (error) { + s.stop(pc.red(`Failed to remove directory ${pc.yellow(context.path)}.`)); + throwWithContext(error, "Directory removal failed"); + } +} + +async function initializeTemplate(context: ProjectContext): Promise { + const templateDefinition = TEMPLATE_DEFINITIONS.find( + (t) => t.name === context.template, + ); + if (!templateDefinition) { + throw new Error(`Template definition not found for: ${context.template}`); + } + + try { + await copyTemplate(context.template, context); + } catch (error) { + throwWithContext( + error, + `Template initialization failed for '${context.template}'`, + ); + } + + // Create .gitignore if it doesn't exist + const gitignorePath = join(context.path, ".gitignore"); + if (!existsSync(gitignorePath)) { + try { + await fs.writeFile( + gitignorePath, + "node_modules/\n.env\n.env.local\ndist/\nlib/\n.wrangler/\nwrangler.jsonc\n*.tsbuildinfo\n", + ); + } catch (error) { + const errorMsg = error instanceof Error ? error.message : String(error); + log.warn(`Failed to create .gitignore: ${errorMsg}`); + } + } +} + +export async function createAlchemy(cliOptions: CreateInput): Promise { + try { + intro(pc.cyan("🧪 Welcome to Alchemy!")); + log.info("Creating a new Alchemy project..."); + + const context = await createProjectContext(cliOptions); + + log.info(`Detected package manager: ${pc.green(context.packageManager)}`); + + await handleDirectoryOverwrite(context); + + await initializeTemplate(context); + + const installInstructions = + context.options.install === false + ? ` +${pc.cyan("📦 Install dependencies:")} + cd ${context.name} + ${context.packageManager} install + +` + : ""; + + note( + ` +${pc.cyan("📁 Navigate to your project:")} + cd ${context.name} + +${installInstructions}${pc.cyan("🚀 Deploy your project:")} + ${context.packageManager} run deploy + +${pc.cyan("🧹 Destroy your project:")} + ${context.packageManager} run destroy + +${pc.cyan("📚 Learn more:")} + https://alchemy.run +`, + "Next Steps:", + ); + + outro( + pc.green(`✅ Project ${pc.yellow(context.name)} created successfully!`), + ); + } catch (error) { + log.error("An unexpected error occurred:"); + if (error instanceof Error) { + log.error(`${pc.red("Error:")} ${error.message}`); + if (error.stack && process.env.DEBUG) { + log.error(`${pc.gray("Stack trace:")}\n${error.stack}`); + } + } else { + log.error(pc.red(String(error))); + } + process.exit(1); + } +} diff --git a/alchemy/bin/constants.ts b/alchemy/bin/constants.ts new file mode 100644 index 000000000..73b73f4f7 --- /dev/null +++ b/alchemy/bin/constants.ts @@ -0,0 +1,13 @@ +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import { getPackageVersion } from "./services/get-package-version.ts"; + +const __filename = fileURLToPath(import.meta.url); +const distPath = path.dirname(__filename); +export const PKG_ROOT = path.join(distPath, "../"); + +export const dependencyVersionMap = { + alchemy: getPackageVersion(), +} as const; + +export type DependencyVersionMap = keyof typeof dependencyVersionMap; diff --git a/alchemy/bin/create-alchemy.ts b/alchemy/bin/create-alchemy.ts deleted file mode 100644 index ee1d883f9..000000000 --- a/alchemy/bin/create-alchemy.ts +++ /dev/null @@ -1,1214 +0,0 @@ -#!/usr/bin/env node - -import { confirm, input, select } from "@inquirer/prompts"; -import { applyEdits, modify } from "jsonc-parser"; -import { execSync } from "node:child_process"; -import { existsSync } from "node:fs"; -import * as fs from "node:fs/promises"; -import { join, resolve } from "node:path"; - -const isTest = process.env.NODE_ENV === "test"; - -// Package manager detection -type PackageManager = "bun" | "npm" | "pnpm" | "yarn"; - -// CLI options interface -interface CliOptions { - name?: string; - template?: string; - yes?: boolean; - overwrite?: boolean; - help?: boolean; - version?: boolean; -} - -// Mutable state that will be initialised inside `createAlchemy()` and -// reused by the helper utilities defined later in the file. Keeping these -// at module scope preserves the existing references inside helper -// functions without requiring any further changes. -let options: CliOptions = { yes: isTest }; -let pm: PackageManager; -let alchemyVersion: string; -let projectPath: string; -let projectName: string; -let template: string; - -// Define templates -const templates: Template[] = [ - { - name: "typescript", - description: "Basic TypeScript Worker project", - init: initTypescriptProject, - }, - { - name: "vite", - description: "React Vite.js application", - init: initViteProject, - }, - { - name: "astro", - description: "Astro application with SSR", - init: initAstroProject, - }, - { - name: "react-router", - description: "React Router application", - init: initReactRouterProject, - }, - { - name: "sveltekit", - description: "SvelteKit application", - init: initSvelteKitProject, - }, - { - name: "tanstack-start", - description: "TanStack Start application", - init: initTanstackStartProject, - }, - { - name: "rwsdk", - description: "Redwood SDK application", - init: initRedwoodProject, - }, - { - name: "nuxt", - description: "Nuxt.js application", - init: initNuxtProject, - }, -]; - -// ------------------------------------------------------------------------------------------------- -// Public API – this is invoked by the parent `alchemy` CLI wrapper. It reproduces the original -// behaviour but relies on the caller to provide the already-parsed command-line options. -// ------------------------------------------------------------------------------------------------- - -export async function createAlchemy( - cliOptions: Partial = {}, -): Promise { - // Merge with defaults so helper functions can keep referencing the global `options` - options = { yes: isTest, ...cliOptions }; - - // Handle help / version flags early - if (options.help) { - console.log(` -Usage: alchemy create [options] - -Options: --h, --help Show help --v, --version Show version - Project name (positional / non-interactive) ---template= Template name (non-interactive) --y, --yes Skip confirmations (non-interactive) ---overwrite Overwrite existing directory - -Templates: -${templates.map((t) => ` ${t.name.padEnd(15)} ${t.description}`).join("\n")} -`); - return; - } - - if (options.version) { - console.log("0.28.0"); - return; - } - - console.log("🧪 Welcome to Alchemy!"); - console.log("Creating a new Alchemy project...\n"); - - pm = detectPackageManager(); - console.log(`Detected package manager: ${pm}\n`); - - // Acquire project name – prompt if not provided - if (options.name) { - projectName = options.name; - console.log(`Using project name: ${projectName}`); - } else { - projectName = await input({ - message: "What is your project name?", - default: "my-alchemy-app", - validate: (input) => { - if (!input.trim()) return "Project name is required"; - if (!/^[a-z0-9-_]+$/i.test(input)) - return "Project name can only contain letters, numbers, hyphens, and underscores"; - return true; - }, - }); - } - - // Validate project name (even if provided non-interactively) - if (!projectName.trim()) { - throw new Error("Project name is required"); - } - if (!/^[a-z0-9-_]+$/i.test(projectName)) { - throw new Error( - "Project name can only contain letters, numbers, hyphens, and underscores", - ); - } - - // Select template – prompt if not provided - if (options.template) { - template = options.template; - console.log(`Using template: ${template}`); - - if (!templates.find((t) => t.name === template)) { - throw new Error( - `Template '${template}' not found. Available templates: ${templates.map((t) => t.name).join(", ")}`, - ); - } - } else { - template = await select({ - message: "Which template would you like to use?", - choices: templates.map((t) => ({ - name: t.description, - value: t.name, - })), - }); - } - - const selectedTemplate = templates.find((t) => t.name === template)!; - - // Prepare working directory - projectPath = resolve(process.cwd(), projectName); - - if (existsSync(projectPath)) { - let overwriteConfirmed: boolean; - if (options.overwrite || options.yes) { - overwriteConfirmed = true; - console.log( - `Directory ${projectName} already exists. Overwriting due to CLI flag.`, - ); - } else { - overwriteConfirmed = await confirm({ - message: `Directory ${projectName} already exists. Overwrite?`, - default: false, - }); - } - - if (!overwriteConfirmed) { - console.log("Cancelled."); - return; - } - } - - console.log(`\n🔨 Creating ${template} project in ${projectPath}...`); - - alchemyVersion = `alchemy${isTest ? "@file:../../alchemy" : ""}`; - - // Execute the template initialisation - await selectedTemplate.init(projectName, projectPath); - - // Ensure a .gitignore exists - const gitignorePath = join(projectPath, ".gitignore"); - if (!existsSync(gitignorePath)) { - await fs.writeFile( - gitignorePath, - "node_modules/\n.env\n.env.local\ndist/\nlib/\n.wrangler/\nwrangler.jsonc\n*.tsbuildinfo\n", - ); - } - - console.log(`\n✅ Project ${projectName} created successfully!`); - console.log("\n📁 Navigate to your project:"); - console.log(` cd ${projectName}`); - console.log("\n🚀 Deploy your project:"); - console.log(` ${pm} run deploy`); - console.log("\n🧹 Destroy your project:"); - console.log(` ${pm} run destroy`); - console.log("\n📚 Learn more: https://alchemy.run"); -} - -// Template definitions -interface Template { - name: string; - description: string; - init: (projectName: string, projectPath: string) => Promise; -} - -async function initTypescriptProject( - projectName: string, - projectPath: string, -): Promise { - await mkdir(projectPath); - - const commands = getPackageManagerCommands(pm); - - // Initialize project - execCommand(commands.init, projectPath); - - await createEnvTs(projectPath); - await initWranglerRunTs(projectPath, { - entrypoint: "src/worker.ts", - }); - await appendGitignore(projectPath); - - // Create basic project structure - await mkdir(projectPath, "src"); - - // Create worker.ts - await fs.writeFile( - join(projectPath, "src", "worker.ts"), - `import type { worker } from "../alchemy.run.ts"; - -export default { - async fetch(request: Request, env: typeof worker.Env, ctx: ExecutionContext): Promise { - return new Response("Hello World from ${projectName}!"); - }, -}; -`, - ); - - // Create tsconfig.json - await writeJsonFile(join(projectPath, "tsconfig.json"), { - compilerOptions: { - target: "ESNext", - module: "ESNext", - moduleResolution: "Bundler", - strict: true, - esModuleInterop: true, - skipLibCheck: true, - allowImportingTsExtensions: true, - rewriteRelativeImportExtensions: true, - types: ["@cloudflare/workers-types", "@types/node"], - }, - include: ["src/**/*", "types/**/*", "alchemy.run.ts"], - }); - - await writeJsonFile(join(projectPath, "package.json"), { - name: projectName, - version: "0.0.0", - description: "Alchemy Typescript Project", - type: "module", - scripts: { - build: "tsc -b", - deploy: "tsx ./alchemy.run.ts", - destroy: "tsx ./alchemy.run.ts --destroy", - }, - devDependencies: { - "@cloudflare/workers-types": "latest", - "@types/node": "^24.0.1", - alchemy: "^0.28.0", - typescript: "^5.8.3", - }, - }); - - // Install dependencies - install({ - devDependencies: [ - alchemyVersion, - "@cloudflare/workers-types", - "@types/node", - "typescript", - ], - }); -} - -async function initViteProject( - projectName: string, - projectPath: string, -): Promise { - npx(`create-vite@6.5.0 ${projectName} --template react-ts`); - const root = projectPath; - await rm(join(root, "tsconfig.app.json")); - await rm(join(root, "tsconfig.node.json")); - - await initWebsiteProject(projectPath, { - entrypoint: "worker/index.ts", - devDependencies: ["@cloudflare/vite-plugin"], - }); - - await fs.writeFile( - join(root, "vite.config.ts"), - `import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' - -import { cloudflare } from "@cloudflare/vite-plugin"; - -// https://vite.dev/config/ -export default defineConfig({ - plugins: [react(), cloudflare()], -}); -`, - ); - await writeJsonFile(join(root, "tsconfig.json"), { - exclude: ["test"], - include: ["types/**/*.ts", "src/**/*.ts", "alchemy.run.ts"], - compilerOptions: { - target: "es2021", - lib: ["es2021"], - jsx: "react-jsx", - module: "es2022", - moduleResolution: "Bundler", - resolveJsonModule: true, - allowJs: true, - checkJs: false, - noEmit: true, - isolatedModules: true, - allowSyntheticDefaultImports: true, - forceConsistentCasingInFileNames: true, - allowImportingTsExtensions: true, - rewriteRelativeImportExtensions: true, - strict: true, - skipLibCheck: true, - types: ["@cloudflare/workers-types", "./types/env.d.ts"], - }, - }); - await mkdir(root, "worker"); - await fs.writeFile( - join(root, "worker", "index.ts"), - `export default { - fetch(request) { - const url = new URL(request.url); - - if (url.pathname.startsWith("/api/")) { - return Response.json({ - name: "Cloudflare", - }); - } - return new Response(null, { status: 404 }); - }, -} satisfies ExportedHandler; -`, - ); -} - -async function initAstroProject( - projectName: string, - projectPath: string, -): Promise { - create( - `astro@latest ${projectName} -- --no-git --no-deploy --install ${options.yes ? "--yes" : ""}`, - ); - - await initWebsiteProject(projectPath, { - scripts: { - dev: "astro dev", - build: "astro check && astro build", - }, - devDependencies: ["@astrojs/cloudflare"], - }); - - // Update astro.config.mjs - await fs.writeFile( - join(projectPath, "astro.config.mjs"), - `import { defineConfig } from 'astro/config'; -import cloudflare from '@astrojs/cloudflare'; - -// https://astro.build/config -export default defineConfig({ - output: 'server', - adapter: cloudflare(), -}); -`, - ); - - // Create API route example - await mkdir(projectPath, "src", "pages", "api"); - await fs.writeFile( - join(projectPath, "src", "pages", "api", "hello.ts"), - `import type { APIRoute } from 'astro'; - -export const GET: APIRoute = async ({ request }) => { - // Access Cloudflare runtime context - const runtime = request.cf; - - return new Response(JSON.stringify({ - message: "Hello from Astro API on Cloudflare!", - timestamp: new Date().toISOString(), - colo: runtime?.colo || "unknown", - country: runtime?.country || "unknown", - city: runtime?.city || "unknown", - }), { - status: 200, - headers: { - 'Content-Type': 'application/json', - }, - }); -}; -`, - ); -} - -async function initReactRouterProject( - projectName: string, - projectPath: string, -): Promise { - create( - `cloudflare@2.49.3 ${projectName} -- --framework=react-router --no-git --no-deploy ${options.yes ? "--yes" : ""}`, - ); - - await initWebsiteProject(projectPath, { - entrypoint: "workers/app.ts", - devDependencies: ["@cloudflare/vite-plugin"], - tsconfig: "tsconfig.node.json", - }); - - await modifyTsConfig(projectPath, { - tsconfig: "tsconfig.node.json", - }); - - await modifyJsoncFile(join(projectPath, "tsconfig.json"), { - "compilerOptions.types": undefined, - "compilerOptions.noEmit": undefined, - }); - - await fs.writeFile( - join(projectPath, "vite.config.ts"), - `import { reactRouter } from "@react-router/dev/vite"; -import { cloudflare } from "@cloudflare/vite-plugin"; -import tailwindcss from "@tailwindcss/vite"; -import { defineConfig } from "vite"; -import tsconfigPaths from "vite-tsconfig-paths"; - -export default defineConfig({ - plugins: [ - cloudflare({ viteEnvironment: { name: "ssr" } }), - tailwindcss(), - reactRouter(), - tsconfigPaths(), - ], -}); -`, - ); -} - -async function initSvelteKitProject( - projectName: string, - projectPath: string, -): Promise { - npx( - `-y sv@latest create --install=${pm} --types=ts ${options.yes ? "--template minimal --no-add-ons" : ""} ${projectName}`, - ); - - await initWebsiteProject(projectPath, { - // entrypoint: "src/routes/index.svelte", - }); - - install({ - devDependencies: [ - "@sveltejs/adapter-cloudflare", - "@sveltejs/vite-plugin-svelte", - ], - }); - - // Update svelte.config.js - await fs.writeFile( - join(projectPath, "svelte.config.js"), - `import adapter from '@sveltejs/adapter-cloudflare'; -import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; - -/** @type {import('@sveltejs/kit').Config} */ -const config = { - preprocess: vitePreprocess(), - kit: { - adapter: adapter() - } -}; - -export default config; -`, - ); - - // Create vite.config.ts - await fs.writeFile( - join(projectPath, "vite.config.ts"), - `import { sveltekit } from '@sveltejs/kit/vite'; -import { defineConfig } from 'vite'; - -export default defineConfig({ -\tplugins: [sveltekit()], -}); -`, - ); -} - -async function initRedwoodProject(): Promise { - npx(`-y create-rwsdk@latest ${projectName}`); - install(); - execCommand(`${getPackageManagerCommands(pm).run} dev:init`, projectPath); - await initWebsiteProject(projectPath, { - scripts: { - deploy: "tsx --env-file .env ./alchemy.run.ts", - destroy: "tsx --env-file .env ./alchemy.run.ts --destroy", - }, - }); - await modifyJsoncFile(join(projectPath, "tsconfig.json"), { - include: undefined, - "compilerOptions.types": ["@cloudflare/workers-types", "types/**/*.ts"], - }); -} - -async function initTanstackStartProject( - projectName: string, - projectPath: string, -): Promise { - npx( - `gitpick TanStack/router/tree/main/examples/react/start-basic ${projectName}`, - ); - - await initWebsiteProject(projectPath); - - await Promise.all([ - rm(join(projectPath, "postcss.config.mjs")), - rm(join(projectPath, "tailwind.config.mjs")), - ]); - - install({ - dependencies: ["tailwind-merge@3"], - devDependencies: ["tailwindcss@4", "@tailwindcss/vite@latest", "postcss"], - }); - - await Promise.all([ - fs.writeFile( - join(projectPath, "vite.config.ts"), - `import tailwindcss from "@tailwindcss/vite"; -import { tanstackStart } from "@tanstack/react-start/plugin/vite"; -import { cloudflareWorkersDevEnvironmentShim } from "alchemy/cloudflare"; -import { defineConfig, PluginOption } from "vite"; -import tsConfigPaths from "vite-tsconfig-paths"; - -export default defineConfig({ - server: { - port: 3000, - }, - build: { - target: "esnext", - rollupOptions: { - external: ["node:async_hooks", "cloudflare:workers"], - }, - }, - plugins: [ - tailwindcss() as PluginOption, - cloudflareWorkersDevEnvironmentShim(), - tsConfigPaths({ - projects: ["./tsconfig.json"], - }), - tanstackStart({ - target: "cloudflare-module", - tsr: { - routeTreeFileHeader: [ - "/** biome-ignore-all lint/suspicious/noExplicitAny: code generated by @tanstack/react-start */", - ], - quoteStyle: "double", - }, - }), - ], -}); -`, - ), - - fs.writeFile( - join(projectPath, "src", "styles", "app.css"), - `@import "tailwindcss"; - -:root { - --border: var(--color-zinc-200); - --popover: var(--color-white); - --popover-foreground: var(--color-zinc-950); -} - -@theme { - --font-sans: var(--font-sans, Inter), ui-sans-serif, system-ui, sans-serif, - "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; -} -`, - ), - ]); -} - -async function initNuxtProject(): Promise { - // create( - // `cloudflare@latest -- ${projectName} --framework=nuxt --no-git --no-deploy`, - // ); - npx( - `nuxi@3.25.1 init nuxt --packageManager npm --no-install --no-gitInit ${isTest ? "--template=ui -M @nuxt/ui" : ""}`, - ); - // npx nuxi@3.25.1 init nuxt --packageManager npm --no-install --no-gitInit - /* --M, --modules Nuxt modules to install (comma separated without spaces) - - ◻ @nuxt/content – The file-based CMS with support for Markdown, YAML, JSON -◻ @nuxt/eslint – Project-aware, easy-to-use, extensible and future-proof ESLint integration -◻ @nuxt/fonts – Add custom web fonts with performance in mind -◻ @nuxt/icon – Icon module for Nuxt with 200,000+ ready to use icons from Iconify -◻ @nuxt/image – Add images with progressive processing, lazy-loading, resizing and providers -support -◻ @nuxt/scripts – Add 3rd-party scripts without sacrificing performance -◻ @nuxt/test-utils – Test utilities for Nuxt -◻ @nuxt/ui – The Intuitive UI Library powered by Reka UI and Tailwind CSS -*/ - - await initWebsiteProject(projectPath, { - scripts: { - build: "nuxt build", - }, - include: ["server/**/*.ts"], - }); - - await fs.writeFile( - join(projectPath, "nuxt.config.ts"), - `// https://nuxt.com/docs/api/configuration/nuxt-config -export default defineNuxtConfig({ - compatibilityDate: '2025-05-15', - devtools: { enabled: true }, - nitro: { - preset: "cloudflare_module", - cloudflare: { - deployConfig: true, - nodeCompat: true - } - }, - modules: ["nitro-cloudflare-dev"], -}); -`, - ); - - install({ - devDependencies: ["nitro-cloudflare-dev"], - }); - - install(); - - await mkdir(projectPath, "server", "api"); - await fs.writeFile( - join(projectPath, "server", "api", "hello.ts"), - `// see: https://nuxt.com/docs/guide/directory-structure/server - -export default defineEventHandler((event) => { - return { - hello: "world", - }; -}); -`, - ); - - await mkdir(projectPath, "server", "middleware"); - await fs.writeFile( - join(projectPath, "server", "middleware", "hello.ts"), - `// see: https://nuxt.com/docs/guide/directory-structure/server#server-middleware - -export default defineEventHandler((event) => { - console.log('New request: ' + getRequestURL(event)) -}) -`, - ); - await fs.writeFile( - join(projectPath, "server", "middleware", "auth.ts"), - `// see: https://nuxt.com/docs/guide/directory-structure/server#server-middleware - -export default defineEventHandler((event) => { - event.context.auth = { user: 123 } -}); -`, - ); -} - -interface WebsiteOptions { - entrypoint?: string; - tsconfig?: string; - scripts?: Record; - include?: string[]; - types?: string[]; - devDependencies?: string[]; - dependencies?: string[]; -} - -/** - * Unified initialization function for website projects that use create-cloudflare - */ -async function initWebsiteProject( - projectPath: string, - options: WebsiteOptions = {}, -): Promise { - await createEnvTs(projectPath); - await cleanupWrangler(projectPath); - await modifyTsConfig(projectPath, options); - await modifyPackageJson(projectPath, options?.scripts); - - // Create alchemy.run.ts - await initWranglerRunTs(projectPath, options); - - await appendGitignore(projectPath); - await appendEnv(projectPath); - - install({ - dependencies: options.dependencies, - devDependencies: [ - "@cloudflare/workers-types", - alchemyVersion, - ...(pm === "bun" ? [] : ["tsx"]), - "typescript", - ...(options.devDependencies ?? []), - ], - }); -} - -async function appendGitignore(projectPath: string): Promise { - try { - await fs.writeFile( - join(projectPath, ".gitignore"), - [ - await fs.readFile(join(projectPath, ".gitignore"), "utf-8"), - ".alchemy/", - ".env", - ].join("\n"), - ); - } catch { - await fs.writeFile(join(projectPath, ".gitignore"), ".alchemy/"); - } -} - -async function initWranglerRunTs( - projectPath: string, - options?: { - entrypoint?: string; - }, -): Promise { - // Create alchemy.run.ts - await fs.writeFile( - join(projectPath, "alchemy.run.ts"), - createAlchemyRunTs(projectName, options), - ); -} - -function createAlchemyRunTs( - projectName: string, - options?: { - entrypoint?: string; - }, -): string { - const adopt = isTest ? "\n adopt: true," : ""; - if (template === "typescript") { - return `/// - -import alchemy from "alchemy"; -import { Worker } from "alchemy/cloudflare"; - -const app = await alchemy("${projectName}"); - -export const worker = await Worker("worker", { - name: "${projectName}",${adopt} - entrypoint: "${options?.entrypoint || "./src/worker.ts"}", -}); - -console.log(worker.url); - -await app.finalize(); -`; - } else if (template === "rwsdk") { - return `/// -import alchemy from "alchemy"; -import { D1Database, DurableObjectNamespace, Redwood } from "alchemy/cloudflare"; - -const app = await alchemy("${projectName}"); - -const database = await D1Database("database", { - name: "${projectName}-db",${adopt} - migrationsDir: "migrations", -}); - -export const worker = await Redwood("website", { - name: "${projectName}-website", - command: "${detectPackageManager()} run build",${adopt} - bindings: { - AUTH_SECRET_KEY: alchemy.secret(process.env.AUTH_SECRET_KEY), - DB: database, - SESSION_DURABLE_OBJECT: new DurableObjectNamespace("session", { - className: "SessionDurableObject", - }), - }, -}); - -console.log({ - url: worker.url, -}); - -await app.finalize(); - `; - } - - // Map template names to their corresponding resource names - const resourceMap: Record = { - vite: "Vite", - astro: "Astro", - "react-router": "ReactRouter", - sveltekit: "SvelteKit", - "tanstack-start": "TanStackStart", - rwsdk: "Redwood", - nuxt: "Nuxt", - }; - - const resourceName = resourceMap[template]; - if (!resourceName) { - throw new Error(`Unknown template: ${template}`); - } - - // Special configuration for Vite template - const config = - options?.entrypoint !== undefined - ? `{ - main: "${options?.entrypoint || "./src/index.ts"}", - command: "${detectPackageManager()} run build",${adopt} -}` - : `{ - command: "${detectPackageManager()} run build",${adopt} -}`; - - return `/// - -import alchemy from "alchemy"; -import { ${resourceName} } from "alchemy/cloudflare"; - -const app = await alchemy("${projectName}"); - -export const worker = await ${resourceName}("website", ${config}); - -console.log({ - url: worker.url, -}); - -await app.finalize(); -`; -} - -async function tryReadFile(path: string): Promise { - try { - return await fs.readFile(path, "utf-8"); - } catch { - return undefined; - } -} - -async function appendFile(path: string, content: string): Promise { - const existingContent = await tryReadFile(path); - await fs.writeFile( - path, - `${existingContent ? `${existingContent}\n` : ""}${content}`, - ); -} - -async function appendEnv(projectPath: string): Promise { - await appendFile(join(projectPath, ".env"), "ALCHEMY_PASSWORD=change-me"); - await appendFile( - join(projectPath, ".env.example"), - "ALCHEMY_PASSWORD=change-me", - ); -} - -async function createEnvTs( - projectPath: string, - identifier = "worker", -): Promise { - // Create env.d.ts for proper typing - await mkdir(projectPath, "types"); - await fs.writeFile( - join(projectPath, "types", "env.d.ts"), - `// This file infers types for the cloudflare:workers environment from your Alchemy Worker. -// @see https://alchemy.run/docs/concepts/bindings.html#type-safe-bindings - -import type { ${identifier} } from "../alchemy.run.ts"; - -export type CloudflareEnv = typeof ${identifier}.Env; - -declare global { - type Env = CloudflareEnv; -} - -declare module "cloudflare:workers" { - namespace Cloudflare { - export interface Env extends CloudflareEnv {} - } -} -`, - ); -} - -async function writeJsonFile(file: string, content: any): Promise { - await fs.writeFile(file, JSON.stringify(content, null, 2)); -} - -async function cleanupWrangler(projectPath: string): Promise { - if (existsSync(join(projectPath, "worker-configuration.d.ts"))) { - await fs.unlink(join(projectPath, "worker-configuration.d.ts")); - } - if (existsSync(join(projectPath, "wrangler.jsonc"))) { - await fs.unlink(join(projectPath, "wrangler.jsonc")); - } -} - -/** - * Modifies a JSON/JSONC file with the given modifications - */ -async function modifyJsoncFile( - file: string, - modifications: Record, -): Promise { - if (!existsSync(file)) { - return; // No file to modify - } - - const content = await fs.readFile(file, "utf-8"); - let modifiedContent = content; - - for (const [path, value] of Object.entries(modifications)) { - const pathArray = path.split("."); - const edits = modify(modifiedContent, pathArray, value, { - formattingOptions: { - tabSize: 2, - insertSpaces: true, - eol: "\n", - }, - }); - modifiedContent = applyEdits(modifiedContent, edits); - } - - await fs.writeFile(file, modifiedContent); -} - -/** - * Modifies tsconfig.json to set proper Cloudflare Workers types and remove worker-configuration.d.ts - */ -async function modifyTsConfig( - projectPath: string, - options: WebsiteOptions = {}, -): Promise { - const tsconfigPath = join(projectPath, options.tsconfig ?? "tsconfig.json"); - - if (!existsSync(tsconfigPath)) { - return; // No tsconfig.json to modify - } - - const tsconfigContent = await fs.readFile(tsconfigPath, "utf-8"); - - // Set compilerOptions.types to ["@cloudflare/workers-types"] - const typesEdit = modify( - tsconfigContent, - ["compilerOptions", "types"], - ["@cloudflare/workers-types", "./types/env.d.ts", ...(options.types ?? [])], - { - formattingOptions: { - tabSize: 2, - insertSpaces: true, - eol: "\n", - }, - }, - ); - - let modifiedContent = applyEdits(tsconfigContent, typesEdit); - - // Parse the JSON to get the current includes array - const { parseTree, getNodeValue, findNodeAtLocation } = await import( - "jsonc-parser" - ); - const tree = parseTree(modifiedContent); - const includeNode = tree ? findNodeAtLocation(tree, ["include"]) : undefined; - const currentIncludes = includeNode ? getNodeValue(includeNode) : []; - - // Filter out worker-configuration.d.ts and ensure required files are included - let newIncludes = Array.isArray(currentIncludes) ? [...currentIncludes] : []; - - // Remove worker-configuration.d.ts if it exists - newIncludes = newIncludes.filter( - (include) => - include !== "worker-configuration.d.ts" && - include !== "./worker-configuration.d.ts", - ); - - await fs.writeFile( - tsconfigPath, - applyEdits( - modifiedContent, - modify( - modifiedContent, - ["include"], - Array.from( - new Set([ - "alchemy.run.ts", - "types/**/*.ts", - ...newIncludes.filter( - (include) => - include !== "worker-configuration.d.ts" && - include !== "./worker-configuration.d.ts", - ), - ...(options.include ?? []), - ]), - ), - { - formattingOptions: { - tabSize: 2, - insertSpaces: true, - eol: "\n", - }, - }, - ), - ), - ); -} - -/** - * Modifies package.json for website projects to add proper scripts and type: "module" - */ -async function modifyPackageJson( - projectPath: string, - scripts?: Record, -): Promise { - const packageJsonPath = join(projectPath, "package.json"); - - if (!existsSync(packageJsonPath)) { - return; // No package.json to modify - } - - const packageJson = { - type: "module", - ...JSON.parse(await fs.readFile(packageJsonPath, "utf-8")), - }; - - // Determine deploy command based on package manager - const deployCommand = - pm === "bun" - ? "bun --env-file=./.env ./alchemy.run.ts" - : "tsx --env-file=./.env ./alchemy.run.ts"; - - // Add/update scripts - if (!packageJson.scripts) { - packageJson.scripts = {}; - } - - packageJson.scripts.build = scripts?.build || "vite build"; - packageJson.scripts.deploy = scripts?.deploy || deployCommand; - packageJson.scripts.destroy = - scripts?.destroy || `${deployCommand} --destroy`; - - packageJson.scripts = { - ...Object.fromEntries( - Object.entries(packageJson.scripts).sort(([a], [b]) => - a.localeCompare(b), - ), - ), - }; - - // Write back to file with proper formatting - await fs.writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2)); -} - -function detectPackageManager(): PackageManager { - // Check npm_execpath for bun - if (process.env.npm_execpath?.includes("bun")) { - return "bun"; - } - - // Check npm_config_user_agent - const userAgent = process.env.npm_config_user_agent; - if (userAgent) { - if (userAgent.startsWith("bun")) return "bun"; - if (userAgent.startsWith("pnpm")) return "pnpm"; - if (userAgent.startsWith("yarn")) return "yarn"; - if (userAgent.startsWith("npm")) return "npm"; - } - - // Default fallback - return "npm"; -} - -function getPackageManagerCommands(pm: PackageManager) { - const commands = { - bun: { - init: "bun init -y", - install: "bun install", - add: "bun add", - addDev: "bun add -D", - run: "bun run", - create: "bun create", - x: "bunx", - }, - npm: { - init: "npm init -y", - install: "npm install", - add: "npm install", - addDev: "npm install --save-dev", - run: "npm run", - create: "npm create", - x: "npx", - }, - pnpm: { - init: "pnpm init", - install: "pnpm install", - add: "pnpm add", - addDev: "pnpm add -D", - run: "pnpm run", - create: "pnpm create", - x: "pnpm dlx", - }, - yarn: { - init: "yarn init -y", - install: "yarn install", - add: "yarn add", - addDev: "yarn add -D", - run: "yarn", - create: "yarn create", - x: "yarn dlx", - }, - }; - - return commands[pm]; -} - -async function rm(path: string): Promise { - if (existsSync(path)) { - await fs.rm(path, { recursive: true }); - } -} - -async function mkdir(...path: string[]): Promise { - await fs.mkdir(join(...path), { - recursive: true, - }); -} - -function execCommand(command: string, cwd: string = process.cwd()): void { - console.log(command); - try { - execSync(command, { stdio: "inherit", cwd }); - } catch { - console.error(`Failed to execute: ${command}`); - process.exit(1); - } -} - -function install({ - dependencies, - devDependencies, - cwd = projectPath, -}: { - dependencies?: string[]; - devDependencies?: string[]; - cwd?: string; -} = {}) { - if (!dependencies && !devDependencies) { - execCommand(getPackageManagerCommands(pm).install, cwd); - } - if (dependencies) { - execCommand( - `${getPackageManagerCommands(pm).add} ${dependencies.join(" ")}`, - cwd, - ); - } - if (devDependencies) { - execCommand( - `${getPackageManagerCommands(pm).addDev} ${devDependencies.join(" ")}`, - cwd, - ); - } -} - -function npx(command: string, cwd: string = process.cwd()): void { - execCommand( - `${getPackageManagerCommands(pm).x} ${options.yes ? "--yes" : ""} ${command}`, - cwd, - ); -} - -function create(command: string, cwd: string = process.cwd()): void { - execCommand( - `${getPackageManagerCommands(pm).create} ${options.yes ? "-y" : ""} ${command}`, - cwd, - ); -} diff --git a/alchemy/bin/errors.ts b/alchemy/bin/errors.ts new file mode 100644 index 000000000..e38b44f17 --- /dev/null +++ b/alchemy/bin/errors.ts @@ -0,0 +1,11 @@ +import { log } from "@clack/prompts"; +import pc from "picocolors"; + +export function throwWithContext(error: unknown, context: string): never { + const errorMsg = error instanceof Error ? error.message : String(error); + log.error(pc.red(`❌ ${context}`)); + log.error(pc.gray(` ${errorMsg}`)); + throw new Error(`${context}: ${errorMsg}`, { + cause: error instanceof Error ? error : new Error(String(error)), + }); +} diff --git a/alchemy/bin/services/dependencies.ts b/alchemy/bin/services/dependencies.ts new file mode 100644 index 000000000..fdaff1629 --- /dev/null +++ b/alchemy/bin/services/dependencies.ts @@ -0,0 +1,47 @@ +import { log } from "@clack/prompts"; +import fs from "fs-extra"; +import path from "node:path"; + +import { + type DependencyVersionMap, + dependencyVersionMap, +} from "../constants.ts"; + +export const addPackageDependencies = async (opts: { + dependencies?: DependencyVersionMap[]; + devDependencies?: DependencyVersionMap[]; + projectDir: string; +}): Promise => { + const { dependencies = [], devDependencies = [], projectDir } = opts; + + const pkgJsonPath = path.join(projectDir, "package.json"); + + const pkgJson = await fs.readJson(pkgJsonPath); + + if (!pkgJson.dependencies) pkgJson.dependencies = {}; + if (!pkgJson.devDependencies) pkgJson.devDependencies = {}; + + for (const pkgName of dependencies) { + const version = dependencyVersionMap[pkgName]; + if (version) { + pkgJson.dependencies[pkgName] = version; + } else { + log.warn(`Warning: Dependency ${pkgName} not found in version map.`); + } + } + + for (const pkgName of devDependencies) { + const version = dependencyVersionMap[pkgName]; + if (version) { + pkgJson.devDependencies[pkgName] = version; + } else { + log.warn(`Warning: Dev dependency ${pkgName} not found in version map.`); + } + } + + await fs.writeJson(pkgJsonPath, pkgJson, { + spaces: 2, + }); +}; + +export const addPackageDependency = addPackageDependencies; diff --git a/alchemy/bin/services/get-package-version.ts b/alchemy/bin/services/get-package-version.ts new file mode 100644 index 000000000..89ce896bf --- /dev/null +++ b/alchemy/bin/services/get-package-version.ts @@ -0,0 +1,11 @@ +import fs from "fs-extra"; +import path from "node:path"; +import { PKG_ROOT } from "../constants.ts"; + +export const getPackageVersion = () => { + const packageJsonPath = path.join(PKG_ROOT, "package.json"); + + const packageJsonContent = fs.readJSONSync(packageJsonPath); + + return packageJsonContent.version ?? "1.0.0"; +}; diff --git a/alchemy/bin/services/package-manager.ts b/alchemy/bin/services/package-manager.ts new file mode 100644 index 000000000..1bbd41fa3 --- /dev/null +++ b/alchemy/bin/services/package-manager.ts @@ -0,0 +1,113 @@ +import { log } from "@clack/prompts"; +import { execa } from "execa"; +import * as fs from "fs-extra"; +import type { PackageManager, ProjectContext } from "../types.ts"; + +export function detectPackageManager(): PackageManager { + if (fs.pathExistsSync("bun.lockb")) return "bun"; + if (fs.pathExistsSync("pnpm-lock.yaml")) return "pnpm"; + if (fs.pathExistsSync("yarn.lock")) return "yarn"; + + if (process.env.npm_execpath?.includes("bun")) { + return "bun"; + } + + const userAgent = process.env.npm_config_user_agent; + if (userAgent) { + if (userAgent.startsWith("bun")) return "bun"; + if (userAgent.startsWith("pnpm")) return "pnpm"; + if (userAgent.startsWith("yarn")) return "yarn"; + if (userAgent.startsWith("npm")) return "npm"; + } + + return "npm"; +} + +export function getPackageManagerCommands(pm: PackageManager) { + const commands = { + bun: { + init: "bun init -y", + install: "bun install", + add: "bun add", + addDev: "bun add -D", + run: "bun run", + create: "bun create", + x: "bunx", + }, + npm: { + init: "npm init -y", + install: "npm install", + add: "npm install", + addDev: "npm install --save-dev", + run: "npm run", + create: "npm create", + x: "npx", + }, + pnpm: { + init: "pnpm init", + install: "pnpm install", + add: "pnpm add", + addDev: "pnpm add -D", + run: "pnpm run", + create: "pnpm create", + x: "pnpm dlx", + }, + yarn: { + init: "yarn init -y", + install: "yarn install", + add: "yarn add", + addDev: "yarn add -D", + run: "yarn", + create: "yarn create", + x: "yarn dlx", + }, + }; + + return commands[pm]; +} + +export async function installDependencies( + context: ProjectContext, + { + dependencies, + devDependencies, + cwd, + }: { + dependencies?: string[]; + devDependencies?: string[]; + cwd?: string; + } = {}, +): Promise { + const targetCwd = cwd || context.path; + const pm = context.packageManager; + const commands = getPackageManagerCommands(pm); + + try { + if (!dependencies && !devDependencies) { + await execa(commands.install, { + cwd: targetCwd, + shell: true, + stdio: "pipe", + }); + } + + if (dependencies) { + await execa(`${commands.add} ${dependencies.join(" ")}`, { + cwd: targetCwd, + shell: true, + stdio: "pipe", + }); + } + + if (devDependencies) { + await execa(`${commands.addDev} ${devDependencies.join(" ")}`, { + cwd: targetCwd, + shell: true, + stdio: "pipe", + }); + } + } catch (error) { + log.error("Failed to install dependencies"); + throw error; + } +} diff --git a/alchemy/bin/services/template-manager.ts b/alchemy/bin/services/template-manager.ts new file mode 100644 index 000000000..524ac96b8 --- /dev/null +++ b/alchemy/bin/services/template-manager.ts @@ -0,0 +1,137 @@ +import { log, spinner } from "@clack/prompts"; +import { execa } from "execa"; +import * as fs from "fs-extra"; +import { globby } from "globby"; +import { existsSync } from "node:fs"; +import * as path from "node:path"; +import { join } from "node:path"; + +import { PKG_ROOT } from "../constants.ts"; +import { throwWithContext } from "../errors.ts"; +import type { ProjectContext } from "../types.ts"; +import { addPackageDependencies } from "./dependencies.ts"; +import { + getPackageManagerCommands, + installDependencies, +} from "./package-manager.ts"; + +export async function copyTemplate( + templateName: string, + context: ProjectContext, +): Promise { + const templatePath = path.join(PKG_ROOT, "templates", templateName); + + if (!existsSync(templatePath)) { + throw new Error(`Template '${templateName}' not found at ${templatePath}`); + } + + const filesToRename = ["_gitignore", "_npmrc", "_env", "_env.example"]; + + try { + const copySpinner = spinner(); + copySpinner.start("Copying template files..."); + + const files = await globby("**/*", { + cwd: templatePath, + dot: true, + }); + + for (const file of files) { + const srcPath = join(templatePath, file); + let destFile = file; + + const basename = path.basename(file); + if (filesToRename.includes(basename)) { + const newBasename = `.${basename.slice(1)}`; + destFile = path.join(path.dirname(file), newBasename); + } + + const destPath = join(context.path, destFile); + + await fs.ensureDir(path.dirname(destPath)); + await fs.copy(srcPath, destPath); + } + + copySpinner.stop("Template files copied successfully"); + + await updateTemplatePackageJson(context); + + await addPackageDependencies({ + devDependencies: ["alchemy"], + projectDir: context.path, + }); + + if (context.options.install !== false) { + const installSpinner = spinner(); + installSpinner.start("Installing dependencies..."); + try { + await installDependencies(context); + installSpinner.stop("Dependencies installed successfully"); + } catch (error) { + installSpinner.stop("Failed to install dependencies"); + throw error; + } + } else { + log.info("Skipping dependency installation"); + } + + if (templateName === "rwsdk") { + await handleRwsdkPostInstall(context); + } + + log.success("Project setup complete!"); + } catch (error) { + throwWithContext(error, `Failed to copy template '${templateName}'`); + } +} + +async function updateTemplatePackageJson( + context: ProjectContext, +): Promise { + const packageJsonPath = join(context.path, "package.json"); + + if (!existsSync(packageJsonPath)) { + return; + } + + const packageJson = await fs.readJson(packageJsonPath); + + packageJson.name = context.name; + + const deployCommand = + context.packageManager === "bun" + ? "bun --env-file=./.env ./alchemy.run.ts" + : "tsx --env-file=./.env ./alchemy.run.ts"; + + if (packageJson.scripts) { + packageJson.scripts.deploy = deployCommand; + packageJson.scripts.destroy = `${deployCommand} --destroy`; + } + + await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 }); +} + +async function handleRwsdkPostInstall(context: ProjectContext): Promise { + try { + const migrationsDir = join(context.path, "migrations"); + await fs.ensureDir(migrationsDir); + + const commands = getPackageManagerCommands(context.packageManager); + const devInitCommand = `${commands.run} dev:init`; + + if (context.options.install !== false) { + await execa(devInitCommand, { + cwd: context.path, + shell: true, + }); + } else { + log.info( + `To complete rwsdk setup, run: cd ${context.name} && ${devInitCommand}`, + ); + } + } catch (_error) { + log.warn( + "Failed to complete rwsdk setup. You may need to run 'dev:init' manually.", + ); + } +} diff --git a/alchemy/bin/types.ts b/alchemy/bin/types.ts new file mode 100644 index 000000000..200f8fc62 --- /dev/null +++ b/alchemy/bin/types.ts @@ -0,0 +1,83 @@ +import { z } from "zod"; + +export const TEMPLATE_DEFINITIONS = [ + { name: "typescript", description: "TypeScript Worker" }, + { name: "vite", description: "React Vite" }, + { name: "astro", description: "Astro SSR" }, + { name: "react-router", description: "React Router" }, + { name: "sveltekit", description: "SvelteKit" }, + { name: "tanstack-start", description: "TanStack Start" }, + { name: "rwsdk", description: "Redwood SDK" }, + { name: "nuxt", description: "Nuxt.js" }, +] as const; + +const templateNames = TEMPLATE_DEFINITIONS.map((t) => t.name); + +export const TemplateSchema = z + .enum(templateNames as [string, ...string[]]) + .describe("Project template type"); +export type TemplateType = z.infer; + +export const PackageManagerSchema = z + .enum(["bun", "npm", "pnpm", "yarn"]) + .describe("Package manager"); +export type PackageManager = z.infer; + +export const ProjectNameSchema = z + .string() + .min(1, "Project name cannot be empty") + .max(255, "Project name must be less than 255 characters") + .refine( + (name) => name === "." || !name.startsWith("."), + "Project name cannot start with a dot (except for '.')", + ) + .refine( + (name) => name === "." || !name.startsWith("-"), + "Project name cannot start with a dash", + ) + .refine((name) => { + const invalidChars = ["<", ">", ":", '"', "|", "?", "*"]; + return !invalidChars.some((char) => name.includes(char)); + }, "Project name contains invalid characters") + .refine( + (name) => name.toLowerCase() !== "node_modules", + "Project name is reserved", + ) + .describe("Project name or path"); +export type ProjectName = z.infer; + +export interface ProjectContext { + name: string; + path: string; + template: TemplateType; + packageManager: PackageManager; + isTest: boolean; + options: CreateInput; +} + +export interface WebsiteOptions { + entrypoint?: string; + tsconfig?: string; + scripts?: Record; + include?: string[]; + types?: string[]; + devDependencies?: string[]; + dependencies?: string[]; +} + +export type CreateInput = { + name?: string; + template?: TemplateType; + packageManager?: PackageManager; + bun?: boolean; + npm?: boolean; + pnpm?: boolean; + yarn?: boolean; + yes?: boolean; + overwrite?: boolean; + install?: boolean; +}; + +export type CLIInput = CreateInput & { + projectDirectory?: string; +}; diff --git a/alchemy/package.json b/alchemy/package.json index ef6ed553b..db76893f3 100644 --- a/alchemy/package.json +++ b/alchemy/package.json @@ -1,6 +1,6 @@ { "name": "alchemy", - "version": "0.34.3", + "version": "0.41.2", "type": "module", "module": "./lib/index.js", "license": "Apache-2.0", @@ -11,7 +11,9 @@ }, "scripts": { "build:cli": "./scripts/bundle-cli.sh", - "build": "rm -rf ./*.tsbuildinfo && rm -rf ./lib && tsc -b && bun run build:cli", + "build:workers": "bun ./scripts/build-workers.ts", + "build": "bun ./scripts/generate-build-date.ts && rm -rf ./*.tsbuildinfo && rm -rf ./lib && tsc -b && bun run build:cli && bun run build:workers", + "dev:cli": "./scripts/bundle-cli.sh --watch", "docs:gen": "rm -rf ./docs && typedoc", "postbuild": "cpx 'src/**/*/types.d.ts' lib", "publish:npm": "cp ../README.md . && bun run build && bun run build:cli && npm publish && rm README.md" @@ -24,7 +26,9 @@ "files": [ "bin", "lib", - "src" + "src", + "templates", + "workers" ], "exports": { ".": { @@ -55,6 +59,10 @@ "bun": "./src/dns/index.ts", "import": "./lib/dns/index.js" }, + "./docker": { + "bun": "./src/docker/index.ts", + "import": "./lib/docker/index.js" + }, "./esbuild": { "bun": "./src/esbuild/index.ts", "import": "./lib/esbuild/index.js" @@ -148,7 +156,6 @@ "ai": "^4.0.0", "arktype": "^2.0.0", "cloudflare": "^4.0.0", - "dofs": "^0.0.1", "hono": "^4.0.0", "prettier": "^3.0.0", "stripe": "^17.0.0", @@ -161,30 +168,40 @@ "@aws-sdk/client-resource-groups-tagging-api": "^3.830.0", "@aws-sdk/client-s3": "3.726.1", "@biomejs/biome": "^1.9.4", + "@clack/prompts": "^0.11.0", + "@cloudflare/containers": "^0.0.13", "@cloudflare/puppeteer": "^1.0.2", - "@cloudflare/workers-types": "^4.20250303.0", + "@cloudflare/workers-types": "^4.20250620.0", "@iarna/toml": "^2.2.5", - "@inquirer/prompts": "^7.0.0", "@octokit/rest": "^21.1.1", "@types/bun": "latest", "@types/diff": "^5.0.0", + "@types/fs-extra": "^11.0.4", "@types/libsodium-wrappers": "^0.7.14", "@types/node": "latest", "@types/turndown": "^5.0.5", "ai": "^4.1.16", + "alchemy": "^0.37.1", "arktype": "^2.1.16", "braintrust": "^0.0.201", "change-case": "^5.4.4", "cloudflare": "^4.2.0", "cpx": "^1.5.0", + "dofs": "^0.1.0", + "execa": "^9.6.0", + "fs-extra": "^11.3.0", + "globby": "^14.1.0", "libsodium-wrappers": "^0.7.15", - "miniflare": "^4.0.0", + "miniflare": "^4.20250617.4", + "nitro-cloudflare-dev": "^0.2.2", "openpgp": "^6.1.0", + "picocolors": "^1.1.1", "prettier": "^3.5.3", + "trpc-cli": "^0.9.2", "turndown": "^7.2.0", "typedoc": "^0.28.1", "typedoc-plugin-markdown": "^4.6.0", - "typescript": "latest", + "typescript": "^5.8.3", "vite": "^6.0.7", "vitepress": "^1.6.3", "wrangler": "^3.114.0", diff --git a/alchemy/scripts/build-workers.ts b/alchemy/scripts/build-workers.ts new file mode 100644 index 000000000..0a960b774 --- /dev/null +++ b/alchemy/scripts/build-workers.ts @@ -0,0 +1,21 @@ +import { readdir } from "node:fs/promises"; +import path from "node:path"; +import { bundle } from "../src/esbuild/bundle"; + +const WORKERS_DIR = path.join(__dirname, "..", "workers"); + +const workers = await readdir(WORKERS_DIR); + +for (const worker of workers) { + if (!worker.endsWith(".ts")) { + continue; + } + await bundle({ + entryPoint: path.join(WORKERS_DIR, worker), + outdir: WORKERS_DIR, + bundle: true, + format: "esm", + target: "es2022", + external: ["cloudflare:*", "node:crypto"], + }); +} \ No newline at end of file diff --git a/alchemy/scripts/bundle-cli.sh b/alchemy/scripts/bundle-cli.sh index d87340589..054851c67 100755 --- a/alchemy/scripts/bundle-cli.sh +++ b/alchemy/scripts/bundle-cli.sh @@ -1,5 +1,13 @@ #!/usr/bin/env sh +WATCH_FLAG="" +for arg in "$@"; do + if [ "$arg" = "--watch" ]; then + WATCH_FLAG="--watch" + break + fi +done + esbuild bin/alchemy.ts \ --bundle \ --platform=node \ @@ -8,4 +16,5 @@ esbuild bin/alchemy.ts \ --format=esm \ --external:node:* \ --main-fields=module,main \ - --banner:js="import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);" \ No newline at end of file + --banner:js="import { createRequire as __createRequire } from 'node:module'; const require = __createRequire(import.meta.url);" \ + $WATCH_FLAG diff --git a/alchemy/scripts/generate-build-date.ts b/alchemy/scripts/generate-build-date.ts new file mode 100644 index 000000000..1f9e277e7 --- /dev/null +++ b/alchemy/scripts/generate-build-date.ts @@ -0,0 +1,31 @@ +#!/usr/bin/env bun + +/** + * This script generates a TypeScript file containing the build date + * for use as the default worker compatibility date. + */ + +import { writeFileSync } from 'node:fs'; +import { resolve } from 'node:path'; + +// Generate today's date in YYYY-MM-DD format +const buildDate = new Date().toISOString().split('T')[0]; + +// TypeScript content to generate +const content = `// This file is auto-generated during build +// Do not edit manually + +/** + * The build date used as the default worker compatibility date. + * This is set to the date when the package was built. + */ +export const BUILD_DATE = "${buildDate}"; +`; + +// Write to src directory so it gets included in the package +const outputPath = resolve(import.meta.dirname, '..', 'src', 'build-date.ts'); + +writeFileSync(outputPath, content, 'utf8'); + +console.log(`Generated build date: ${buildDate}`); +console.log(`Written to: ${outputPath}`); \ No newline at end of file diff --git a/alchemy/src/alchemy.ts b/alchemy/src/alchemy.ts index 244f8b38a..4ad3bc10f 100644 --- a/alchemy/src/alchemy.ts +++ b/alchemy/src/alchemy.ts @@ -1,6 +1,7 @@ import fs from "node:fs/promises"; import path from "node:path"; +import { ReplacedSignal } from "./apply.ts"; import { DestroyedSignal, destroy } from "./destroy.ts"; import { env } from "./env.ts"; import { @@ -15,9 +16,9 @@ import { isRuntime } from "./runtime/global.ts"; import { Scope } from "./scope.ts"; import { secret } from "./secret.ts"; import type { StateStoreType } from "./state.ts"; +import type { LoggerApi } from "./util/cli.ts"; import { logger } from "./util/logger.ts"; import { TelemetryClient } from "./util/telemetry/client.ts"; -import type { LoggerApi } from "./util/cli.ts"; /** * Parses CLI arguments to extract alchemy options @@ -33,6 +34,14 @@ function parseCliArgs(): Partial { options.phase = "read"; } + if ( + args.includes("--dev") || + args.includes("--watch") || + process.execArgv.includes("--watch") + ) { + options.dev = true; + } + // Parse quiet flag if (args.includes("--quiet")) { options.quiet = true; @@ -181,21 +190,30 @@ async function _alchemy( const root = new Scope({ ...mergedOptions, appName, - stage: - mergedOptions?.stage ?? process.env.ALCHEMY_STAGE ?? process.env.USER, phase, password: mergedOptions?.password ?? process.env.ALCHEMY_PASSWORD, telemetryClient, }); + const stage = new Scope({ + ...mergedOptions, + scopeName: + mergedOptions?.stage ?? process.env.ALCHEMY_STAGE ?? process.env.USER, + parent: root, + appName, + stage: + mergedOptions?.stage ?? process.env.ALCHEMY_STAGE ?? process.env.USER, + }); try { Scope.storage.enterWith(root); + Scope.storage.enterWith(stage); } catch { // we are in Cloudflare Workers, we will emulate the enterWith behavior // see Scope.finalize for where we pop the global scope Scope.globals.push(root); + Scope.globals.push(stage); } if (mergedOptions?.phase === "destroy") { - await destroy(root); + await destroy(stage); return process.exit(0); } return root; @@ -327,6 +345,12 @@ export interface AlchemyOptions { * @default "up" */ phase?: Phase; + /** + * Determines whether Alchemy will run in dev mode. + * + * @default - `true` if `--dev` or `--watch` is passed as a CLI argument, `false` otherwise + */ + dev?: boolean; /** * Name to scope the resource state under (e.g. `.alchemy/{stage}/..`). * @@ -464,7 +488,9 @@ async function run( } return await _scope.run(async () => fn.bind(_scope)(_scope)); } catch (error) { - if (!(error instanceof DestroyedSignal)) { + if ( + !(error instanceof DestroyedSignal || error instanceof ReplacedSignal) + ) { _scope.fail(); } throw error; diff --git a/alchemy/src/apply.ts b/alchemy/src/apply.ts index 0625b9938..869627942 100644 --- a/alchemy/src/apply.ts +++ b/alchemy/src/apply.ts @@ -12,7 +12,7 @@ import { type Resource, type ResourceProps, } from "./resource.ts"; -import { Scope } from "./scope.ts"; +import { Scope, type PendingDeletions } from "./scope.ts"; import { serialize } from "./serde.ts"; import type { State } from "./state.ts"; import { formatFQN } from "./util/cli.ts"; @@ -33,6 +33,8 @@ export function apply( return _apply(resource, props, options); } +export class ReplacedSignal extends Error {} + async function _apply( resource: PendingResource, props: ResourceProps | undefined, @@ -88,6 +90,7 @@ async function _apply( }; await scope.state.set(resource[ResourceID], state); } + const oldOutput = state.output; const alwaysUpdate = options?.alwaysUpdate ?? provider.options?.alwaysUpdate ?? false; @@ -162,33 +165,84 @@ async function _apply( props: state.oldProps, state, replace: () => { + if (phase === "create") { + throw new Error( + `Resource ${resource[ResourceKind]} ${resource[ResourceFQN]} cannot be replaced in create phase.`, + ); + } if (isReplaced) { logger.warn( `Resource ${resource[ResourceKind]} ${resource[ResourceFQN]} is already marked as REPLACE`, ); - return; } + isReplaced = true; + throw new ReplacedSignal(); }, }); - const output = await alchemy.run( - resource[ResourceID], - { - isResource: true, - parent: scope, - }, - async (scope) => { - options?.resolveInnerScope?.(scope); - return provider.handler.bind(ctx)(resource[ResourceID], props); - }, - ); + let output: Resource; + try { + output = await alchemy.run( + resource[ResourceID], + { + isResource: true, + parent: scope, + }, + async (scope) => { + options?.resolveInnerScope?.(scope); + return provider.handler.bind(ctx)(resource[ResourceID], props); + }, + ); + } catch (error) { + if (error instanceof ReplacedSignal) { + if (scope.children.get(resource[ResourceID])?.children.size! > 0) { + throw new Error( + `Resource ${resource[ResourceFQN]} has children and cannot be replaced.`, + ); + } + + output = await alchemy.run( + resource[ResourceID], + { isResource: true, parent: scope }, + async () => + provider.handler.bind( + context({ + scope, + phase: "create", + kind: resource[ResourceKind], + id: resource[ResourceID], + fqn: resource[ResourceFQN], + seq: resource[ResourceSeq], + props: state.props, + state, + replace: () => { + throw new Error( + `Resource ${resource[ResourceKind]} ${resource[ResourceFQN]} cannot be replaced in create phase.`, + ); + }, + }), + )(resource[ResourceID], props), + ); + + const pendingDeletions = + (await scope.get("pendingDeletions")) ?? []; + pendingDeletions.push({ + resource: oldOutput, + oldProps: state.oldProps, + }); + await scope.set("pendingDeletions", pendingDeletions); + } else { + throw error; + } + } if (!quiet) { logger.task(resource[ResourceFQN], { - prefix: phase === "create" ? "created" : "updated", + prefix: + phase === "create" ? "created" : isReplaced ? "replaced" : "updated", prefixColor: "greenBright", resource: formatFQN(resource[ResourceFQN]), - message: `${phase === "create" ? "Created" : "Updated"} Resource`, + message: `${phase === "create" ? "Created" : isReplaced ? "Replaced" : "Updated"} Resource`, status: "success", }); } @@ -213,9 +267,6 @@ async function _apply( props, // deps: [...deps], }); - // if (output !== undefined) { - // resource[Provide](output as Out); - // } return output as any; } catch (error) { scope.telemetryClient.record({ diff --git a/alchemy/src/build-date.ts b/alchemy/src/build-date.ts new file mode 100644 index 000000000..cb59e7bdb --- /dev/null +++ b/alchemy/src/build-date.ts @@ -0,0 +1,8 @@ +// This file is auto-generated during build +// Do not edit manually + +/** + * The build date used as the default worker compatibility date. + * This is set to the date when the package was built. + */ +export const BUILD_DATE = "2025-06-26"; diff --git a/alchemy/src/cloudflare/api.ts b/alchemy/src/cloudflare/api.ts index cfddb1207..810cf7e69 100644 --- a/alchemy/src/cloudflare/api.ts +++ b/alchemy/src/cloudflare/api.ts @@ -196,7 +196,8 @@ export class CloudflareApi { (error) => error instanceof InternalError || error instanceof TooManyRequestsError || - error instanceof ForbiddenError, + error instanceof ForbiddenError || + error.code === "ECONNRESET", 10, // Maximum 10 attempts (1 initial + 9 retries) 1000, // Start with 1s delay, will exponentially increase ); diff --git a/alchemy/src/cloudflare/bindings.ts b/alchemy/src/cloudflare/bindings.ts index f1b7bb43e..3cf6d6cdd 100644 --- a/alchemy/src/cloudflare/bindings.ts +++ b/alchemy/src/cloudflare/bindings.ts @@ -11,6 +11,7 @@ import type { Assets } from "./assets.ts"; import type { Bound } from "./bound.ts"; import type { BrowserRendering } from "./browser-rendering.ts"; import type { R2BucketResource } from "./bucket.ts"; +import type { Container } from "./container.ts"; import type { D1DatabaseResource } from "./d1-database.ts"; import type { DispatchNamespaceResource } from "./dispatch-namespace.ts"; import type { DurableObjectNamespace } from "./durable-object-namespace.ts"; @@ -44,11 +45,12 @@ export type Binding = | Ai | AiGatewayResource | Assets + | Container | CloudflareSecret | D1DatabaseResource | DispatchNamespaceResource | AnalyticsEngineDataset - | DurableObjectNamespace + | DurableObjectNamespace | HyperdriveResource | Images | KVNamespaceResource diff --git a/alchemy/src/cloudflare/bound.ts b/alchemy/src/cloudflare/bound.ts index 1bf5a3cf1..5c5b86e76 100644 --- a/alchemy/src/cloudflare/bound.ts +++ b/alchemy/src/cloudflare/bound.ts @@ -7,6 +7,7 @@ import type { Assets } from "./assets.ts"; import type { Binding, Json, Self } from "./bindings.ts"; import type { BrowserRendering } from "./browser-rendering.ts"; import type { R2BucketResource as _R2Bucket } from "./bucket.ts"; +import type { Container as _Container } from "./container.ts"; import type { D1DatabaseResource } from "./d1-database.ts"; import type { DispatchNamespaceResource } from "./dispatch-namespace.ts"; import type { DurableObjectNamespace as _DurableObjectNamespace } from "./durable-object-namespace.ts"; @@ -36,7 +37,7 @@ type BoundWorker< export type Bound = T extends _DurableObjectNamespace< infer O > - ? DurableObjectNamespace + ? DurableObjectNamespace : T extends { type: "kv_namespace" } ? KVNamespace : T extends WorkerStub @@ -87,4 +88,11 @@ export type Bound = T extends _DurableObjectNamespace< ? Service : T extends Json ? T - : Service; + : T extends _Container< + infer Obj + > + ? DurableObjectNamespace< + Obj & + Rpc.DurableObjectBranded + > + : Service; diff --git a/alchemy/src/cloudflare/bucket.ts b/alchemy/src/cloudflare/bucket.ts index 98b8347b5..95749f375 100644 --- a/alchemy/src/cloudflare/bucket.ts +++ b/alchemy/src/cloudflare/bucket.ts @@ -87,6 +87,17 @@ export interface BucketProps { * Whether to adopt an existing bucket */ adopt?: boolean; + + /** + * Whether to emulate the bucket locally when Alchemy is running in watch mode. + */ + dev?: { + /** + * Whether to run the bucket remotely instead of locally + * @default false + */ + remote?: boolean; + }; } /** @@ -248,6 +259,7 @@ const R2BucketResource = Resource( jurisdiction: props.jurisdiction || "default", type: "r2_bucket", accountId: api.accountId, + dev: props.dev, }); }, ); diff --git a/alchemy/src/cloudflare/bundle/bundle-worker-dev.ts b/alchemy/src/cloudflare/bundle/bundle-worker-dev.ts new file mode 100644 index 000000000..4c36d38bc --- /dev/null +++ b/alchemy/src/cloudflare/bundle/bundle-worker-dev.ts @@ -0,0 +1,127 @@ +import type esbuild from "esbuild"; +import type { Bindings } from "../bindings.ts"; +import type { WorkerProps } from "../worker.ts"; +import { createAliasPlugin } from "./alias-plugin.ts"; +import { external, external_als } from "./external.ts"; +import { getNodeJSCompatMode } from "./nodejs-compat-mode.ts"; +import { nodeJsCompatPlugin } from "./nodejs-compat.ts"; +import { wasmPlugin } from "./wasm-plugin.ts"; + +interface DevWorkerContext { + context: esbuild.BuildContext; + dispose: () => Promise; +} + +declare global { + var _ALCHEMY_DEV_WORKER_CONTEXTS: Map | undefined; +} + +const activeContexts = () => + (globalThis._ALCHEMY_DEV_WORKER_CONTEXTS ??= new Map()); + +/** + * Creates an esbuild context for watching and hot-reloading a worker + */ +export async function createWorkerDevContext( + workerName: string, + props: WorkerProps & { + entrypoint: string; + compatibilityDate: string; + compatibilityFlags: string[]; + }, + hooks: HotReloadHooks, +) { + // Clean up any existing context for this worker + const existing = activeContexts().get(workerName); + if (existing) { + await existing.dispose(); + } + + if (!props.entrypoint) { + throw new Error( + "A worker dev context was created, but no entry point was provided.", + ); + } + + const esbuild = await import("esbuild"); + const nodeJsCompatMode = await getNodeJSCompatMode( + props.compatibilityDate, + props.compatibilityFlags, + ); + + const projectRoot = props.projectRoot ?? process.cwd(); + + const context = await esbuild.context({ + entryPoints: [props.entrypoint], + format: props.format === "cjs" ? "cjs" : "esm", + target: "esnext", + platform: "node", + minify: false, + bundle: true, + ...props.bundle, + write: false, // We want the result in memory for hot reloading + conditions: ["workerd", "worker", "import", "module", "browser"], + mainFields: ["module", "main"], + absWorkingDir: projectRoot, + keepNames: true, + loader: { + ".sql": "text", + ".json": "json", + ...props.bundle?.loader, + }, + plugins: [ + wasmPlugin, + ...(props.bundle?.plugins ?? []), + ...(nodeJsCompatMode === "v2" ? [await nodeJsCompatPlugin()] : []), + ...(props.bundle?.alias + ? [ + createAliasPlugin({ + alias: props.bundle?.alias, + projectRoot, + }), + ] + : []), + hotReloadPlugin(hooks), + ], + external: [ + ...(nodeJsCompatMode === "als" ? external_als : external), + ...(props.bundle?.external ?? []), + ], + }); + + await context.watch(); + + activeContexts().set(workerName, { + context, + dispose: async () => { + await context.dispose(); + activeContexts().delete(workerName); + }, + }); +} + +interface HotReloadHooks { + onBuildStart: () => void | Promise; + onBuildEnd: (script: string) => void | Promise; + onBuildError: (errors: esbuild.Message[]) => void | Promise; +} + +function hotReloadPlugin(hooks: HotReloadHooks): esbuild.Plugin { + return { + name: "alchemy-hot-reload", + setup(build) { + build.onStart(hooks.onBuildStart); + build.onEnd(async (result) => { + if (result.errors.length > 0) { + await hooks.onBuildError(result.errors); + return; + } + + if (result.outputFiles && result.outputFiles.length > 0) { + const newScript = result.outputFiles[0].text; + await hooks.onBuildEnd(newScript); + } + }); + }, + }; +} diff --git a/alchemy/src/cloudflare/bundle/bundle-worker.ts b/alchemy/src/cloudflare/bundle/bundle-worker.ts index 5ced5053c..20a9e41db 100644 --- a/alchemy/src/cloudflare/bundle/bundle-worker.ts +++ b/alchemy/src/cloudflare/bundle/bundle-worker.ts @@ -34,11 +34,6 @@ export async function bundleWorkerScript( props.compatibilityFlags, ); - if (nodeJsCompatMode === "v1") { - throw new Error( - "You must set your compatibilty date >= 2024-09-23 when using 'nodejs_compat' compatibility flag", - ); - } const main = props.entrypoint; if (props.noBundle) { @@ -71,7 +66,7 @@ export async function bundleWorkerScript( ).flat(), ), ); - const useColor = !(process.env.CI || process.env.NO_COLOR); + const useColor = !process.env.NO_COLOR; logger.log( `${useColor ? kleur.gray("worker:") : "worker:"} ${useColor ? kleur.blue(props.name) : props.name}`, ); @@ -102,7 +97,8 @@ export async function bundleWorkerScript( platform: "node", minify: false, ...(props.bundle || {}), - conditions: ["workerd", "worker", "browser"], + conditions: ["workerd", "worker", "import", "module", "browser"], + mainFields: ["module", "main"], absWorkingDir: projectRoot, keepNames: true, // Important for Durable Object classes loader: { diff --git a/alchemy/src/cloudflare/bundle/nodejs-compat-mode.ts b/alchemy/src/cloudflare/bundle/nodejs-compat-mode.ts index f120c26bc..c14b5fb54 100644 --- a/alchemy/src/cloudflare/bundle/nodejs-compat-mode.ts +++ b/alchemy/src/cloudflare/bundle/nodejs-compat-mode.ts @@ -1,4 +1,3 @@ -import type { NodeJSCompatMode } from "miniflare"; import { logger } from "../../util/logger.ts"; /** @@ -19,8 +18,12 @@ export async function getNodeJSCompatMode( props?: { noBundle?: boolean; }, -): Promise { - const { getNodeCompat } = await import("miniflare"); +) { + const { getNodeCompat } = await import("miniflare").catch(() => { + throw new Error( + "Miniflare is not installed, but is required to determine the Node.js compatibility mode for Workers. Please run `npm install miniflare`.", + ); + }); const { mode, hasNodejsCompatFlag, @@ -46,5 +49,11 @@ export async function getNodeJSCompatMode( ); } + if (mode === "v1") { + throw new Error( + "You must set your compatibilty date >= 2024-09-23 when using 'nodejs_compat' compatibility flag", + ); + } + return mode; } diff --git a/alchemy/src/cloudflare/certificate-pack.ts b/alchemy/src/cloudflare/certificate-pack.ts new file mode 100644 index 000000000..84a55a4f1 --- /dev/null +++ b/alchemy/src/cloudflare/certificate-pack.ts @@ -0,0 +1,698 @@ +import type { Context } from "../context.ts"; +import { Resource } from "../resource.ts"; +import { logger } from "../util/logger.ts"; +import { handleApiError } from "./api-error.ts"; +import { + createCloudflareApi, + type CloudflareApi, + type CloudflareApiOptions, +} from "./api.ts"; +import type { Zone } from "./zone.ts"; + +/** + * Certificate Authority options for Advanced Certificate Packs + */ +export type CertificateAuthority = "google" | "lets_encrypt" | "ssl_com"; + +/** + * Validation method for certificate verification + */ +export type ValidationMethod = "txt" | "http" | "email"; + +/** + * Validity period options for certificates + */ +export type ValidityDays = 14 | 30 | 90 | 365; + +/** + * Certificate pack status values during lifecycle + */ +export type CertificatePackStatus = + | "initializing" + | "pending_validation" + | "deleted" + | "pending_issuance" + | "pending_deployment" + | "pending_deletion" + | "pending_expiration" + | "expired" + | "active" + | "initializing_timed_out" + | "validation_timed_out" + | "issuance_timed_out" + | "deployment_timed_out" + | "deletion_timed_out" + | "pending_cleanup" + | "staging_deployment" + | "staging_active" + | "deactivating" + | "inactive" + | "backup_issued" + | "holding_deployment"; + +/** + * Properties for creating a Certificate Pack + */ +export interface CertificatePackProps extends CloudflareApiOptions { + /** + * The zone to create the certificate pack for + * Can be a Zone resource, zone ID string, or omitted to auto-infer from hosts + */ + zone?: string | Zone; + + /** + * Certificate Authority to use for issuing the certificate + * - google: Google Trust Services (Enterprise features) + * - lets_encrypt: Let's Encrypt (Free, shorter validity periods) + * - ssl_com: SSL.com (Commercial certificates with extended validation) + * + * **Note:** This property is immutable after creation. To change the CA, + * you must delete and recreate the certificate pack. + */ + certificateAuthority: CertificateAuthority; + + /** + * List of hostnames to include in the certificate + * Maximum 50 hosts, must include the zone apex (root domain) + * Supports wildcards (e.g., "*.example.com") + * + * **Note:** This property is immutable after creation. + */ + hosts: string[]; + + /** + * Certificate type - only "advanced" is supported + * + * **Note:** This property is immutable after creation. + * @default "advanced" + */ + type?: "advanced"; + + /** + * Method used to validate domain ownership + * - txt: DNS TXT record validation + * - http: HTTP file validation + * - email: Email validation + * + * **Note:** This property is immutable after creation. + */ + validationMethod: ValidationMethod; + + /** + * Certificate validity period in days + * Available options: 14, 30, 90, or 365 days + * + * **Note:** This property is immutable after creation. + */ + validityDays: ValidityDays; + + /** + * Whether to add Cloudflare branding subdomain as Common Name + * Adds sni.cloudflaressl.com subdomain when enabled + * + * **Note:** This is the only property that can be updated after creation. + * @default false + */ + cloudflareBranding?: boolean; + + /** + * Whether to delete the certificate pack + * If set to false, the pack will remain but the resource will be removed from state + * + * @default true + */ + delete?: boolean; +} + +/** + * Output returned after Certificate Pack creation/update + */ +export interface CertificatePack + extends Resource<"cloudflare::CertificatePack"> { + /** + * The unique ID of the certificate pack + */ + id: string; + + /** + * Certificate Authority used for the certificate + */ + certificateAuthority: CertificateAuthority; + + /** + * Whether Cloudflare branding is enabled + */ + cloudflareBranding: boolean; + + /** + * List of hostnames included in the certificate + */ + hosts: string[]; + + /** + * Current status of the certificate pack + */ + status: CertificatePackStatus; + + /** + * Certificate type + */ + type: "advanced"; + + /** + * Validation method used for domain verification + */ + validationMethod: ValidationMethod; + + /** + * Certificate validity period in days + */ + validityDays: ValidityDays; + + /** + * Zone ID the certificate pack belongs to + */ + zoneId: string; + + /** + * Zone name (domain) + */ + zoneName: string; +} + +/** + * Creates and manages Cloudflare Advanced Certificate Packs. + * + * Advanced Certificate Packs provide flexible SSL/TLS certificates with + * multiple Certificate Authority options, custom validity periods, and + * support for up to 50 hostnames per certificate. + * + * **Important Notes:** + * - Requires a paid Cloudflare plan (not available on Free plans) + * - Certificate provisioning can take up to 10 minutes + * - Most properties are immutable after creation (only cloudflareBranding can be updated) + * - To change immutable properties, you must delete and recreate the certificate pack + * + * @example + * // Create a basic certificate pack with Let's Encrypt + * const basicCert = await CertificatePack("my-cert", { + * zone: myZone, + * certificateAuthority: "lets_encrypt", + * hosts: ["example.com", "www.example.com"], + * validationMethod: "txt", + * validityDays: 90 + * }); + * + * @example + * // Create an enterprise certificate with Google Trust Services + * const enterpriseCert = await CertificatePack("enterprise-cert", { + * zone: "example.com", + * certificateAuthority: "google", + * hosts: ["example.com", "*.example.com", "api.example.com"], + * validationMethod: "txt", + * validityDays: 365, + * cloudflareBranding: true + * }); + * + * @example + * // Create a wildcard certificate with SSL.com + * const wildcardCert = await CertificatePack("wildcard-cert", { + * zone: myZone, + * certificateAuthority: "ssl_com", + * hosts: ["example.com", "*.example.com"], + * validationMethod: "email", + * validityDays: 365 + * }); + * + * @example + * // Create a certificate for multiple subdomains + * const multiDomainCert = await CertificatePack("multi-cert", { + * zone: "example.com", + * certificateAuthority: "lets_encrypt", + * hosts: [ + * "example.com", + * "www.example.com", + * "api.example.com", + * "admin.example.com", + * "blog.example.com" + * ], + * validationMethod: "http", + * validityDays: 90 + * }); + * + * @see https://developers.cloudflare.com/api/resources/ssl/subresources/certificate_packs/ + */ +export const CertificatePack = Resource( + "cloudflare::CertificatePack", + async function ( + this: Context, + _id: string, + props: CertificatePackProps, + ): Promise { + // Create Cloudflare API client with automatic account discovery + const api = await createCloudflareApi(props); + + // Resolve zone ID and zone name + let zoneId: string; + let zoneName: string; + + if (props.zone) { + // Zone provided - use it + if (typeof props.zone === "string") { + zoneId = props.zone; + // Try to get zone name from API for better error messages + try { + const zoneResponse = await api.get(`/zones/${zoneId}`); + if (zoneResponse.ok) { + const zoneData = (await zoneResponse.json()) as { + result: { name: string }; + }; + zoneName = zoneData.result.name; + } else { + zoneName = zoneId; // Fallback to ID + } + } catch { + zoneName = zoneId; // Fallback to ID + } + } else { + zoneId = props.zone.id; + zoneName = props.zone.name || props.zone.id; + } + } else { + // Auto-infer zone from the first host + if (props.hosts.length === 0) { + throw new Error( + "At least one host must be specified when zone is not provided", + ); + } + + logger.log(`Auto-inferring zone from hostname: ${props.hosts[0]}`); + const zoneInfo = await findZoneForHostname(api, props.hosts[0]); + zoneId = zoneInfo.zoneId; + zoneName = zoneInfo.zoneName; + logger.log(`Auto-inferred zone: ${zoneName} (${zoneId})`); + } + + if (this.phase === "delete") { + if (this.output?.id && props.delete !== false) { + const deleteResponse = await api.delete( + `/zones/${zoneId}/ssl/certificate_packs/${this.output.id}`, + ); + + if (!deleteResponse.ok && deleteResponse.status !== 404) { + await handleApiError( + deleteResponse, + "delete", + "certificate pack", + this.output.id, + ); + } + } else { + logger.warn("Certificate pack not found, skipping delete"); + } + return this.destroy(); + } + + if (this.phase === "update" && this.output?.id) { + // Validate immutable properties + const currentPack = this.output; + + if (props.certificateAuthority !== currentPack.certificateAuthority) { + throw new Error( + `Cannot change certificateAuthority from '${currentPack.certificateAuthority}' to '${props.certificateAuthority}'. Certificate Authority is immutable after creation. You must delete and recreate the certificate pack.`, + ); + } + + if ( + JSON.stringify(props.hosts.sort()) !== + JSON.stringify(currentPack.hosts.sort()) + ) { + throw new Error( + `Cannot change hosts from [${currentPack.hosts.join(", ")}] to [${props.hosts.join(", ")}]. Hosts are immutable after creation. You must delete and recreate the certificate pack.`, + ); + } + + if (props.validationMethod !== currentPack.validationMethod) { + throw new Error( + `Cannot change validationMethod from '${currentPack.validationMethod}' to '${props.validationMethod}'. Validation method is immutable after creation. You must delete and recreate the certificate pack.`, + ); + } + + if (props.validityDays !== currentPack.validityDays) { + throw new Error( + `Cannot change validityDays from ${currentPack.validityDays} to ${props.validityDays}. Validity period is immutable after creation. You must delete and recreate the certificate pack.`, + ); + } + + const type = props.type || "advanced"; + if (type !== currentPack.type) { + throw new Error( + `Cannot change type from '${currentPack.type}' to '${type}'. Type is immutable after creation. You must delete and recreate the certificate pack.`, + ); + } + + // Only cloudflareBranding can be updated + if (props.cloudflareBranding !== currentPack.cloudflareBranding) { + logger.log( + `Updating certificate pack cloudflare branding from ${currentPack.cloudflareBranding} to ${props.cloudflareBranding}`, + ); + + const updateResponse = await api.patch( + `/zones/${zoneId}/ssl/certificate_packs/${this.output.id}`, + { + cloudflare_branding: props.cloudflareBranding || false, + }, + ); + + if (!updateResponse.ok) { + await handleApiError( + updateResponse, + "update", + "certificate pack", + this.output.id, + ); + } + } + + // Get updated certificate pack details + const response = await api.get( + `/zones/${zoneId}/ssl/certificate_packs/${this.output.id}`, + ); + + if (!response.ok) { + await handleApiError( + response, + "get", + "certificate pack", + this.output.id, + ); + } + + const updatedPack = ( + (await response.json()) as { result: CloudflareCertificatePack } + ).result; + + return this({ + id: updatedPack.id, + certificateAuthority: updatedPack.certificate_authority, + cloudflareBranding: updatedPack.cloudflare_branding, + hosts: updatedPack.hosts, + status: updatedPack.status, + type: updatedPack.type, + validationMethod: updatedPack.validation_method, + validityDays: updatedPack.validity_days, + zoneId: updatedPack.zone_id, + zoneName: zoneName, + }); + } + + // Create new certificate pack + if (props.hosts.length === 0) { + throw new Error("At least one host must be specified"); + } + + if (props.hosts.length > 50) { + throw new Error("Maximum 50 hosts are allowed per certificate pack"); + } + + // Validate that zone apex is included + const hasZoneApex = props.hosts.some( + (host) => host === zoneName || (zoneName && host === zoneName), + ); + + if (!hasZoneApex && zoneName) { + logger.warn( + `Zone apex '${zoneName}' is not included in hosts. This may cause certificate validation issues.`, + ); + } + + // Check for existing certificate pack that matches our configuration + const existingPack = await findMatchingCertificatePack(api, zoneId, props); + + if (existingPack) { + // Adopt the existing certificate pack + logger.log( + `Adopting existing certificate pack ${existingPack.id} instead of creating a new one`, + ); + + return this({ + id: existingPack.id, + certificateAuthority: existingPack.certificate_authority, + cloudflareBranding: existingPack.cloudflare_branding, + hosts: existingPack.hosts, + status: existingPack.status, + type: existingPack.type, + validationMethod: existingPack.validation_method, + validityDays: existingPack.validity_days, + zoneId: existingPack.zone_id, + zoneName: zoneName, + }); + } + + logger.log( + `Creating certificate pack with ${props.hosts.length} hosts using ${props.certificateAuthority}`, + ); + + const createResponse = await api.post( + `/zones/${zoneId}/ssl/certificate_packs/order`, + { + certificate_authority: props.certificateAuthority, + cloudflare_branding: props.cloudflareBranding || false, + hosts: props.hosts, + type: props.type || "advanced", + validation_method: props.validationMethod, + validity_days: props.validityDays, + }, + ); + + if (!createResponse.ok) { + const errorText = await createResponse.text(); + + // Provide helpful error messages for common issues + if (errorText.includes("subscription")) { + throw new Error( + `Failed to create certificate pack: Advanced Certificate Packs require a paid Cloudflare plan. Please upgrade your subscription to use this feature.\n\nOriginal error: ${errorText}`, + ); + } + + if (errorText.includes("quota")) { + throw new Error( + `Failed to create certificate pack: Certificate pack quota exceeded. Please check your account limits.\n\nOriginal error: ${errorText}`, + ); + } + + // Throw generic error for other cases + throw new Error( + `Failed to create certificate pack: ${createResponse.statusText}\n\n${errorText}`, + ); + } + + const createdPack = ( + (await createResponse.json()) as { result: CloudflareCertificatePack } + ).result; + + logger.log( + `Certificate pack created with ID ${createdPack.id}. Status: ${createdPack.status}. Note: Certificate provisioning can take up to 10 minutes.`, + ); + + return this({ + id: createdPack.id, + certificateAuthority: createdPack.certificate_authority, + cloudflareBranding: createdPack.cloudflare_branding, + hosts: createdPack.hosts, + status: createdPack.status, + type: createdPack.type, + validationMethod: createdPack.validation_method, + validityDays: createdPack.validity_days, + zoneId: createdPack.zone_id, + zoneName: zoneName, + }); + }, +); + +/** + * Cloudflare Certificate Pack API response format + */ +interface CloudflareCertificatePack { + id: string; + certificate_authority: CertificateAuthority; + cloudflare_branding: boolean; + hosts: string[]; + status: CertificatePackStatus; + type: "advanced"; + validation_method: ValidationMethod; + validity_days: ValidityDays; + zone_id: string; +} + +/** + * Helper function to wait for certificate pack to reach active status + * Useful for testing or when you need to ensure the certificate is ready + * + * @param api CloudflareApi instance + * @param zoneId Zone ID + * @param certificatePackId Certificate pack ID + * @param timeoutMs Maximum time to wait in milliseconds (default: 15 minutes) + * @returns Promise resolving to the final certificate pack status + * + * @example + * // Wait for certificate to become active + * const finalStatus = await waitForCertificatePackActive( + * api, + * zoneId, + * certificatePack.id, + * 10 * 60 * 1000 // 10 minutes + * ); + * console.log(`Certificate pack is now: ${finalStatus}`); + */ +export async function waitForCertificatePackActive( + api: CloudflareApi, + zoneId: string, + certificatePackId: string, + timeoutMs: number = 15 * 60 * 1000, // 15 minutes default +): Promise { + const startTime = Date.now(); + const pollInterval = 30 * 1000; // Poll every 30 seconds + + while (Date.now() - startTime < timeoutMs) { + const response = await api.get( + `/zones/${zoneId}/ssl/certificate_packs/${certificatePackId}`, + ); + + if (!response.ok) { + throw new Error( + `Failed to check certificate pack status: ${response.statusText}`, + ); + } + + const pack = ( + (await response.json()) as { result: CloudflareCertificatePack } + ).result; + + // Return immediately if active or in a final error state + if (pack.status === "active") { + return pack.status; + } + + if ( + pack.status.includes("timed_out") || + pack.status === "expired" || + pack.status === "deleted" + ) { + return pack.status; + } + + // Wait before next poll + await new Promise((resolve) => setTimeout(resolve, pollInterval)); + } + + throw new Error( + `Certificate pack did not become active within ${timeoutMs / 1000 / 60} minutes`, + ); +} + +/** + * Helper function to find zone ID from a hostname + * Searches for the zone that matches the hostname or its parent domains + * + * @param api CloudflareApi instance + * @param hostname The hostname to find the zone for + * @returns Promise resolving to the zone ID and zone name + */ +async function findZoneForHostname( + api: CloudflareApi, + hostname: string, +): Promise<{ zoneId: string; zoneName: string }> { + // Remove wildcard prefix if present + const cleanHostname = hostname.replace(/^\*\./, ""); + + // Get all zones and find the best match + const response = await api.get("/zones"); + + if (!response.ok) { + throw new Error(`Failed to list zones: ${response.statusText}`); + } + + const zonesData = (await response.json()) as { + result: Array<{ id: string; name: string }>; + }; + + // Find the zone that best matches the hostname + // We look for the longest matching zone name (most specific) + let bestMatch: { zoneId: string; zoneName: string } | null = null; + let longestMatch = 0; + + for (const zone of zonesData.result) { + if ( + cleanHostname === zone.name || + cleanHostname.endsWith(`.${zone.name}`) + ) { + if (zone.name.length > longestMatch) { + longestMatch = zone.name.length; + bestMatch = { zoneId: zone.id, zoneName: zone.name }; + } + } + } + + if (!bestMatch) { + throw new Error( + `Could not find zone for hostname '${hostname}'. Available zones: ${zonesData.result.map((z) => z.name).join(", ")}`, + ); + } + + return bestMatch; +} + +/** + * Helper function to find existing certificate packs that match the given configuration + * Used to adopt existing certificates instead of creating duplicates + * + * @param api CloudflareApi instance + * @param zoneId Zone ID to search in + * @param props Certificate pack properties to match + * @returns Promise resolving to matching certificate pack or null if none found + */ +async function findMatchingCertificatePack( + api: CloudflareApi, + zoneId: string, + props: CertificatePackProps, +): Promise { + const response = await api.get(`/zones/${zoneId}/ssl/certificate_packs`); + + if (!response.ok) { + throw new Error(`Failed to list certificate packs: ${response.statusText}`); + } + + const packsData = (await response.json()) as { + result: CloudflareCertificatePack[]; + }; + + // Find a certificate pack that matches our configuration + for (const pack of packsData.result) { + // Skip deleted or expired packs + if (pack.status === "deleted" || pack.status === "expired") { + continue; + } + + // Check if the configuration matches + if ( + pack.certificate_authority === props.certificateAuthority && + pack.validation_method === props.validationMethod && + pack.validity_days === props.validityDays && + pack.type === (props.type || "advanced") + ) { + // Check if all requested hosts are covered by this certificate pack + const packHosts = new Set(pack.hosts); + const allHostsCovered = props.hosts.every((host) => packHosts.has(host)); + + if (allHostsCovered) { + logger.log( + `Found existing certificate pack ${pack.id} that covers all requested hosts`, + ); + return pack; + } + } + } + + return null; +} diff --git a/alchemy/src/cloudflare/container.ts b/alchemy/src/cloudflare/container.ts new file mode 100644 index 000000000..8d18c0a42 --- /dev/null +++ b/alchemy/src/cloudflare/container.ts @@ -0,0 +1,843 @@ +import type { Context } from "../context.ts"; +import { Image, type ImageProps, type ImageRegistry } from "../docker/image.ts"; +import { Resource } from "../resource.ts"; +import { secret } from "../secret.ts"; +import { + type CloudflareApi, + type CloudflareApiOptions, + createCloudflareApi, +} from "./api.ts"; + +export interface ContainerProps + extends Omit, + Partial { + className: string; + maxInstances?: number; + scriptName?: string; + instanceType?: InstanceType; + observability?: DeploymentObservability; + schedulingPolicy?: SchedulingPolicy; +} + +/** + * @see https://developers.cloudflare.com/containers/pricing/ + */ +export type InstanceType = "dev" | "basic" | "standard" | (string & {}); + +export function isContainer(binding: any): binding is Container { + return binding.type === "container"; +} + +export type Container = { + type: "container"; + id: string; + name?: string; + className: string; + image: Image; + maxInstances?: number; + scriptName?: string; + sqlite?: true; + instanceType?: InstanceType; + observability?: DeploymentObservability; + schedulingPolicy?: SchedulingPolicy; + /** + * @internal + */ + __phantom?: T; +}; + +export async function Container( + id: string, + props: ContainerProps, +): Promise> { + // Otherwise, obtain Cloudflare registry credentials automatically + const api = await createCloudflareApi(props); + const creds = await getContainerCredentials(api); + + const registry: ImageRegistry = { + server: "registry.cloudflare.com", + username: creds.username || creds.user!, + password: secret(creds.password), + }; + + // Ensure repository name is namespaced with accountId + const repoBase = props.name ?? id; + const repoName = repoBase.includes("/") + ? repoBase + : `${api.accountId}/${repoBase}`; + + // Replace disallowed "latest" tag with timestamp + const finalTag = + props.tag === undefined || props.tag === "latest" + ? `latest-${Date.now()}` + : props.tag; + + const image = await Image(id, { + build: props.build, + name: repoName, + tag: finalTag, + skipPush: false, + registry, + }); + + return { + type: "container", + id, + name: props.name ?? id, + className: props.className, + image, + maxInstances: props.maxInstances, + scriptName: props.scriptName, + instanceType: props.instanceType, + observability: props.observability, + schedulingPolicy: props.schedulingPolicy, + sqlite: true, + }; +} + +export interface ContainerApplicationRollout { + strategy: "rolling"; + kind?: "full_auto"; + stepPercentage: number; + targetConfiguration: { + image: string; + instance_type?: InstanceType; + observability: { + logs: { + enabled: boolean; + }; + }; + }; +} + +export interface ContainerApplicationProps extends CloudflareApiOptions { + name: string; + schedulingPolicy?: SchedulingPolicy; + instances?: number; + /** + * The instance type to be used for the deployment. + * + * @default "dev" + */ + instanceType?: InstanceType; + /** + * The observability configuration for the deployment. + */ + observability?: DeploymentObservability; + /** + * The maximum number of instances to be used for the deployment. + */ + maxInstances?: number; + image: Image; + registryId?: string; + durableObjects?: { + namespaceId: string; + }; + rollout?: ContainerApplicationRollout; +} + +export type SchedulingPolicy = + | "moon" + | "gpu" + | "regional" + | "fill_metals" + | "default"; + +export interface ContainerApplication + extends Resource<"cloudflare::ContainerApplication"> { + id: string; + name: string; +} + +/** + * Deploy and manage container applications on Cloudflare's global network. + * + * ContainerApplication creates a managed container deployment that runs your Docker images + * with automatic scaling, scheduling, and integration with Cloudflare's services. + * + * @example + * // Deploy a simple web application container + * const webApp = await ContainerApplication("my-web-app", { + * name: "my-web-app", + * image: await Image("web-app", { + * name: "web-app", + * build: { + * context: "./docker/web-app" + * } + * }), + * instances: 1, + * maxInstances: 3 + * }); + * + * @example + * // Deploy a container with GPU support for AI workloads + * const aiApp = await ContainerApplication("ai-inference", { + * name: "ai-inference", + * image: await Image("ai-model", { + * name: "ai-model", + * build: { + * context: "./docker/ai" + * } + * }), + * schedulingPolicy: "gpu", + * instances: 2, + * maxInstances: 5 + * }); + * + * @example + * // Deploy a container integrated with Durable Objects + * const doApp = await ContainerApplication("stateful-app", { + * name: "stateful-app", + * image: await Image("do-app", { + * name: "do-app", + * build: { + * context: "./container" + * } + * }), + * durableObjects: { + * namespaceId: myDurableObjectNamespace.id + * }, + * instances: 1, + * maxInstances: 10 + * }); + * + * @example + * // Create a Container binding for use in a Worker + * const worker = await Worker("my-worker", { + * name: "my-worker", + * entrypoint: "./src/worker.ts", + * bindings: { + * MY_CONTAINER: new Container("my-container", { + * className: "MyContainerClass", + * image: await Image("container-do", { + * name: "container-do", + * context: "./docker/durable-object" + * }), + * maxInstances: 100, + * name: "my-container-do" + * }) + * } + * }); + */ +export const ContainerApplication = Resource( + "cloudflare::ContainerApplication", + async function ( + this: Context, + _id: string, + props: ContainerApplicationProps, + ): Promise { + const api = await createCloudflareApi(props); + if (this.phase === "delete") { + if (this.output?.id) { + // Delete the container application + await deleteContainerApplication(api, this.output.id); + } + return this.destroy(); + } + // Prefer the immutable repo digest if present. Falls back to the tag reference. + const imageReference = props.image.repoDigest ?? props.image.imageRef; + + const configuration = { + image: imageReference, + instance_type: props.instanceType ?? "dev", + observability: { + logs: { + enabled: true, + }, + logging: { + enabled: true, + }, + }, + }; + if (this.phase === "update") { + const application = await updateContainerApplication( + api, + this.output.id, + { + instances: props.instances ?? 1, + max_instances: props.maxInstances ?? 10, + scheduling_policy: props.schedulingPolicy ?? "default", + configuration, + }, + ); + // TODO(sam): should we wait for the rollout to complete? + await createContainerApplicationRollout(api, application.id, { + description: "Progressive update", + strategy: "rolling", + kind: props.rollout?.kind ?? "full_auto", + step_percentage: props.rollout?.stepPercentage ?? 25, + target_configuration: configuration, + }); + return this({ + id: application.id, + name: application.name, + }); + } else { + const application = await createContainerApplication(api, { + name: props.name, + scheduling_policy: props.schedulingPolicy ?? "default", + instances: props.instances ?? 1, + max_instances: props.maxInstances ?? 1, + durable_objects: props.durableObjects + ? { + namespace_id: props.durableObjects.namespaceId, + } + : undefined, + constraints: { + tier: 1, + }, + configuration: { + image: imageReference, + instance_type: props.instanceType ?? "dev", + observability: { + logs: { + enabled: true, + }, + }, + }, + }); + + return this({ + id: application.id, + name: application.name, + }); + } + }, +); + +export interface ContainerApplicationData { + name: string; + scheduling_policy: string; + instances: number; + max_instances: number; + constraints: { + tier: number; + [key: string]: any; + }; + configuration: { + image: string; + location: string; + vcpu: number; + memory_mib: number; + disk: any; + network: any; + command: string[]; + entrypoint: string[]; + runtime: string; + deployment_type: string; + observability: any; + memory: string; + [key: string]: any; + }; + durable_objects: { + namespace_id: string; + [key: string]: any; + }; + id: string; + account_id: string; + created_at: string; + version: number; + durable_object_namespace_id: string; + health: { + instances: any; + [key: string]: any; + }; + [key: string]: any; +} + +export async function listContainerApplications( + api: CloudflareApi, +): Promise { + const deployments = await api.get( + `/accounts/${api.accountId}/containers/applications`, + ); + const response = (await deployments.json()) as any as { + result: ContainerApplicationData[]; + errors: { message: string }[]; + }; + if (deployments.ok) { + return response.result; + } + throw Error( + `Failed to list container applications: ${response.errors.map((e) => e.message).join(", ")}`, + ); +} + +export interface CreateContainerApplicationBody { + name: string; + max_instances: number; + configuration: DeploymentConfiguration; + durable_objects?: { + namespace_id: string; + }; + instances?: number; + scheduling_policy?: string; + constraints?: { tier: number }; + [key: string]: any; +} + +export async function createContainerApplication( + api: CloudflareApi, + body: CreateContainerApplicationBody, +) { + const response = await api.post( + `/accounts/${api.accountId}/containers/applications`, + body, + ); + const result = (await response.json()) as { + result: ContainerApplicationData; + errors: { message: string }[]; + }; + if (response.ok) { + return result.result; + } + + throw Error( + `Failed to create container application: ${result.errors?.map((e: { message: string }) => e.message).join(", ") ?? "Unknown error"}`, + ); +} + +type Region = + | "AFR" + | "APAC" + | "EEUR" + | "ENAM" + | "WNAM" + | "ME" + | "OC" + | "SAM" + | "WEUR" + | (string & {}); + +type City = + | "AFR" + | "APAC" + | "EEUR" + | "ENAM" + | "WNAM" + | "ME" + | "OC" + | "SAM" + | "WEUR" + | (string & {}); + +export type UpdateApplicationRequestBody = { + /** + * Number of deployments to maintain within this applicaiton. This can be used to scale the appliation up/down. + */ + instances?: number; + max_instances?: number; + affinities?: { + colocation?: "datacenter"; + }; + scheduling_policy?: SchedulingPolicy; + constraints?: { + region?: Region; + tier?: number; + regions?: Array; + cities?: Array; + }; + /** + * The deployment configuration of all deployments created by this application. + * Right now, if you modify the application configuration, only new deployments + * created will have the new configuration. You can delete old deployments to + * release new instances. + * + * TODO(sam): should this trigger a replacement? + */ + configuration?: DeploymentConfiguration; +}; + +export async function updateContainerApplication( + api: CloudflareApi, + applicationId: string, + body: UpdateApplicationRequestBody, +) { + const response = await api.patch( + `/accounts/${api.accountId}/containers/applications/${applicationId}`, + body, + ); + const result = (await response.json()) as { + result: ContainerApplicationData; + errors: { message: string }[]; + }; + if (response.ok) { + return result.result; + } + + throw Error( + `Failed to create container application: ${result.errors?.map((e: { message: string }) => e.message).join(", ") ?? "Unknown error"}`, + ); +} + +export async function deleteContainerApplication( + api: CloudflareApi, + applicationId: string, +) { + const response = await api.delete( + `/accounts/${api.accountId}/containers/applications/${applicationId}`, + ); + const result = (await response.json()) as any; + if (response.ok) { + return result.result; + } + throw Error( + `Failed to delete container application: ${result.errors?.map((e: { message: string }) => e.message).join(", ") ?? "Unknown error"}`, + ); +} + +interface CreateRolloutApplicationRequest { + description: string; + strategy: "rolling"; + kind?: "full_auto"; + step_percentage: number; + target_configuration: DeploymentConfiguration; +} + +interface CreateRolloutApplicationResponse { + id: string; + created_at: string; + last_updated_at: string; + description: string; + status: "progressing" | "completed" | "failed" | (string & {}); + health: { + instances: { + healthy: number; + failed: number; + starting: number; + scheduling: number; + }; + }; + kind: "full_auto" | (string & {}); + strategy: "rolling" | (string & {}); + current_configuration: { + image: string; + observability?: { + logs?: { + enabled: boolean; + }; + logging?: { + enabled: boolean; + }; + }; + }; + target_configuration: DeploymentConfiguration; + current_version: number; + target_version: number; + steps: Array<{ + id: number; + status: "progressing" | "pending" | "completed" | "failed" | (string & {}); + step_size: { + percentage: number; + }; + description: string; + started_at?: string; + }>; + progress: { + total_steps: number; + current_step: number; + updated_instances: number; + total_instances: number; + }; +} + +export async function createContainerApplicationRollout( + api: CloudflareApi, + applicationId: string, + body: CreateRolloutApplicationRequest, +) { + const response = await api.post( + `/accounts/${api.accountId}/containers/applications/${applicationId}/rollouts`, + body, + ); + const result = (await response.json()) as { + result: CreateRolloutApplicationResponse; + errors: { message: string }[]; + }; + if (response.ok) { + return result.result; + } + throw Error( + `Failed to create container application rollout: ${result.errors.map((e: { message: string }) => e.message).join(", ")}`, + ); +} + +export type ImageRegistryCredentialsConfiguration = { + permissions: Array<"pull" | "push">; + expiration_minutes: number; +}; + +export async function getContainerCredentials( + api: CloudflareApi, + registryId = "registry.cloudflare.com", +) { + const credentials = await api.post( + `/accounts/${api.accountId}/containers/registries/${registryId}/credentials`, + { + permissions: ["pull", "push"], + expiration_minutes: 60, + } satisfies ImageRegistryCredentialsConfiguration, + ); + const result = (await credentials.json()) as { + result: { + user?: string; + username?: string; + password: string; + }; + errors: { message: string }[]; + }; + if (credentials.ok) { + return result.result; + } + throw Error( + `Failed to get container credentials: ${result.errors.map((e: { message: string }) => e.message).join(", ")}`, + ); +} + +// The Cloudflare managed registry is special in that the namespaces for repos should always +// start with the Cloudflare Account tag +// This is a helper to generate the image tag with correct namespace attached to the Cloudflare Registry host +export const getCloudflareRegistryWithAccountNamespace = ( + accountID: string, + tag: string, +): string => { + return `${getCloudflareContainerRegistry()}/${accountID}/${tag}`; +}; + +// default cloudflare managed registry, can be overriden with the env var - CLOUDFLARE_CONTAINER_REGISTRY +export const getCloudflareContainerRegistry = () => { + // previously defaulted to registry.cloudchamber.cfdata.org + return process.env.CLOUDFLARE_CONTAINER_REGISTRY ?? "registry.cloudflare.com"; +}; + +/** + * Given a container image that is a registry link, this function + * returns true if the link points the Cloudflare container registry + * (defined as per `getCloudflareContainerRegistry` above) + */ +export function isCloudflareRegistryLink(image: string) { + const cfRegistry = getCloudflareContainerRegistry(); + return image.includes(cfRegistry); +} + +/** Prefixes with the cloudflare-dev namespace. The name should be the container's DO classname, and the tag a build uuid. */ +export const getDevContainerImageName = (name: string, tag: string) => { + return `${MF_DEV_CONTAINER_PREFIX}/${name.toLowerCase()}:${tag}`; +}; + +export const MF_DEV_CONTAINER_PREFIX = "cloudflare-dev"; + +export interface ContainerIdentity { + account_id: string; + external_account_id: string; + legacy_identity: string; + capabilities: string[]; + limits: { + account_id: string; + vcpu_per_deployment: number; + memory_mib_per_deployment: number; + memory_per_deployment: string; + disk_per_deployment: string; + disk_mb_per_deployment: number; + total_vcpu: number; + total_memory_mib: number; + node_group: string; + ipv4s: number; + network_modes: string[]; + total_disk_mb: number; + total_memory: string; + }; + locations: any[]; + defaults: { + vcpus: number; + memory_mib: number; + memory: string; + disk_mb: number; + }; +} + +export async function getContainerIdentity(api: CloudflareApi) { + const metrics = await api.get(`/accounts/${api.accountId}/containers/me`); + const result = (await metrics.json()) as { + result: ContainerIdentity; + errors: { message: string }[]; + }; + if (metrics.ok) { + return result.result; + } + throw Error( + `Failed to get container me: ${result.errors.map((e: { message: string }) => e.message).join(", ")}`, + ); +} + +/** + * Duration string. From Go documentation: + * A string representing the duration in the form "3d1h3m". Leading zero units are omitted. + * As a special case, durations less than one second format use a smaller unit (milli-, micro-, or nanoseconds) + * to ensure that the leading digit is non-zero. + */ +export type Duration = string; + +interface DeploymentObservability { + logs?: { + enabled: boolean; + }; +} + +export type DeploymentConfiguration = { + /** + * The image to be used for the deployment. + */ + image: string; + /** + * The instance type to be used for the deployment. + */ + instance_type?: InstanceType; + /** + * The observability configuration for the deployment. + */ + observability?: DeploymentObservability; + /** + * A list of SSH public key IDs from the account + */ + ssh_public_key_ids?: Array; + /** + * A list of objects with secret names and the their access types from the account + */ + secrets?: Array<{ + /** + * The name of the secret within the container + */ + name: string; + type: "env"; + /** + * Corresponding secret name from the account + */ + secret: string; + }>; + /** + * Specify the vcpu to be used for the deployment. The default will be the one configured for the account. + */ + vcpu?: number; + /** + * Specify the memory to be used for the deployment. The default will be the one configured for the account. + */ + memory?: string; + /** + * The disk configuration for this deployment + */ + disk?: { + size: string; + }; + /** + * Container environment variables + */ + environment_variables?: Array<{ + name: string; + value: string; + }>; + /** + * Deployment labels + */ + labels?: Array<{ + name: string; + value: string; + }>; + network?: { + /** + * Assign an IPv4 address to the deployment. One of 'none' (default), 'predefined' (allocate one from a set of IPv4 addresses in the global pool), 'account' (allocate one from a set of IPv4 addresses preassigned in the account pool). Only applicable to "public" mode. + * + */ + assign_ipv4?: "none" | "predefined" | "account"; + /** + * Assign an IPv6 address to the deployment. One of 'predefined' (allocate one from a set of IPv6 addresses in the global pool), 'account' (allocate one from a set of IPv6 addresses preassigned in the account pool). The container will always be assigned to an IPv6 if the networking mode is "public". + * + */ + assign_ipv6?: "none" | "predefined" | "account"; + mode?: "public" | "private"; + }; + command?: string[]; + entrypoint?: string[]; + dns?: { + /** + * List of DNS servers that the deployment will use to resolve domain names. You can only specify a maximum of 3. + */ + servers?: Array; + /** + * The container resolver will append these domains to every resolve query. For example, if you have 'google.com', + * and your deployment queries 'web', it will append 'google.com' to 'web' in the search query before trying 'web'. + * Limited to 6 domains. + */ + searches?: Array; + }; + ports?: Array<{ + /** + * The name of the port. The port name should be unique for each deployment. Minimum length of 1 and maximum length of 15. No consecutive dashes. If the name is 'web-ui', the container will receive an environment variable as follows: + * - CLOUDFLARE_PORT_WEB_UI: Port inside the container + * - CLOUDFLARE_HOST_PORT_WEB_UI: Port outside the container + * - CLOUDFLARE_HOST_IP_WEB_UI: Address of the external network interface the port is allocated on + * - CLOUDFLARE_HOST_ADDR_WEB_UI: CLOUDFLARE_HOST_ADDR_WEB_UI ':' CLOUDFLARE_HOST_PORT_WEB_UI + * + */ + name: string; + /** + * Optional port number, it's assigned only if the user specified it. If it's not specified, the datacenter scheduler will decide it. + */ + port?: number; + }>; + /** + * Health and readiness checks for this deployment. + */ + checks?: Array<{ + /** + * Optional name for the check. If omitted, a name will be generated automatically. + */ + name?: string; + /** + * The type of check to perform. A TCP check succeeds if it can connect to the provided port. An HTTP check succeeds if it receives a successful HTTP response (2XX) + */ + type: "http" | "tcp"; + /** + * Connect to the port using TLS + */ + tls?: boolean; + /** + * The name of the port defined in the "ports" property of the deployment + */ + port: string; + /** + * Configuration for HTTP checks. Only valid when "type" is "http" + */ + http?: { + method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE" | "OPTIONS" | "HEAD"; + /** + * If the method is one of POST, PATCH or PUT, this is required. It's the body that will be passed to the HTTP healthcheck request. + */ + body?: string; + /** + * Path that will be used to perform the healthcheck. + */ + path?: string; + /** + * HTTP headers to include in the request. + */ + headers?: Record; + }; + /** + * How often the check should be performed + */ + interval: Duration; + /** + * The amount of time to wait for the check to complete before considering the check to have failed + */ + timeout: Duration; + /** + * Number of times to attempt the check before considering it to have failed + */ + retries?: number; + /** + * The kind of check. A failed "healthy" check affects a deployment's "healthy" status, while a failed "ready" check affects a deployment's "ready" status. + */ + kind: "health" | "ready"; + }>; +}; diff --git a/alchemy/src/cloudflare/custom-domain.ts b/alchemy/src/cloudflare/custom-domain.ts index 3e0c66a85..61c058dd8 100644 --- a/alchemy/src/cloudflare/custom-domain.ts +++ b/alchemy/src/cloudflare/custom-domain.ts @@ -7,6 +7,7 @@ import { type CloudflareApi, type CloudflareApiOptions, } from "./api.ts"; +import { inferZoneIdFromPattern } from "./route.ts"; /** * Properties for creating or updating a CustomDomain @@ -19,8 +20,10 @@ export interface CustomDomainProps extends CloudflareApiOptions { /** * Cloudflare Zone ID for the domain + * + * @default - inferred from the domain name */ - zoneId: string; + zoneId?: string; /** * Name of the worker to bind to the domain @@ -32,6 +35,14 @@ export interface CustomDomainProps extends CloudflareApiOptions { * @default "production" */ environment?: string; + + /** + * If true, adopt an existing custom domain binding during creation. + * If false and the domain already exists, creation will fail. + * This only applies during the create phase. + * @default false + */ + adopt?: boolean; } /** @@ -85,6 +96,15 @@ export interface CustomDomain * workerName: apiWorker.name // Use the name from the Worker resource * }); * + * @example + * // Adopt an existing domain binding during creation + * const existingDomain = await CustomDomain("api-domain", { + * name: "api.example.com", + * zoneId: "YOUR_ZONE_ID", + * workerName: "my-api-worker", + * adopt: true // If domain already exists, adopt it instead of failing + * }); + * * @see https://developers.cloudflare.com/api/resources/workers/subresources/domains/ */ export const CustomDomain = Resource( @@ -97,23 +117,15 @@ export const CustomDomain = Resource( // Create Cloudflare API client with automatic account discovery const api = await createCloudflareApi(props); - // Validate required properties - if (!props.name) { - throw new Error("Domain name (props.name) is required"); - } - if (!props.zoneId) { - throw new Error("Zone ID (props.zoneId) is required"); - } - if (!props.workerName) { - throw new Error("Worker name (props.workerName) is required"); - } - if (this.phase === "delete") { await deleteCustomDomain(this, api, logicalId, props); return this.destroy(); } // Create or Update phase - return await ensureCustomDomain(this, api, logicalId, props); + return await ensureCustomDomain(this, api, logicalId, { + ...props, + zoneId: props.zoneId ?? (await inferZoneIdFromPattern(api, props.name)), + }); }, ); @@ -134,19 +146,10 @@ async function deleteCustomDomain( return; // Exit early if no ID } - logger.log( - `Deleting CustomDomain binding ${domainIdToDelete} for ${domainHostname}`, - ); const response = await api.delete( `/accounts/${api.accountId}/workers/domains/${domainIdToDelete}`, ); - logger.log( - `Delete result for ${domainIdToDelete} (${domainHostname}):`, - response.status, - response.statusText, - ); - // 404 is acceptable during deletion for idempotency if (!response.ok && response.status !== 404) { await handleApiError( @@ -167,13 +170,14 @@ async function ensureCustomDomain( context: Context, api: CloudflareApi, _logicalId: string, - props: CustomDomainProps, + props: CustomDomainProps & { + zoneId: string; + }, ): Promise { const environment = props.environment || "production"; const domainHostname = props.name; // Check if domain binding already exists for this account - logger.log(`Checking existing domain bindings for account ${api.accountId}`); const listResponse = await api.get( `/accounts/${api.accountId}/workers/domains`, ); @@ -211,12 +215,15 @@ async function ensureCustomDomain( let currentDomainId = existingBinding?.id; const bindingExists = !!existingBinding; - logger.log( - `Domain binding status for ${domainHostname} (Zone: ${props.zoneId}):`, - bindingExists - ? `Found (ID: ${currentDomainId}, Worker: ${existingBinding.service}, Env: ${existingBinding.environment})` - : "Not found", - ); + // Handle the case where domain already exists during create phase + if (context.phase === "create" && bindingExists) { + if (!props.adopt) { + throw new Error( + `CustomDomain for ${domainHostname} already exists in zone ${props.zoneId}. ` + + "Set adopt: true to take control of the existing domain binding.", + ); + } + } // Determine if we need to update (binding exists but has different service or environment) const needsUpdate = @@ -231,9 +238,6 @@ async function ensureCustomDomain( // Cloudflare's PUT /accounts/{account_id}/workers/domains acts as an upsert if (!bindingExists || needsUpdate) { operationPerformed = bindingExists ? "update" : "create"; - logger.log( - `${operationPerformed === "update" ? "Updating" : "Creating"} domain binding: ${domainHostname} (Zone: ${props.zoneId}) → ${props.workerName}:${environment}`, - ); const putPayload = { zone_id: props.zoneId, @@ -273,13 +277,6 @@ async function ensureCustomDomain( resultantBinding = putResult.result; currentDomainId = resultantBinding.id; // Update ID from the PUT response - logger.log( - `Successfully ${operationPerformed}d binding, new ID: ${currentDomainId}`, - ); - } else { - logger.log( - `Domain binding already exists and is up to date: ${domainHostname} (ID: ${currentDomainId}) → ${props.workerName}:${environment}`, - ); } // Ensure we have the final binding details diff --git a/alchemy/src/cloudflare/d1-database.ts b/alchemy/src/cloudflare/d1-database.ts index a27020c5a..ab5bb9ca4 100644 --- a/alchemy/src/cloudflare/d1-database.ts +++ b/alchemy/src/cloudflare/d1-database.ts @@ -93,6 +93,16 @@ export interface D1DatabaseProps extends CloudflareApiOptions { * This is analogous to wrangler's `migrations_dir`. */ migrationsDir?: string; + /** + * Whether to emulate the database locally when Alchemy is running in watch mode. + */ + dev?: { + /** + * Whether to run the database remotely instead of locally + * @default false + */ + remote?: boolean; + }; } export function isD1Database( @@ -241,13 +251,9 @@ const D1DatabaseResource = Resource( const databaseName = props.name ?? id; if (this.phase === "delete") { - logger.log("Deleting D1 database:", databaseName); if (props.delete !== false) { - // Delete D1 database - logger.log("Deleting D1 database:", databaseName); await deleteDatabase(api, this.output?.id); } - // Return void (a deleted database has no content) return this.destroy(); } @@ -352,6 +358,7 @@ const D1DatabaseResource = Resource( type: "d1", id: dbData.result.uuid || "", name: databaseName, + dev: props.dev, fileSize: dbData.result.file_size, numTables: dbData.result.num_tables, version: dbData.result.version, diff --git a/alchemy/src/cloudflare/do-state-store/internal.ts b/alchemy/src/cloudflare/do-state-store/internal.ts index 1efc2523d..37a8837b2 100644 --- a/alchemy/src/cloudflare/do-state-store/internal.ts +++ b/alchemy/src/cloudflare/do-state-store/internal.ts @@ -1,9 +1,9 @@ -import path from "node:path"; -import { bundle } from "../../esbuild/index.ts"; +import { logger } from "../../util/logger.ts"; import { withExponentialBackoff } from "../../util/retry.ts"; import { handleApiError } from "../api-error.ts"; import type { CloudflareApi } from "../api.ts"; -import { putWorker } from "../worker.ts"; +import type { WorkerMetadata } from "../worker-metadata.ts"; +import { getWorkerTemplate } from "../worker/get-worker-template.ts"; import type { DOStateStoreAPI } from "./types.ts"; interface DOStateStoreClientOptions { @@ -82,6 +82,30 @@ export class DOStateStoreClient { }); } + async waitUntilReady(): Promise { + // This ensures the token is correct and the worker is ready to use. + let last: Response | undefined; + let delay = 1000; + for (let i = 0; i < 20; i++) { + const res = await this.validate(); + if (res.ok) { + return; + } + if (!last) { + logger.log("Waiting for state store deployment..."); + } + last = res; + // Exponential backoff with jitter + const jitter = Math.random() * 0.1 * delay; + await new Promise((resolve) => setTimeout(resolve, delay + jitter)); + delay *= 1.5; // Increase the delay for next attempt + delay = Math.min(delay, 10000); // Cap at 10 seconds + } + throw new Error( + `Failed to access state store: ${last?.status} ${last?.statusText}`, + ); + } + async fetch(path: string, init: RequestInit = {}): Promise { const url = new URL(path, this.options.url); url.searchParams.set("app", this.options.app); @@ -96,7 +120,7 @@ export class DOStateStoreClient { } } -const TAG = "alchemy-state-store:2025-06-03"; +const TAG = "alchemy-state-store:2025-06-23"; const cache = new Map(); @@ -116,33 +140,50 @@ export async function upsertStateStoreWorker( cache.set(key, TAG); return; } - const script = await bundleWorkerScript(); - await putWorker(api, workerName, script, { - main_module: "worker.js", - compatibility_date: "2025-06-01", - compatibility_flags: ["nodejs_compat"], - bindings: [ - { - name: "DOFS_STATE_STORE", - type: "durable_object_namespace", - class_name: "DOFSStateStore", - }, - { - name: "DOFS_TOKEN", - type: "secret_text", - text: token, - }, - ], - migrations: !found - ? { - new_sqlite_classes: ["DOFSStateStore"], - } - : undefined, - tags: [TAG], - observability: { - enabled: true, - }, - }); + const formData = new FormData(); + const worker = await getWorkerTemplate("do-state-store"); + formData.append(worker.name, worker); + formData.append( + "metadata", + new Blob([ + JSON.stringify({ + main_module: worker.name, + compatibility_date: "2025-06-01", + compatibility_flags: ["nodejs_compat"], + bindings: [ + { + name: "DOFS_STATE_STORE", + type: "durable_object_namespace", + class_name: "DOFSStateStore", + }, + { + name: "DOFS_TOKEN", + type: "secret_text", + text: token, + }, + ], + migrations: !found + ? { + new_sqlite_classes: ["DOFSStateStore"], + } + : undefined, + tags: [TAG], + observability: { + enabled: true, + }, + } satisfies WorkerMetadata), + ]), + ); + + // Put the worker with migration tag v1 + const response = await api.put( + `/accounts/${api.accountId}/workers/scripts/${workerName}`, + formData, + ); + if (!response.ok) { + throw await handleApiError(response, "upload", "worker", workerName); + } + const subdomainRes = await api.post( `/accounts/${api.accountId}/workers/scripts/${workerName}/subdomain`, { enabled: true, preview_enabled: false }, @@ -151,7 +192,7 @@ export async function upsertStateStoreWorker( }, ); if (!subdomainRes.ok) { - await handleApiError( + throw await handleApiError( subdomainRes, "creating worker subdomain", "worker", @@ -191,7 +232,7 @@ export async function getAccountSubdomain(api: CloudflareApi) { const res = await api.get(`/accounts/${api.accountId}/workers/subdomain`); if (!res.ok) { throw new Error( - `Failed to get account subdomain: ${res.status} ${res.statusText}`, + `Failed to get account subdomain: ${res.status} ${res.statusText}: ${await res.text().catch(() => "unknown error")}`, ); } const json: { result: { subdomain: string } } = await res.json(); @@ -199,18 +240,3 @@ export async function getAccountSubdomain(api: CloudflareApi) { cache.set(key, subdomain); return subdomain; } - -async function bundleWorkerScript() { - const result = await bundle({ - entryPoint: path.join(__dirname, "worker.ts"), - bundle: true, - format: "esm", - target: "es2022", - external: ["cloudflare:*", "node:crypto"], - write: false, - }); - if (!result.outputFiles?.[0]) { - throw new Error("Failed to bundle worker.ts"); - } - return result.outputFiles[0].text; -} diff --git a/alchemy/src/cloudflare/do-state-store/store.ts b/alchemy/src/cloudflare/do-state-store/store.ts index ed2c114a9..601034815 100644 --- a/alchemy/src/cloudflare/do-state-store/store.ts +++ b/alchemy/src/cloudflare/do-state-store/store.ts @@ -4,11 +4,8 @@ import type { Scope } from "../../scope.ts"; import type { State, StateStore } from "../../state.ts"; import { deserializeState } from "../../state.ts"; import { createCloudflareApi, type CloudflareApiOptions } from "../api.ts"; -import { - DOStateStoreClient, - getAccountSubdomain, - upsertStateStoreWorker, -} from "./internal.ts"; +import { getAccountSubdomain } from "../worker/subdomain.ts"; +import { DOStateStoreClient, upsertStateStoreWorker } from "./internal.ts"; export interface DOStateStoreOptions extends CloudflareApiOptions { /** @@ -95,33 +92,19 @@ export class DOStateStore implements StateStore { this.options.worker?.force ?? false, ), ]); + if (!subdomain) { + throw new Error( + "Failed to access state store worker because the workers.dev subdomain is not available.", + ); + } const client = new DOStateStoreClient({ app: this.scope.appName ?? "alchemy", stage: this.scope.stage, url: `https://${workerName}.${subdomain}.workers.dev`, token, }); - // This ensures the token is correct and the worker is ready to use. - let last: Response | undefined; - let delay = 1000; - for (let i = 0; i < 20; i++) { - const res = await client.validate(); - if (res.ok) { - return client; - } - if (!last) { - console.log("Waiting for state store deployment..."); - } - last = res; - // Exponential backoff with jitter - const jitter = Math.random() * 0.1 * delay; - await new Promise((resolve) => setTimeout(resolve, delay + jitter)); - delay *= 1.5; // Increase the delay for next attempt - delay = Math.min(delay, 10000); // Cap at 10 seconds - } - throw new Error( - `Failed to access state store: ${last?.status} ${last?.statusText}`, - ); + await client.waitUntilReady(); + return client; } private async getClient() { diff --git a/alchemy/src/cloudflare/durable-object-namespace.ts b/alchemy/src/cloudflare/durable-object-namespace.ts index 951ca686d..d36502e12 100644 --- a/alchemy/src/cloudflare/durable-object-namespace.ts +++ b/alchemy/src/cloudflare/durable-object-namespace.ts @@ -41,9 +41,8 @@ export function isDurableObjectNamespace( * environment: "production" * }); */ -export class DurableObjectNamespace< - T extends Rpc.DurableObjectBranded | undefined = undefined, -> implements DurableObjectNamespaceInput +export class DurableObjectNamespace + implements DurableObjectNamespaceInput { public readonly type = "durable_object_namespace" as const; // alias for bindingName to be consistent with other bindings diff --git a/alchemy/src/cloudflare/images.ts b/alchemy/src/cloudflare/images.ts index 5c03ab23f..343a089cb 100644 --- a/alchemy/src/cloudflare/images.ts +++ b/alchemy/src/cloudflare/images.ts @@ -56,4 +56,18 @@ */ export class Images { public readonly type = "images"; + public readonly dev?: { + remote?: boolean; + }; + constructor(props?: { + dev?: { + /** + * Whether to run the images binding remotely instead of locally + * @default false + */ + remote?: boolean; + }; + }) { + this.dev = props?.dev; + } } diff --git a/alchemy/src/cloudflare/index.ts b/alchemy/src/cloudflare/index.ts index 4c5d3a990..eefc4cbfa 100644 --- a/alchemy/src/cloudflare/index.ts +++ b/alchemy/src/cloudflare/index.ts @@ -13,6 +13,8 @@ export * from "./browser-rendering.ts"; export * from "./bucket.ts"; export * from "./bundle/external.ts"; export * from "./bundle/local-dev-cloudflare-shim.ts"; +export * from "./certificate-pack.ts"; +export * from "./container.ts"; export * from "./custom-domain.ts"; export * from "./d1-clone.ts"; export * from "./d1-database.ts"; diff --git a/alchemy/src/cloudflare/kv-namespace.ts b/alchemy/src/cloudflare/kv-namespace.ts index 64207488e..d5268624a 100644 --- a/alchemy/src/cloudflare/kv-namespace.ts +++ b/alchemy/src/cloudflare/kv-namespace.ts @@ -41,6 +41,17 @@ export interface KVNamespaceProps extends CloudflareApiOptions { * @default true */ delete?: boolean; + + /** + * Whether to emulate the KV namespace locally when Alchemy is running in watch mode. + */ + dev?: { + /** + * Whether to run the KV namespace remotely instead of locally + * @default false + */ + remote?: boolean; + }; } /** @@ -251,6 +262,7 @@ const _KVNamespace = Resource( namespaceId, title: props.title, values: props.values, + dev: props.dev, createdAt: createdAt, modifiedAt: Date.now(), }); diff --git a/alchemy/src/cloudflare/pipeline.ts b/alchemy/src/cloudflare/pipeline.ts index 8b7b4c1a1..f64ec39aa 100644 --- a/alchemy/src/cloudflare/pipeline.ts +++ b/alchemy/src/cloudflare/pipeline.ts @@ -522,6 +522,7 @@ export async function createPipeline( api: CloudflareApi, pipelineName: string, props: PipelineProps, + attempt = 0, ): Promise { // Prepare the create payload const createPayload = preparePipelinePayload(api, pipelineName, props); @@ -532,6 +533,11 @@ export async function createPipeline( ); if (!createResponse.ok) { + if (createResponse.status === 404 && attempt < 3) { + // bucket does not exist, this might be transient, let's retry + await new Promise((resolve) => setTimeout(resolve, 1000 * (1 + attempt))); + return await createPipeline(api, pipelineName, props, attempt + 1); + } return await handleApiError( createResponse, "creating", diff --git a/alchemy/src/cloudflare/queue.ts b/alchemy/src/cloudflare/queue.ts index c1caf5a8e..3bee0dbb5 100644 --- a/alchemy/src/cloudflare/queue.ts +++ b/alchemy/src/cloudflare/queue.ts @@ -72,6 +72,17 @@ export interface QueueProps extends CloudflareApiOptions { * @default false */ adopt?: boolean; + + /** + * Whether to emulate the queue locally when Alchemy is running in watch mode. + */ + dev?: { + /** + * Whether to run the queue remotely instead of locally + * @default false + */ + remote?: boolean; + }; } export function isQueue(eventSource: any): eventSource is Queue { @@ -289,6 +300,7 @@ const QueueResource = Resource("cloudflare::Queue", async function < createdOn: queueData.result.created_on || new Date().toISOString(), modifiedOn: queueData.result.modified_on || new Date().toISOString(), accountId: api.accountId, + dev: props.dev, // phantom properties Body: undefined as T, Batch: undefined! as MessageBatch, diff --git a/alchemy/src/cloudflare/route.ts b/alchemy/src/cloudflare/route.ts index 25cf75ee9..f4d371523 100644 --- a/alchemy/src/cloudflare/route.ts +++ b/alchemy/src/cloudflare/route.ts @@ -153,13 +153,7 @@ export const Route = Resource( // Get or infer zone ID (only needed for create/update phases) let zoneId = props.zoneId; if (!zoneId) { - const inferredZoneId = await inferZoneIdFromPattern(props.pattern, { - accountId: props.accountId, - apiKey: props.apiKey, - apiToken: props.apiToken, - baseUrl: props.baseUrl, - email: props.email, - }); + const inferredZoneId = await inferZoneIdFromPattern(api, props.pattern); if (!inferredZoneId) { throw new Error( @@ -423,17 +417,17 @@ function extractDomainFromPattern(pattern: string): string { * @param apiOptions API options for Cloudflare API calls * @returns Promise resolving to zone ID or null if not found */ -async function inferZoneIdFromPattern( +export async function inferZoneIdFromPattern( + api: CloudflareApi, pattern: string, - apiOptions: Partial, -): Promise { +): Promise { const domain = extractDomainFromPattern(pattern); // Handle wildcard domains by removing the wildcard part const cleanDomain = domain.replace(/^\*\./, ""); // Try to find zone for the exact domain first - let zone = await getZoneByDomain(cleanDomain, apiOptions); + let zone = await getZoneByDomain(api, cleanDomain); if (zone) { return zone.id; } @@ -442,11 +436,14 @@ async function inferZoneIdFromPattern( const domainParts = cleanDomain.split("."); for (let i = 1; i < domainParts.length - 1; i++) { const parentDomain = domainParts.slice(i).join("."); - zone = await getZoneByDomain(parentDomain, apiOptions); + zone = await getZoneByDomain(api, parentDomain); if (zone) { return zone.id; } } - return null; + throw new Error( + `Could not infer zone ID for route pattern "${pattern}". ` + + "Please ensure the domain is managed by Cloudflare or specify an explicit zoneId.", + ); } diff --git a/alchemy/src/cloudflare/vite.ts b/alchemy/src/cloudflare/vite.ts index a8eaa3038..b65fe319e 100644 --- a/alchemy/src/cloudflare/vite.ts +++ b/alchemy/src/cloudflare/vite.ts @@ -1,4 +1,5 @@ import path from "node:path"; +import { detectPackageManager } from "../util/detect-package-manager.ts"; import type { Assets } from "./assets.ts"; import type { Bindings } from "./bindings.ts"; import { Website, type WebsiteProps } from "./website.ts"; @@ -17,6 +18,13 @@ export async function Vite( props: ViteProps, ): Promise> { const defaultAssets = path.join("dist", "client"); + const packageManager = detectPackageManager(); + const devCommand = { + npm: "npx vite dev", + bun: "bun vite dev", + pnpm: "pnpm vite dev", + yarn: "yarn vite dev", + }[packageManager]; return Website(id, { ...props, spa: true, @@ -27,5 +35,9 @@ export async function Vite( dist: props.assets.dist ?? defaultAssets, } : (props.assets ?? defaultAssets), + dev: props.dev ?? { + command: devCommand, + url: "http://localhost:5173", + }, }); } diff --git a/alchemy/src/cloudflare/website.ts b/alchemy/src/cloudflare/website.ts index d597183a3..359035cd4 100644 --- a/alchemy/src/cloudflare/website.ts +++ b/alchemy/src/cloudflare/website.ts @@ -94,6 +94,14 @@ export interface WebsiteProps * @default false */ spa?: boolean; + + /** + * Configure the command to use in development mode + */ + dev?: { + command: string; + url: string; + }; } export type Website = B extends { ASSETS: any } @@ -114,34 +122,53 @@ export async function Website( { parent: Scope.current, }, - async () => { + async (scope) => { + // directory from which all relative paths are resolved const cwd = path.resolve(props.cwd || process.cwd()); - const fileName = + + function resolveAbsPath(f: S): S { + return ( + f ? (path.isAbsolute(f) ? f : path.resolve(cwd, f)) : undefined + ) as S; + } + + // absolute path to the wrangler.jsonc file + const wranglerJsonPath = resolveAbsPath( typeof wrangler === "boolean" ? "wrangler.jsonc" : typeof wrangler === "string" ? wrangler - : (wrangler?.path ?? "wrangler.jsonc"); - const wranglerPath = - fileName && path.relative(cwd, path.join(cwd, fileName)); - const wranglerMain = - typeof wrangler === "object" - ? (wrangler.main ?? props.main) - : props.main; + : (wrangler?.path ?? "wrangler.jsonc"), + ); - const workerName = props.name ?? id; + // absolute path to the worker entrypoint + const mainPath = resolveAbsPath(props.main); + + // absolute path to the worker entrypoint (if different in wrangler.jsonc) + const wranglerMainPath = resolveAbsPath( + typeof wrangler === "object" + ? (wrangler.main ?? props.main!) + : props.main, + ); - const assetDir = + // absolute path to the assets directory + const assetsDirPath = resolveAbsPath( typeof props.assets === "string" ? props.assets - : (props.assets?.dist ?? "dist"); + : (props.assets?.dist ?? "dist"), + ); + + const workerName = props.name ?? id; const workerProps = { ...props, compatibilityDate: props.compatibilityDate ?? DEFAULT_COMPATIBILITY_DATE, name: workerName, - entrypoint: props.main, + entrypoint: mainPath + ? // this path should be relative to the process.cwd() because it is used by esbuild + path.relative(process.cwd(), mainPath) + : undefined, assets: { html_handling: "auto-trailing-slash", not_found_handling: props.spa ? "single-page-application" : "none", @@ -158,22 +185,35 @@ export default { };`, url: true, adopt: true, + dev: props.dev + ? { + command: props.dev.command, + url: props.dev.url, + cwd, + } + : undefined, } as WorkerProps & { name: string }; if (wrangler) { + // paths in wrangler.jsonc must be relative to it + const relativeToWrangler = (f: S): S => + (f ? path.relative(path.dirname(wranglerJsonPath), f) : f) as S; await WranglerJson("wrangler.jsonc", { - path: wranglerPath, + path: wranglerJsonPath, worker: workerProps, - main: wranglerMain, + main: relativeToWrangler(wranglerMainPath), // hard-code the assets directory because we haven't yet included the assets binding assets: { binding: "ASSETS", - directory: assetDir, + // path must be relative to the wrangler.jsonc file + directory: relativeToWrangler(assetsDirPath), }, }); } - if (props.command) { + const isDev = scope.dev && !!props.dev; + + if (props.command && !isDev) { await Exec("build", { cwd, command: props.command, @@ -188,9 +228,12 @@ export default { ...workerProps.bindings, // we don't include the Assets binding until after build to make sure the asset manifest is correct // we generate the wrangler.json using all the bind - ASSETS: await Assets("assets", { - path: assetDir, - }), + ASSETS: isDev + ? undefined + : await Assets("assets", { + // Assets are discovered from proces.cwd(), not Website.cwd or wrangler.jsonc + path: path.relative(process.cwd(), assetsDirPath), + }), }, } as WorkerProps & { name: string })) as Website; }, diff --git a/alchemy/src/cloudflare/worker-metadata.ts b/alchemy/src/cloudflare/worker-metadata.ts index 1528830df..d90126bf6 100644 --- a/alchemy/src/cloudflare/worker-metadata.ts +++ b/alchemy/src/cloudflare/worker-metadata.ts @@ -1,20 +1,24 @@ import path from "node:path"; -import type { Context } from "../context.ts"; +import { assertNever } from "../util/assert-never.ts"; import { logger } from "../util/logger.ts"; -import { slugify } from "../util/slugify.ts"; +import type { CloudflareApi } from "./api.ts"; import { Self, type Bindings, type WorkerBindingDurableObjectNamespace, type WorkerBindingSpec, } from "./bindings.ts"; +import { isContainer, type Container } from "./container.ts"; import { isDurableObjectNamespace, type DurableObjectNamespace, } from "./durable-object-namespace.ts"; import { createAssetConfig, type AssetUploadResult } from "./worker-assets.ts"; -import type { SingleStepMigration } from "./worker-migration.ts"; -import type { AssetsConfig, Worker, WorkerProps } from "./worker.ts"; +import type { + MultiStepMigration, + SingleStepMigration, +} from "./worker-migration.ts"; +import type { AssetsConfig, WorkerProps } from "./worker.ts"; /** * Metadata returned by Cloudflare API for a worker script @@ -191,19 +195,27 @@ export interface WorkerMetadata { cron: string; suspended: boolean; }[]; + containers?: { class_name: string }[]; } -export async function prepareWorkerMetadata( - ctx: Context>, - oldBindings: WorkerBindingSpec[] | undefined, - oldTags: string[] | undefined, +export async function prepareWorkerMetadata( + api: CloudflareApi, props: WorkerProps & { compatibilityDate: string; compatibilityFlags: string[]; workerName: string; + migrationTag?: string; + assetUploadResult?: AssetUploadResult; }, - assetUploadResult?: AssetUploadResult, ): Promise { + const oldSettings = await getWorkerSettings(api, props.workerName); + const oldTags: string[] | undefined = Array.from( + new Set([ + ...(oldSettings?.default_environment?.script?.tags ?? []), + ...(oldSettings?.tags ?? []), + ]), + ); + const oldBindings = oldSettings?.bindings; // we use Cloudflare Worker tags to store a mapping between Alchemy's stable identifier and the binding name // e.g. // { @@ -212,7 +224,7 @@ export async function prepareWorkerMetadata( // will be stored as alchemy:do:stable-id:BINDING_NAME // TODO(sam): should we base64 encode to ensure no `:` collision risk? const bindingNameToStableId = Object.fromEntries( - oldTags?.flatMap((tag) => { + oldTags?.flatMap((tag: string) => { // alchemy:do:{stableId}:{bindingName} if (tag.startsWith("alchemy:do:")) { const [, , stableId, bindingName] = tag.split(":"); @@ -222,6 +234,14 @@ export async function prepareWorkerMetadata( }) ?? [], ); + const oldMigrationTag = oldTags?.flatMap((tag: string) => { + if (tag.startsWith("alchemy:migration-tag:")) { + return [tag.slice("alchemy:migration-tag:".length)]; + } + return []; + })[0]; + const newMigrationTag = bumpMigrationTagVersion(oldMigrationTag); + const deletedClasses = oldBindings?.flatMap((oldBinding) => { if ( oldBinding.type === "durable_object_namespace" && @@ -240,8 +260,9 @@ export async function prepareWorkerMetadata( // try and find the DO binding by stable id const object = Object.values(props.bindings).find( - (binding): binding is DurableObjectNamespace => - isDurableObjectNamespace(binding) && binding.id === stableId, + (binding): binding is DurableObjectNamespace | Container => + (isDurableObjectNamespace(binding) || isContainer(binding)) && + binding.id === stableId, ); if (object) { // we found the corresponding object, it should not be deleted @@ -284,29 +305,30 @@ export async function prepareWorkerMetadata( }, // TODO(sam): base64 encode instead? 0 collision risk vs readability. tags: [ - `alchemy:id:${slugify(ctx.fqn)}`, // encode a mapping table of Durable Object stable ID -> binding name // we use this to reliably compute class migrations based on server-side state ...Object.entries(props.bindings ?? {}).flatMap( ([bindingName, binding]) => - isDurableObjectNamespace(binding) + isDurableObjectNamespace(binding) || isContainer(binding) ? // TODO(sam): base64 encode if contains `:`? [`alchemy:do:${binding.id}:${bindingName}`] : [], ), + // encode the migraiton tag if there is one so we can avoid the failed PutWorker after adoption + ...(newMigrationTag ? [`alchemy:migration-tag:${newMigrationTag}`] : []), ], migrations: { - new_classes: props.migrations?.new_classes ?? [], - deleted_classes: [ - ...(deletedClasses ?? []), - ...(props.migrations?.deleted_classes ?? []), - ], - renamed_classes: props.migrations?.renamed_classes ?? [], - transferred_classes: props.migrations?.transferred_classes ?? [], - new_sqlite_classes: props.migrations?.new_sqlite_classes ?? [], + old_tag: oldMigrationTag, + new_tag: newMigrationTag, + new_classes: [], + deleted_classes: [...(deletedClasses ?? [])], + renamed_classes: [], + transferred_classes: [], + new_sqlite_classes: [], }, }; + const assetUploadResult = props.assetUploadResult; // If we have asset upload results, add them to the metadata if (assetUploadResult) { meta.assets = { @@ -369,7 +391,10 @@ export async function prepareWorkerMetadata( type: "durable_object_namespace", name: bindingName, class_name: binding.className, - script_name: binding.scriptName, + script_name: + binding.scriptName === props.workerName + ? undefined + : binding.scriptName, environment: binding.environment, namespace_id: binding.namespaceId, }); @@ -494,15 +519,35 @@ export async function prepareWorkerMetadata( key_base64: binding.key_base64?.unencrypted, key_jwk: binding.key_jwk?.unencrypted, }); + } else if (binding.type === "container") { + meta.bindings.push({ + type: "durable_object_namespace", + class_name: binding.className, + name: bindingName, + }); + if ( + binding.scriptName === undefined || + // TODO(sam): not sure if Cloudflare Api would like us using `this` worker name here + binding.scriptName === props.workerName + ) { + // we do not need configure class migrations for cross-script bindings + configureClassMigration(bindingName, binding); + } + (meta.containers ??= []).push({ class_name: binding.className }); } else { - // @ts-expect-error - we should never reach here - throw new Error(`Unsupported binding type: ${binding.type}`); + assertNever( + binding, + `Unsupported binding type: ${ + // @ts-expect-error - types think it's never + binding.type + }`, + ); } } function configureClassMigration( bindingName: string, - newBinding: DurableObjectNamespace, + newBinding: DurableObjectNamespace | Container, ) { let prevBinding: WorkerBindingDurableObjectNamespace | undefined; if (oldBindings) { @@ -577,3 +622,70 @@ export async function prepareWorkerMetadata( } return meta; } + +export function bumpMigrationTagVersion(tag?: string) { + if (tag) { + if (!tag.match(/^v\d+$/)) { + throw new Error(`Invalid tag format: ${tag}. Expected format: v`); + } + return `v${Number.parseInt(tag.slice(1)) + 1}`; + } + return undefined; +} + +interface WorkerSettings { + bindings: WorkerBindingSpec[]; + compatibility_date: string; + compatibility_flags: string[]; + migrations: SingleStepMigration | MultiStepMigration; + [key: string]: any; +} + +async function getWorkerSettings( + api: CloudflareApi, + workerName: string, +): Promise { + // Fetch the bindings for a worker by calling the Cloudflare API endpoint: + // GET /accounts/:account_id/workers/scripts/:script_name/bindings + // See: https://developers.cloudflare.com/api/resources/workers/subresources/scripts/subresources/script_and_version_settings/methods/get/ + const response = await api.get( + `/accounts/${api.accountId}/workers/scripts/${workerName}/settings`, + ); + if (response.status === 404) { + return undefined; + } + if (!response.ok) { + throw new Error( + `Error getting worker bindings: ${response.status} ${response.statusText}`, + ); + } + // The result is an object with a "result" property containing the bindings array + const { result, success, errors } = (await response.json()) as { + result: { + bindings: WorkerBindingSpec[]; + compatibility_date: string; + compatibility_flags: string[]; + migrations: SingleStepMigration | MultiStepMigration; + [key: string]: any; + }; + success: boolean; + errors: Array<{ + code: number; + message: string; + documentation_url: string; + [key: string]: any; + }>; + messages: Array<{ + code: number; + message: string; + documentation_url: string; + [key: string]: any; + }>; + }; + if (!success) { + throw new Error( + `Error getting worker bindings: ${response.status} ${response.statusText}\nErrors:\n${errors.map((e) => `- [${e.code}] ${e.message} (${e.documentation_url})`).join("\n")}`, + ); + } + return result; +} diff --git a/alchemy/src/cloudflare/worker-stub.ts b/alchemy/src/cloudflare/worker-stub.ts index 81687d7c8..6aaa3c55e 100644 --- a/alchemy/src/cloudflare/worker-stub.ts +++ b/alchemy/src/cloudflare/worker-stub.ts @@ -7,6 +7,7 @@ import { type CloudflareApi, type CloudflareApiOptions, } from "./api.ts"; +import { configureURL } from "./worker.ts"; /** * Properties for creating a Worker stub @@ -19,6 +20,14 @@ export interface WorkerStubProps< */ name: string; + /** + * Whether to enable a workers.dev URL for this worker + * + * If true, the worker will be available at {name}.{subdomain}.workers.dev + * @default true + */ + url?: boolean; + /** * The RPC class to use for the worker. * @@ -39,6 +48,12 @@ export interface WorkerStub< */ name: string; + /** + * The worker's URL if enabled + * Format: {name}.{subdomain}.workers.dev + */ + url?: string; + /** * Optional type branding for the worker's RPC entrypoint. * @@ -59,12 +74,21 @@ export function isWorkerStub(resource: Resource): resource is WorkerStub { * exists and creates an empty one if needed. * * @example - * // Reserve a worker name without deploying code + * // Reserve a worker name without deploying code, with URL enabled (default) * const workerStub = await WorkerStub("my-worker", { * name: "my-reserved-worker" * }); * - * console.log(`Worker ${workerStub.name} exists: ${!workerStub.created}`); + * console.log(`Worker ${workerStub.name} is available at: ${workerStub.url}`); + * + * @example + * // Reserve a worker name without enabling URL + * const workerStub = await WorkerStub("my-worker", { + * name: "my-reserved-worker", + * url: false + * }); + * + * console.log(`Worker ${workerStub.name} created without URL`); */ export const WorkerStub = Resource("cloudflare::WorkerStub", async function < RPC extends Rpc.WorkerEntrypointBranded = Rpc.WorkerEntrypointBranded, @@ -84,11 +108,16 @@ export const WorkerStub = Resource("cloudflare::WorkerStub", async function < await createEmptyWorker(api, props.name); } + // Configure URL if requested (defaults to true) + const enableUrl = props.url ?? true; + const workerUrl = await configureURL(this, api, props.name, enableUrl); + // Return the worker stub info return this({ type: "service", __rpc__: props.rpc as unknown as RPC, ...props, + url: workerUrl, }) as WorkerStub; }); diff --git a/alchemy/src/cloudflare/worker.ts b/alchemy/src/cloudflare/worker.ts index 6c3f60b26..3bc56a87f 100644 --- a/alchemy/src/cloudflare/worker.ts +++ b/alchemy/src/cloudflare/worker.ts @@ -1,4 +1,13 @@ +import { spawn } from "node:child_process"; +import { + existsSync, + mkdirSync, + readFileSync, + unlinkSync, + writeFileSync, +} from "node:fs"; import path from "node:path"; +import { BUILD_DATE } from "../build-date.ts"; import type { Context } from "../context.ts"; import type { BundleProps } from "../esbuild/bundle.ts"; import { InnerResourceScope, Resource, ResourceKind } from "../resource.ts"; @@ -10,9 +19,9 @@ import { Secret, secret } from "../secret.ts"; import { serializeScope } from "../serde.ts"; import type { type } from "../type.ts"; import { getContentType } from "../util/content-type.ts"; +import { DeferredPromise } from "../util/deferred-promise.ts"; import { logger } from "../util/logger.ts"; import { withExponentialBackoff } from "../util/retry.ts"; -import { slugify } from "../util/slugify.ts"; import { CloudflareApiError, handleApiError } from "./api-error.ts"; import { type CloudflareApi, @@ -24,15 +33,19 @@ import { type Binding, type Bindings, Json, + type WorkerBindingDurableObjectNamespace, type WorkerBindingService, type WorkerBindingSpec, } from "./bindings.ts"; import type { Bound } from "./bound.ts"; import { isBucket } from "./bucket.ts"; +import { createWorkerDevContext } from "./bundle/bundle-worker-dev.ts"; import { type NoBundleResult, bundleWorkerScript, } from "./bundle/bundle-worker.ts"; +import { type Container, ContainerApplication } from "./container.ts"; +import { CustomDomain } from "./custom-domain.ts"; import { isD1Database } from "./d1-database.ts"; import type { DispatchNamespaceResource } from "./dispatch-namespace.ts"; import { @@ -56,12 +69,14 @@ import { Route } from "./route.ts"; import { isVectorizeIndex } from "./vectorize-index.ts"; import { type AssetUploadResult, uploadAssets } from "./worker-assets.ts"; import { - type WorkerMetadata, type WorkerScriptMetadata, + bumpMigrationTagVersion, prepareWorkerMetadata, } from "./worker-metadata.ts"; -import type { SingleStepMigration } from "./worker-migration.ts"; import { WorkerStub, isWorkerStub } from "./worker-stub.ts"; +import type { MiniflareWorkerOptions } from "./worker/miniflare-worker-options.ts"; +import { miniflareServer } from "./worker/miniflare.ts"; +import { getAccountSubdomain } from "./worker/subdomain.ts"; import { Workflow, isWorkflow, upsertWorkflow } from "./workflow.ts"; /** @@ -98,11 +113,12 @@ export interface AssetsConfig { /** * When true, requests will always invoke the Worker script. + * If an array is passed, the worker will be invoked for matching requests. * Otherwise, attempt to serve an asset matching the request, falling back to the Worker script. * * @default false */ - run_worker_first?: boolean; + run_worker_first?: boolean | string[]; } export interface BaseWorkerProps< @@ -172,11 +188,6 @@ export interface BaseWorkerProps< enabled?: boolean; }; - /** - * Migrations to apply to the worker - */ - migrations?: SingleStepMigration; - /** * Whether to adopt the Worker if it already exists when creating */ @@ -184,7 +195,7 @@ export interface BaseWorkerProps< /** * The compatibility date for the worker - * @default "2025-04-26" + * @default BUILD_DATE - automatically pinned to the package build date */ compatibilityDate?: string; @@ -220,27 +231,66 @@ export interface BaseWorkerProps< * Routes to create for this worker. * * Each route maps a URL pattern to this worker script. + * + * @example + * await Worker("my-worker", { + * routes: [ + * "sub.example.com/*", + * { pattern: "sub.example.com/*", zoneId: "1234567890" }, + * ], + * }); */ - routes?: Array<{ - /** - * URL pattern for the route - * @example "sub.example.com/*" - */ - pattern: string; - /** - * Zone ID for the route. If not provided, will be automatically inferred from the route pattern. - */ - zoneId?: string; - /** - * Whether this is a custom domain route - */ - customDomain?: boolean; - /** - * Whether to adopt an existing route with the same pattern if it exists - * @default false - */ - adopt?: boolean; - }>; + routes?: Array< + | string + | { + /** + * URL pattern for the route + * @example "sub.example.com/*" + */ + pattern: string; + /** + * Zone ID for the route. If not provided, will be automatically inferred from the route pattern. + */ + zoneId?: string; + /** + * Whether to adopt an existing route with the same pattern if it exists + * @default false + */ + adopt?: boolean; + } + >; + + /** + * Custom domains to bind to the worker + * + * @example + * await Worker("my-worker", { + * domains: [ + * "example.com", + * { name: "example.com", zoneId: "1234567890" }, + * ], + * }); + */ + domains?: ( + | string + | { + /** + * The domain name to bind to the worker + */ + domainName: string; + /** + * Zone ID for the domain. + * + * @default - If not provided, will be automatically inferred from the domain name. + */ + zoneId?: string; + /** + * Whether to adopt an existing domain if it exists + * @default false + */ + adopt?: boolean; + } + )[]; /** * The RPC class to use for the worker. @@ -266,6 +316,32 @@ export interface BaseWorkerProps< * @example "pr-123" */ version?: string; + + /** + * Configuration for local development. By default, when Alchemy is running in development mode, + * the worker will be emulated locally and available at a randomly selected port. + */ + dev?: + | boolean + | { + /** + * Port to use for local development + */ + port?: number; + /** + * EXPERIMENTAL: Whether to run the worker remotely instead of locally. + * + * When this is enabled, hot reloading will not work. + * + * @default false + */ + remote?: boolean; + } + | { + command: string; + url: string; + cwd?: string; + }; } export interface InlineWorkerProps< @@ -373,7 +449,7 @@ export type Worker< B extends Bindings | undefined = Bindings | undefined, RPC extends Rpc.WorkerEntrypointBranded = Rpc.WorkerEntrypointBranded, > = Resource<"cloudflare::Worker"> & - Omit, "url" | "script" | "routes"> & + Omit, "url" | "script" | "routes" | "domains"> & globalThis.Service & { /** @internal phantom property */ __rpc__?: RPC; @@ -423,6 +499,11 @@ export type Worker< */ routes?: Route[]; + /** + * The custom domains that were created for this worker + */ + domains?: CustomDomain[]; + // phantom property (for typeof myWorker.Env) Env: B extends Bindings ? { @@ -867,7 +948,7 @@ export function Worker( }); } -export const DEFAULT_COMPATIBILITY_DATE = "2025-04-20"; +export const DEFAULT_COMPATIBILITY_DATE = BUILD_DATE; export const _Worker = Resource( "cloudflare::Worker", @@ -883,9 +964,6 @@ export const _Worker = Resource( throw new Error("entrypoint must be provided when noBundle is true"); } - // Create Cloudflare API client with automatic account discovery - const api = await createCloudflareApi(props); - // Use the provided name const workerName = props.name ?? id; @@ -893,13 +971,201 @@ export const _Worker = Resource( props.compatibilityDate ?? DEFAULT_COMPATIBILITY_DATE; const compatibilityFlags = props.compatibilityFlags ?? []; - const uploadWorkerScript = async (props: WorkerProps) => { - const [oldBindings, oldMetadata] = await Promise.all([ - getWorkerBindings(api, workerName), - getWorkerScriptMetadata(api, workerName), - ]); - const oldTags = oldMetadata?.default_environment?.script?.tags; + if (this.scope.dev && this.phase !== "delete" && props.dev !== false) { + // Get current timestamp + const now = Date.now(); + if (typeof props.dev === "object" && "command" in props.dev) { + upsertDevCommand({ + id, + command: props.dev.command, + cwd: props.dev.cwd ?? process.cwd(), + env: props.env ?? {}, + }); + return this({ + type: "service", + id, + entrypoint: props.entrypoint, + name: workerName, + compatibilityDate, + compatibilityFlags, + format: props.format || "esm", // Include format in the output + bindings: props.bindings ?? ({} as B), + env: props.env, + observability: props.observability, + createdAt: now, + updatedAt: now, + eventSources: props.eventSources, + url: props.dev.url, + dev: props.dev, + // Include assets configuration in the output + assets: props.assets, + // Include cron triggers in the output + crons: props.crons, + // phantom property + Env: undefined!, + } as unknown as Worker); + } + + const sharedOptions: Omit = { + name: workerName, + compatibilityDate, + compatibilityFlags, + bindings: props.bindings ?? ({} as B), + port: typeof props.dev === "object" ? props.dev.port : undefined, + }; + + let url: string; + + // If entrypoint is provided, set up hot reloading with esbuild context + if (props.entrypoint) { + const startPromise = new DeferredPromise(); + + await createWorkerDevContext( + workerName, + { + ...props, + entrypoint: props.entrypoint, + compatibilityDate, + compatibilityFlags, + }, + { + onBuildStart: () => { + logger.task("miniflare-server", { + message: `${startPromise.status === "pending" ? "Building" : "Rebuilding"}`, + status: "pending", + resource: id, + prefix: "build", + prefixColor: "cyanBright", + }); + }, + onBuildEnd: async (newScript) => { + try { + // Hot reload callback - update the miniflare worker + const server = await miniflareServer.push({ + ...sharedOptions, + script: newScript, + }); + if (startPromise.status === "pending") { + logger.task("miniflare-server", { + message: `Started Miniflare server at ${server.url}`, + status: "success", + resource: id, + prefix: "ready", + prefixColor: "cyanBright", + }); + startPromise.resolve(server.url); + } else { + logger.task("miniflare-server", { + message: `Updated Miniflare server at ${server.url}`, + status: "success", + resource: id, + prefix: "reload", + prefixColor: "cyanBright", + }); + } + } catch (error) { + if (startPromise.status === "pending") { + logger.error(error); + logger.task("miniflare-server", { + message: "Failed to start Miniflare server", + status: "failure", + resource: id, + prefix: "error", + prefixColor: "redBright", + }); + startPromise.reject( + new Error( + `Failed to start Miniflare server for worker "${workerName}"`, + { cause: error }, + ), + ); + } else { + logger.task("miniflare-server", { + message: "Failed to update Miniflare server", + status: "failure", + resource: id, + prefix: "error", + prefixColor: "redBright", + }); + logger.error(error); + } + } + }, + onBuildError: (errors) => { + if (startPromise.status === "pending") { + logger.task("miniflare-server", { + message: "Failed to build worker", + status: "failure", + resource: id, + prefix: "error", + prefixColor: "redBright", + }); + startPromise.reject(errors); + } else { + logger.task("miniflare-server", { + message: "Failed to rebuild worker", + status: "failure", + resource: id, + prefix: "error", + prefixColor: "redBright", + }); + logger.error(errors); + } + }, + }, + ); + + url = await startPromise.value; + } else { + // Fallback to one-time bundling for inline scripts + const scriptContent = + props.script ?? + (await bundleWorkerScript({ + name: workerName, + ...props, + compatibilityDate, + compatibilityFlags, + })); + const server = await miniflareServer.push({ + ...sharedOptions, + script: + typeof scriptContent === "string" + ? scriptContent + : scriptContent[props.entrypoint!].toString(), + }); + url = server.url; + } + + return this({ + type: "service", + id, + entrypoint: props.entrypoint, + name: workerName, + compatibilityDate, + compatibilityFlags, + format: props.format || "esm", // Include format in the output + bindings: props.bindings ?? ({} as B), + env: props.env, + observability: props.observability, + createdAt: now, + updatedAt: now, + eventSources: props.eventSources, + url, + dev: props.dev, + // Include assets configuration in the output + assets: props.assets, + // Include cron triggers in the output + crons: props.crons, + // phantom property + Env: undefined!, + } as unknown as Worker); + } + + // Create Cloudflare API client with automatic account discovery + const api = await createCloudflareApi(props); + + const uploadWorkerScript = async (props: WorkerProps) => { // Get the script content - either from props.script, or by bundling const scriptBundle = props.script ?? @@ -913,6 +1179,7 @@ export const _Worker = Resource( // Find any assets bindings const assetsBindings: { name: string; assets: Assets }[] = []; const workflowsBindings: Workflow[] = []; + const containersBindings: Container[] = []; if (props.bindings) { for (const [bindingName, binding] of Object.entries(props.bindings)) { @@ -921,6 +1188,8 @@ export const _Worker = Resource( assetsBindings.push({ name: bindingName, assets: binding }); } else if (binding.type === "workflow") { workflowsBindings.push(binding); + } else if (binding.type === "container") { + containersBindings.push(binding); } } } @@ -949,34 +1218,18 @@ export const _Worker = Resource( }); } - // Prepare metadata with bindings - const scriptMetadata = await prepareWorkerMetadata( - this, - oldBindings, - oldTags, - { - ...props, - compatibilityDate, - compatibilityFlags, - workerName, - }, - assetUploadResult, - ); - // Deploy worker (either as version or live worker) - const versionResult = await putWorker( - api, + const versionResult = await putWorker(api, { + ...props, workerName, scriptBundle, - scriptMetadata, dispatchNamespace, - props.version - ? { - versionLabel: props.version, - message: `Version ${props.version}`, - } - : undefined, - ); + version: props.version, + compatibilityDate, + compatibilityFlags, + assetUploadResult, + }); + versionResult.versionId; for (const workflow of workflowsBindings) { if ( @@ -991,6 +1244,47 @@ export const _Worker = Resource( } } + for (const container of containersBindings) { + async function findNamespaceId() { + const response = await api.get( + `/accounts/${api.accountId}/workers/scripts/${workerName}/versions/${versionResult.deploymentId}`, + ); + const result = (await response.json()) as { + result: { + resources: { + bindings: WorkerBindingSpec[]; + }; + }; + }; + const namespaceId = result.result.resources.bindings.find( + (binding): binding is WorkerBindingDurableObjectNamespace => + binding.type === "durable_object_namespace" && + binding.class_name === container.className, + )?.namespace_id; + if (!namespaceId) { + throw new Error( + `Namespace ID not found for container ${container.id}`, + ); + } + return namespaceId; + } + await ContainerApplication(container.id, { + accountId: props.accountId, + apiKey: props.apiKey, + apiToken: props.apiToken, + baseUrl: props.baseUrl, + email: props.email, + image: container.image, + name: container.id, + instanceType: container.instanceType, + observability: container.observability, + durableObjects: { + namespaceId: await findNamespaceId(), + }, + schedulingPolicy: container.schedulingPolicy, + }); + } + await Promise.all( props.eventSources?.map((eventSource) => { if (isQueue(eventSource) || isQueueEventSource(eventSource)) { @@ -1018,7 +1312,9 @@ export const _Worker = Resource( if (props.version) { // For versions, use the preview URL if available workerUrl = versionResult?.previewUrl; - } else { + } else if (!props.namespace) { + // namespaces don't support URLs + // For regular workers, use the normal URL configuration workerUrl = await configureURL( this, @@ -1048,35 +1344,22 @@ export const _Worker = Resource( } } - return { scriptBundle, scriptMetadata, workerUrl, now, versionResult }; + return { scriptBundle, workerUrl, now, versionResult }; }; if (this.phase === "delete") { // Delete any queue consumers attached to this worker first await deleteQueueConsumers(api, workerName); - // @ts-ignore - await uploadWorkerScript({ - ...props, - entrypoint: undefined, - noBundle: false, - script: - props.format === "cjs" - ? "addEventListener('fetch', event => { event.respondWith(new Response('hello world')); });" - : "export default { fetch(request) { return new Response('hello world'); }, queue: () => {} }", - bindings: {} as B, - // we are writing a stub worker (to remove binding/event source dependencies) - // queue consumers will no longer exist by this point - eventSources: undefined, - // stub worker doesn't need dispatch namespace - namespace: undefined, - }); - await withExponentialBackoff( () => - deleteWorker(this, api, { - ...props, + deleteWorker(api, { workerName, + namespace: + typeof props.namespace === "string" + ? props.namespace + : props.namespace?.namespaceId, + url: this.output.url, }), (err) => (err.status === 400 && @@ -1091,10 +1374,6 @@ export const _Worker = Resource( return this.destroy(); } - // Validate input - we need either script, entryPoint, or bundle - if (!props.script && !props.entrypoint) { - throw new Error("One of script or entrypoint must be provided"); - } if (this.phase === "create") { if (props.version) { @@ -1107,34 +1386,76 @@ export const _Worker = Resource( } // We always "adopt" when publishing versions } else if (!props.adopt) { - await assertWorkerDoesNotExist(this, api, workerName); + await assertWorkerDoesNotExist(api, workerName); } } - const { scriptMetadata, workerUrl, now } = await uploadWorkerScript(props); + const { workerUrl, now } = await uploadWorkerScript(props); - // Create routes if provided and capture their outputs - let createdRoutes: Route[] = []; - if (props.routes && props.routes.length > 0) { - // Validate for duplicate patterns - const patterns = props.routes.map((route) => route.pattern); - const duplicates = patterns.filter( - (pattern, index) => patterns.indexOf(pattern) !== index, + function ensureNoDuplicates(name: string, items: string[]) { + const duplicates = items.filter( + (pattern, index) => items.indexOf(pattern) !== index, ); if (duplicates.length > 0) { - throw new Error( - `Duplicate route patterns found: ${duplicates.join(", ")}`, - ); + throw new Error(`Duplicate ${name} found: ${duplicates.join(", ")}`); } + } + + let createdDomains: CustomDomain[] | undefined; + if (props.domains?.length) { + ensureNoDuplicates( + "Custom Domain", + props.domains.map((domain) => + typeof domain === "string" ? domain : domain.domainName, + ), + ); + + createdDomains = await Promise.all( + props.domains.map(async (customDomain) => { + const domainName = + typeof customDomain === "string" + ? customDomain + : customDomain.domainName; + return await CustomDomain(domainName, { + workerName, + name: domainName, + zoneId: + typeof customDomain === "string" + ? undefined + : customDomain.zoneId, + adopt: + typeof customDomain === "string" + ? false + : (customDomain.adopt ?? props.adopt), + }); + }), + ); + } + + // Create routes if provided and capture their outputs + let createdRoutes: Route[] | undefined; + if (props.routes && props.routes.length > 0) { + ensureNoDuplicates( + "Route", + props.routes.map((route) => + typeof route === "string" ? route : route.pattern, + ), + ); // Create Route resources for each route and capture their outputs createdRoutes = await Promise.all( props.routes.map(async (routeConfig) => { - return await Route(routeConfig.pattern, { - pattern: routeConfig.pattern, + const pattern = + typeof routeConfig === "string" ? routeConfig : routeConfig.pattern; + return await Route(pattern, { + pattern, script: workerName, - zoneId: routeConfig.zoneId, // Route resource will handle inference if not provided - adopt: routeConfig.adopt ?? false, + zoneId: + typeof routeConfig === "string" ? undefined : routeConfig.zoneId, // Route resource will handle inference if not provided + adopt: + typeof routeConfig === "string" + ? false + : (routeConfig.adopt ?? props.adopt), accountId: props.accountId, apiKey: props.apiKey, apiToken: props.apiToken, @@ -1181,17 +1502,20 @@ export const _Worker = Resource( format: props.format || "esm", // Include format in the output bindings: exportBindings(), env: props.env, - observability: scriptMetadata.observability, + observability: props.observability, createdAt: now, updatedAt: now, eventSources: props.eventSources, url: workerUrl, + dev: props.dev, // Include assets configuration in the output assets: props.assets, // Include cron triggers in the output crons: props.crons, // Include the created routes in the output - routes: createdRoutes.length > 0 ? createdRoutes : undefined, + routes: createdRoutes, + // Include the created domains in the output + domains: createdDomains, // Include the dispatch namespace in the output namespace: props.namespace, // Include version information in the output @@ -1202,16 +1526,21 @@ export const _Worker = Resource( }, ); -export async function deleteWorker( - ctx: Context>, +export async function deleteWorker( api: CloudflareApi, - props: WorkerProps & { workerName: string }, + props: { + workerName: string; + namespace?: string | DispatchNamespaceResource; + url?: string; + }, ) { const workerName = props.workerName; // Delete worker const deleteResponse = await api.delete( - `/accounts/${api.accountId}/workers/scripts/${workerName}`, + props.namespace + ? `/accounts/${api.accountId}/workers/dispatch/namespaces/${props.namespace}/scripts/${workerName}?force=true` + : `/accounts/${api.accountId}/workers/scripts/${workerName}?force=true`, ); // Check for success (2xx status code) @@ -1220,7 +1549,7 @@ export async function deleteWorker( } // Disable the URL if it was enabled - if (ctx.output?.url) { + if (props.url) { try { await api.post( `/accounts/${api.accountId}/workers/scripts/${workerName}/subdomain`, @@ -1238,22 +1567,82 @@ export async function deleteWorker( return; } -interface PutWorkerOptions { - versionLabel?: string; - message?: string; - dispatchNamespace?: string; +function upsertDevCommand(props: { + id: string; + command: string; + cwd: string; + env: Record; +}) { + const persistFile = path.join(process.cwd(), ".alchemy", `${props.id}.pid`); + if (existsSync(persistFile)) { + const pid = Number.parseInt(readFileSync(persistFile, "utf8")); + try { + // Actually kill the process if it's alive + process.kill(pid, "SIGTERM"); + } catch { + // ignore + } + try { + unlinkSync(persistFile); + } catch { + // ignore + } + } + const command = props.command.split(" "); + const proc = spawn(command[0], command.slice(1), { + env: { + ...process.env, + ...props.env, + ALCHEMY_CLOUDFLARE_PERSIST_PATH: path.join( + process.cwd(), + ".alchemy", + "miniflare", + ), + }, + stdio: ["inherit", "inherit", "inherit"], + }); + process.on("SIGINT", () => { + try { + unlinkSync(persistFile); + } catch { + // ignore + } + proc.kill("SIGINT"); + process.exit(0); + }); + if (proc.pid) { + mkdirSync(path.dirname(persistFile), { recursive: true }); + writeFileSync(persistFile, proc.pid.toString()); + } } -async function putWorkerInternal( +type PutWorkerOptions = WorkerProps & { + dispatchNamespace?: string; + migrationTag?: string; + workerName: string; + scriptBundle: string | NoBundleResult; + version: string | undefined; + compatibilityDate: string; + compatibilityFlags: string[]; + assetUploadResult: AssetUploadResult | undefined; +}; + +export async function putWorker( api: CloudflareApi, - workerName: string, - scriptBundle: string | NoBundleResult, - scriptMetadata: WorkerMetadata, - options: PutWorkerOptions = {}, -): Promise<{ versionId?: string; previewUrl?: string }> { + props: PutWorkerOptions, +): Promise<{ versionId?: string; previewUrl?: string; deploymentId: string }> { + const { + // + dispatchNamespace, + migrationTag, + workerName, + scriptBundle, + version, + } = props; + const scriptMetadata = await prepareWorkerMetadata(api, props); + return withExponentialBackoff( async () => { - const { versionLabel, message, dispatchNamespace } = options; const scriptName = scriptMetadata.main_module ?? scriptMetadata.body_part!; @@ -1284,17 +1673,30 @@ async function putWorkerInternal( } // Prepare metadata - add version annotations if this is a version - const finalMetadata = versionLabel + const finalMetadata = version ? { ...scriptMetadata, // Exclude migrations for worker versions - they're not allowed migrations: undefined, annotations: { - "workers/tag": versionLabel, - ...(message && { "workers/message": message.substring(0, 100) }), + "workers/tag": version, + "workers/message": `Version ${version}`, }, } - : scriptMetadata; + : { + ...scriptMetadata, + migrations: scriptMetadata.migrations + ? { + ...scriptMetadata.migrations, + old_tag: migrationTag, + new_tag: bumpMigrationTagVersion(migrationTag), + } + : undefined, + }; + + if (process.env.DEBUG) { + console.log(`metadata(${scriptName}):`, finalMetadata); + } // Add metadata as JSON formData.append( @@ -1307,8 +1709,12 @@ async function putWorkerInternal( // Determine endpoint and HTTP method let endpoint: string; let method: "PUT" | "POST"; - - if (versionLabel) { + if (version) { + if (dispatchNamespace) { + throw new Error( + "Worker Preview Versions are not supported in Workers for Platforms", + ); + } // Upload worker version using the versions API endpoint = `/accounts/${api.accountId}/workers/scripts/${workerName}/versions`; method = "POST"; @@ -1335,30 +1741,57 @@ async function putWorkerInternal( // Check if the upload was successful if (!uploadResponse.ok) { - await handleApiError( - uploadResponse, - versionLabel ? "uploading worker version" : "uploading worker script", - "worker", - workerName, - ); + try { + return await handleApiError( + uploadResponse, + version ? "uploading worker version" : "uploading worker script", + "worker", + workerName, + ); + } catch (error) { + if (error instanceof CloudflareApiError && error.status === 412) { + // this happens when adopting a Worker managed with Wrangler + // because wrangler includes a migration tag and we do not + // currently, the only way to discover the old_tag is through the error message + // Get Worker Script Settings is meant to return it (according to the docs) + // but it doesn't work at runtime + // + // so, we catch the error and parse out the tag and then retry + if (error.message.includes("when expected tag is")) { + const newTag = error.message.match( + /when expected tag is ['"]?(v\d+)['"]?/, + )?.[1]; + if (newTag) { + return await putWorker(api, { + ...props, + migrationTag: newTag, + }); + } + } else { + throw error; + } + } else { + throw error; + } + } } - - // Handle version response - if (versionLabel) { - const responseData = (await uploadResponse.json()) as { - result: { - id: string; - number: number; - metadata: { - has_preview: boolean; - }; - annotations?: { - "workers/tag"?: string; - }; + const responseData = (await uploadResponse.json()) as { + result: { + id: string; + number: number; + metadata: { + has_preview: boolean; + }; + annotations?: { + "workers/tag"?: string; }; + deployment_id: string; }; - const result = responseData.result; + }; + const result = responseData.result; + // Handle version response + if (props.version) { // Get the account's workers.dev subdomain to construct preview URL let previewUrl: string | undefined; if (result.metadata?.has_preview) { @@ -1385,10 +1818,13 @@ async function putWorkerInternal( return { versionId: result.id, previewUrl, + deploymentId: result.deployment_id, }; } - return {}; + return { + deploymentId: result.deployment_id, + }; }, (err) => err.status === 404 || @@ -1403,27 +1839,6 @@ async function putWorkerInternal( ); } -export async function putWorker( - api: CloudflareApi, - workerName: string, - scriptBundle: string | NoBundleResult, - scriptMetadata: WorkerMetadata, - dispatchNamespace?: string, - version?: { versionLabel: string; message?: string }, -): Promise<{ versionId?: string; previewUrl?: string }> { - return await putWorkerInternal( - api, - workerName, - scriptBundle, - scriptMetadata, - { - dispatchNamespace, - versionLabel: version?.versionLabel, - message: version?.message, - }, - ); -} - export async function checkWorkerExists( api: CloudflareApi, workerName: string, @@ -1434,8 +1849,7 @@ export async function checkWorkerExists( return response.status === 200; } -export async function assertWorkerDoesNotExist( - ctx: Context>, +export async function assertWorkerDoesNotExist( api: CloudflareApi, workerName: string, ) { @@ -1454,14 +1868,6 @@ export async function assertWorkerDoesNotExist( ); } - if ( - metadata.default_environment?.script.tags.includes( - `alchemy:id:${slugify(ctx.fqn)}`, - ) - ) { - return true; - } - throw new Error( `Worker with name '${workerName}' already exists. Please use a unique name.`, ); @@ -1472,7 +1878,7 @@ export async function assertWorkerDoesNotExist( } export async function configureURL( - ctx: Context>, + ctx: Context> | Context, api: CloudflareApi, workerName: string, url: boolean, @@ -1489,31 +1895,10 @@ export async function configureURL( ); // Get the account's workers.dev subdomain - const subdomainResponse = await api.get( - `/accounts/${api.accountId}/workers/subdomain`, - ); - - if (!subdomainResponse.ok) { - throw new Error( - `Could not fetch workers.dev subdomain: ${subdomainResponse.status} ${subdomainResponse.statusText}`, - ); - } - const subdomainData: { - result: { - subdomain: string; - }; - } = await subdomainResponse.json(); - const subdomain = subdomainData.result?.subdomain; + const subdomain = await getAccountSubdomain(api); if (subdomain) { workerUrl = `https://${workerName}.${subdomain}.workers.dev`; - - // Add a delay when the subdomain is first created. - // This is to prevent an issue where a negative cache-hit - // causes the subdomain to be unavailable for 30 seconds. - if (ctx.phase === "create" || !ctx.output?.url) { - await new Promise((resolve) => setTimeout(resolve, 3000)); - } } } else if (url === false && ctx.output?.url) { // Explicitly disable URL if it was previously enabled @@ -1551,51 +1936,6 @@ export async function getWorkerScriptMetadata( return ((await response.json()) as any).result as WorkerScriptMetadata; } -async function getWorkerBindings(api: CloudflareApi, workerName: string) { - // Fetch the bindings for a worker by calling the Cloudflare API endpoint: - // GET /accounts/:account_id/workers/scripts/:script_name/bindings - // See: https://developers.cloudflare.com/api/resources/workers/subresources/scripts/subresources/script_and_version_settings/methods/get/ - const response = await api.get( - `/accounts/${api.accountId}/workers/scripts/${workerName}/settings`, - ); - if (response.status === 404) { - return undefined; - } - if (!response.ok) { - throw new Error( - `Error getting worker bindings: ${response.status} ${response.statusText}`, - ); - } - // The result is an object with a "result" property containing the bindings array - const { result, success, errors } = (await response.json()) as { - result: { - bindings: WorkerBindingSpec[]; - compatibility_date: string; - compatibility_flags: string[]; - [key: string]: any; - }; - success: boolean; - errors: Array<{ - code: number; - message: string; - documentation_url: string; - [key: string]: any; - }>; - messages: Array<{ - code: number; - message: string; - documentation_url: string; - [key: string]: any; - }>; - }; - if (!success) { - throw new Error( - `Error getting worker bindings: ${response.status} ${response.statusText}\nErrors:\n${errors.map((e) => `- [${e.code}] ${e.message} (${e.documentation_url})`).join("\n")}`, - ); - } - return result.bindings; -} - /** * Lists and deletes all queue consumers for a specific worker * @param ctx Worker context containing eventSources diff --git a/alchemy/src/cloudflare/worker/get-worker-template.ts b/alchemy/src/cloudflare/worker/get-worker-template.ts new file mode 100644 index 000000000..d9062ce2c --- /dev/null +++ b/alchemy/src/cloudflare/worker/get-worker-template.ts @@ -0,0 +1,14 @@ +import { readFile } from "node:fs/promises"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; + +export async function getWorkerTemplate( + name: "do-state-store" | "mixed-mode-proxy-worker", +) { + const dir = dirname(fileURLToPath(import.meta.url)); + const path = join(dir, "..", "..", "..", "workers", `${name}.js`); + const template = await readFile(path, "utf8"); + return new File([template], `${name}.js`, { + type: "application/javascript+module", + }); +} diff --git a/alchemy/src/cloudflare/worker/http-server.ts b/alchemy/src/cloudflare/worker/http-server.ts new file mode 100644 index 000000000..8f2c74f59 --- /dev/null +++ b/alchemy/src/cloudflare/worker/http-server.ts @@ -0,0 +1,80 @@ +import http from "node:http"; +import type { AddressInfo } from "node:net"; +import { Readable } from "node:stream"; + +export class HTTPServer { + server: http.Server; + ready: Promise; + + constructor(options: { + port?: number; + fetch: (request: Request) => Promise; + }) { + const { promise, resolve } = Promise.withResolvers(); + this.ready = promise; + this.server = http + .createServer(async (req, res) => { + const response = await options.fetch(toWebRequest(req)); + await writeNodeResponse(res, response); + }) + .listen(options.port, resolve); + } + + get port() { + return (this.server.address() as AddressInfo).port; + } + + get hostname() { + const address = (this.server.address() as AddressInfo)?.address; + if (address === "::") { + return "localhost"; + } + return address; + } + + get url() { + return `http://${this.hostname}:${this.port}`; + } + + stop() { + return new Promise((resolve, reject) => { + this.server.close((err) => { + if (err) { + reject(err); + } else { + resolve(undefined); + } + }); + }); + } +} + +function toWebRequest(request: http.IncomingMessage): Request { + const method = request.method ?? "GET"; + return new Request(`http://${request.headers.host}${request.url ?? "/"}`, { + method, + headers: request.headers as Record, + body: + ["GET", "HEAD", "OPTIONS"].includes(method) || !request.readable + ? undefined + : (Readable.toWeb(request) as unknown as BodyInit), + }); +} + +async function writeNodeResponse(res: http.ServerResponse, response: Response) { + res.statusCode = response.status; + response.headers.forEach((value, key) => { + res.setHeader(key, value); + }); + await response.body?.pipeTo( + new WritableStream({ + write(chunk) { + res.write(chunk); + }, + close() { + res.end(); + }, + }), + ); + res.end(); +} diff --git a/alchemy/src/cloudflare/worker/miniflare-worker-options.ts b/alchemy/src/cloudflare/worker/miniflare-worker-options.ts new file mode 100644 index 000000000..af41d1e7b --- /dev/null +++ b/alchemy/src/cloudflare/worker/miniflare-worker-options.ts @@ -0,0 +1,559 @@ +import { + kCurrentWorker, + type RemoteProxyConnectionString, + type WorkerOptions, +} from "miniflare"; +import assert from "node:assert"; +import { assertNever } from "../../util/assert-never.ts"; +import { logger } from "../../util/logger.ts"; +import { Self, type Binding, type WorkerBindingSpec } from "../bindings.ts"; +import type { WorkerProps } from "../worker.ts"; + +export type MiniflareWorkerOptions = Pick< + WorkerProps, + | "bindings" + | "eventSources" + | "compatibilityDate" + | "compatibilityFlags" + | "format" +> & { + name: string; + script: string; + port?: number; +}; + +type BindingType = Exclude["type"]; + +const REMOTE_ONLY_BINDING_TYPES = [ + "ai", + "ai_gateway", + "browser", + "dispatch_namespace", + "vectorize", +] satisfies BindingType[]; +const REMOTE_OPTIONAL_BINDING_TYPES = [ + "d1", + // "durable_object_namespace", This is supported in Miniflare but needs some wrangling to make it work with a remote proxy. + "images", + "kv_namespace", + "r2_bucket", + "queue", + "service", + // "workflow", same thing +] satisfies BindingType[]; + +type RemoteBindingType = + | (typeof REMOTE_ONLY_BINDING_TYPES)[number] + | (typeof REMOTE_OPTIONAL_BINDING_TYPES)[number]; + +type RemoteBinding = Extract; + +export function buildRemoteBindings( + input: Pick, +) { + const bindings: WorkerBindingSpec[] = []; + for (const [name, binding] of Object.entries(input.bindings ?? {})) { + if (isRemoteOnlyBinding(binding)) { + bindings.push(buildRemoteBinding(name, binding)); + } else if (isRemoteOptionalBinding(binding) && isRemoteEnabled(binding)) { + bindings.push(buildRemoteBinding(name, binding)); + } + } + return bindings; +} + +function isRemoteOptionalBinding(binding: Binding): binding is RemoteBinding { + return ( + typeof binding !== "string" && + binding !== Self && + typeof binding === "object" && + "type" in binding && + REMOTE_OPTIONAL_BINDING_TYPES.includes(binding.type as any) + ); +} + +function isRemoteOnlyBinding(binding: Binding): binding is RemoteBinding { + return ( + typeof binding !== "string" && + binding !== Self && + typeof binding === "object" && + "type" in binding && + REMOTE_ONLY_BINDING_TYPES.includes(binding.type as any) + ); +} + +function isRemoteEnabled(binding: RemoteBinding): boolean { + return ( + "dev" in binding && + typeof binding.dev === "object" && + "remote" in binding.dev && + !!binding.dev.remote + ); +} + +function buildRemoteBinding( + name: string, + binding: RemoteBinding, +): WorkerBindingSpec & { raw?: true } { + switch (binding.type) { + case "ai": { + return { + type: "ai", + name, + raw: true, + }; + } + case "ai_gateway": { + return { + type: "ai", + name, + raw: true, + }; + } + case "browser": { + return { + type: "browser", + name, + raw: true, + }; + } + case "d1": { + return { + type: "d1", + name, + id: binding.id, + raw: true, + }; + } + case "dispatch_namespace": { + return { + type: "dispatch_namespace", + name, + namespace: binding.namespace, + raw: true, + }; + } + // case "durable_object_namespace": { + // return { + // type: "durable_object_namespace", + // name, + // class_name: binding.className, + // script_name: binding.scriptName, + // raw: true, + // }; + // } + case "images": { + return { + type: "images", + name, + raw: true, + }; + } + case "kv_namespace": { + return { + type: "kv_namespace", + name, + namespace_id: + "namespaceId" in binding ? binding.namespaceId : binding.id, + raw: true, + }; + } + case "queue": { + return { + type: "queue", + name, + queue_name: binding.name, + raw: true, + }; + } + case "r2_bucket": { + return { + type: "r2_bucket", + name, + bucket_name: binding.name, + raw: true, + }; + } + case "service": { + return { + type: "service", + name, + service: "service" in binding ? binding.service : binding.name, + environment: "environment" in binding ? binding.environment : undefined, + }; + } + case "vectorize": { + return { + type: "vectorize", + name, + index_name: binding.name, + raw: true, + }; + } + // case "workflow": { + // return { + // type: "workflow", + // name, + // workflow_name: binding.workflowName, + // class_name: binding.className, + // script_name: binding.scriptName, + // raw: true, + // }; + // } + default: { + assertNever(binding); + } + } +} + +export function buildMiniflareWorkerOptions({ + name: workerName, + script, + bindings, + format, + eventSources, + compatibilityDate, + compatibilityFlags, + remoteProxyConnectionString, +}: MiniflareWorkerOptions & { + remoteProxyConnectionString: RemoteProxyConnectionString | undefined; +}): WorkerOptions { + const options: WorkerOptions = { + name: workerName, + script, + modules: format !== "cjs", + compatibilityDate, + compatibilityFlags, + unsafeDirectSockets: [{ entrypoint: undefined, proxy: true }], + // containerEngine: { + // localDocker: { + // socketPath: "/var/run/docker.sock", + // } + // } + }; + for (const [name, binding] of Object.entries(bindings ?? {})) { + if (typeof binding === "string") { + options.bindings = { + ...options.bindings, + [name]: binding, + }; + continue; + } + if (binding === Self) { + options.serviceBindings = { + ...((options.serviceBindings as Record | undefined) ?? + {}), + [name]: kCurrentWorker, + }; + continue; + } + switch (binding.type) { + case "ai": { + assert( + remoteProxyConnectionString, + `Binding "${name}" of type "${binding.type}" requires a remoteProxyConnectionString, but none was provided.`, + ); + options.ai = { + binding: name, + remoteProxyConnectionString, + }; + break; + } + case "ai_gateway": { + assert( + remoteProxyConnectionString, + `Binding "${name}" of type "${binding.type}" requires a remoteProxyConnectionString, but none was provided.`, + ); + options.ai = { + binding: name, + remoteProxyConnectionString, + }; + break; + } + case "analytics_engine": { + (options.analyticsEngineDatasets ??= {})[name] = { + dataset: binding.dataset, + }; + break; + } + case "assets": { + options.assets = { + binding: name, + directory: binding.path, + }; + break; + } + case "browser": { + assert( + remoteProxyConnectionString, + `Binding "${name}" of type "${binding.type}" requires a remoteProxyConnectionString, but none was provided.`, + ); + options.browserRendering = { + binding: name, + remoteProxyConnectionString, + }; + break; + } + case "d1": { + ( + (options.d1Databases ??= {}) as Record< + string, + { + id: string; + remoteProxyConnectionString?: RemoteProxyConnectionString; + } + > + )[name] = { + id: binding.id, + remoteProxyConnectionString: binding.dev?.remote + ? remoteProxyConnectionString + : undefined, + }; + break; + } + case "dispatch_namespace": { + assert( + remoteProxyConnectionString, + `Binding "${name}" of type "${binding.type}" requires a remoteProxyConnectionString, but none was provided.`, + ); + (options.dispatchNamespaces ??= {})[name] = { + namespace: binding.namespace, + remoteProxyConnectionString, + }; + break; + } + case "durable_object_namespace": { + (options.durableObjects ??= {})[name] = { + className: binding.className, + scriptName: binding.scriptName, + useSQLite: binding.sqlite, + // namespaceId: binding.namespaceId, + // unsafeUniqueKey?: string | typeof kUnsafeEphemeralUniqueKey | undefined; + // unsafePreventEviction?: boolean | undefined; + // remoteProxyConnectionString: binding.local + // ? undefined + // : remoteProxyConnectionString, + }; + if (!binding.scriptName || binding.scriptName === workerName) { + options.unsafeDirectSockets!.push({ + entrypoint: binding.className, + proxy: true, + }); + } + break; + } + case "hyperdrive": { + if ("access_client_id" in binding.origin) { + throw new Error( + `Hyperdrive with Cloudflare Access is not supported for locally emulated workers. Worker "${name}" is locally emulated but is bound to Hyperdrive "${name}", which has Cloudflare Access enabled.`, + ); + } + logger.warnOnce( + `Hyperdrive bindings in locally emulated workers are experimental and may not work as expected. Worker "${name}" is locally emulated and bound to Hyperdrive "${name}".`, + ); + const { + scheme = "postgres", + port = 5432, + password, + database, + host, + user, + } = binding.origin; + const connectionString = new URL( + `${scheme}://${user}:${password.unencrypted}@${host}:${port}/${database}?sslmode=${binding.mtls?.sslmode ?? "verify-full"}`, + ); + (options.bindings ??= {})[name] = { + connectionString: connectionString.toString(), + database, + host, + password: password.unencrypted, + port, + scheme, + user, + }; + break; + } + case "images": { + options.images = { + binding: name, + remoteProxyConnectionString: binding.dev?.remote + ? remoteProxyConnectionString + : undefined, + }; + break; + } + case "json": { + (options.bindings ??= {})[name] = binding.json; + break; + } + case "kv_namespace": { + const normalized = + "id" in binding + ? { id: binding.id } + : { id: binding.namespaceId, dev: binding.dev }; + ( + (options.kvNamespaces ??= {}) as Record< + string, + { + id: string; + remoteProxyConnectionString?: RemoteProxyConnectionString; + } + > + )[name] = { + id: normalized.id, + remoteProxyConnectionString: normalized.dev?.remote + ? remoteProxyConnectionString + : undefined, + }; + break; + } + case "pipeline": { + ((options.pipelines ??= {}) as Record)[name] = + binding.id; + break; + } + case "queue": { + ( + (options.queueProducers ??= {}) as Record< + string, + { + queueName: string; + deliveryDelay?: number; + remoteProxyConnectionString?: RemoteProxyConnectionString; + } + > + )[name] = { + queueName: binding.name, + deliveryDelay: binding.settings?.deliveryDelay, + remoteProxyConnectionString: binding.dev?.remote + ? remoteProxyConnectionString + : undefined, + }; + break; + } + case "r2_bucket": { + ( + (options.r2Buckets ??= {}) as Record< + string, + { + id: string; + remoteProxyConnectionString?: RemoteProxyConnectionString; + } + > + )[name] = { + id: binding.name, + remoteProxyConnectionString: binding.dev?.remote + ? remoteProxyConnectionString + : undefined, + }; + break; + } + case "secret": { + (options.bindings ??= {})[name] = binding.unencrypted; + break; + } + case "secrets_store_secret": { + options.secretsStoreSecrets = { + ...((options.secretsStoreSecrets as + | Record + | undefined) ?? {}), + [name]: { + store_id: binding.storeId, + secret_name: binding.name, + }, + }; + break; + } + case "secret_key": { + throw new Error( + `Secret key bindings are not supported for locally emulated workers. Worker "${name}" is locally emulated but is bound to secret key "${name}".`, + ); + } + case "service": { + if (!("id" in binding)) { + throw new Error( + `Service bindings must have an id. Worker "${name}" is bound to service "${name}" but does not have an id.`, + ); + } + if (isRemoteEnabled(binding)) { + (options.serviceBindings ??= {})[name] = { + name: binding.name, + remoteProxyConnectionString, + }; + } else { + (options.serviceBindings ??= {})[name] = binding.name; + } + break; + } + case "vectorize": { + assert( + remoteProxyConnectionString, + `Binding "${name}" of type "${binding.type}" requires a remoteProxyConnectionString, but none was provided.`, + ); + (options.vectorize ??= {})[name] = { + index_name: binding.name, + remoteProxyConnectionString, + }; + break; + } + case "version_metadata": { + // This is how Wrangler does it: + // https://github.com/cloudflare/workers-sdk/blob/70ba9fbf905a9ba5fe158d0bc8d48f6bf31712a2/packages/wrangler/src/dev/miniflare.ts#L881 + (options.bindings ??= {})[name] = { + id: crypto.randomUUID(), + tag: "", + timestamp: "0", + }; + break; + } + case "workflow": { + (options.workflows ??= {})[name] = { + name: binding.workflowName, + className: binding.className, + scriptName: binding.scriptName, + // remoteProxyConnectionString: + // "local" in binding && binding.local + // ? undefined + // : remoteProxyConnectionString, + }; + break; + } + case "container": { + (options.durableObjects ??= {})[name] = { + className: binding.className, + scriptName: binding.scriptName, + useSQLite: binding.sqlite, + container: { + imageName: binding.image.name, + }, + // namespaceId: binding.namespaceId, + // unsafeUniqueKey?: string | typeof kUnsafeEphemeralUniqueKey | undefined; + // unsafePreventEviction?: boolean | undefined; + // remoteProxyConnectionString: binding.local + // ? undefined + // : remoteProxyConnectionString, + }; + if (!binding.scriptName || binding.scriptName === workerName) { + options.unsafeDirectSockets!.push({ + entrypoint: binding.className, + proxy: true, + }); + } + break; + } + default: { + assertNever(binding); + } + } + } + for (const eventSource of eventSources ?? []) { + const queue = "queue" in eventSource ? eventSource.queue : eventSource; + if (queue.dev?.remote !== false) { + throw new Error( + `Locally emulated workers cannot consume remote queues. Worker "${workerName}" is locally emulated but is consuming remote queue "${queue.name}".`, + ); + } + ((options.queueConsumers ??= []) as string[]).push(queue.name); + } + return options; +} diff --git a/alchemy/src/cloudflare/worker/miniflare.ts b/alchemy/src/cloudflare/worker/miniflare.ts new file mode 100644 index 000000000..f068e17f3 --- /dev/null +++ b/alchemy/src/cloudflare/worker/miniflare.ts @@ -0,0 +1,199 @@ +import type { + Miniflare, + MiniflareOptions, + RemoteProxyConnectionString, + WorkerOptions, +} from "miniflare"; +import path from "node:path"; +import { findOpenPort } from "../../util/find-open-port.ts"; +import { logger } from "../../util/logger.ts"; +import { HTTPServer } from "./http-server.ts"; +import { + buildMiniflareWorkerOptions, + buildRemoteBindings, + type MiniflareWorkerOptions, +} from "./miniflare-worker-options.ts"; +import { createMixedModeProxy, type MixedModeProxy } from "./mixed-mode.ts"; + +class MiniflareServer { + miniflare?: Miniflare; + workers = new Map(); + servers = new Map(); + mixedModeProxies = new Map(); + + stream = new WritableStream<{ + worker: MiniflareWorkerOptions; + promise: PromiseWithResolvers; + }>({ + write: async ({ worker, promise }) => { + try { + const server = await this.set(worker); + promise.resolve(server); + } catch (error) { + promise.reject(error); + } + }, + close: async () => { + await this.dispose(); + }, + }); + writer = this.stream.getWriter(); + + async push(worker: MiniflareWorkerOptions) { + const promise = Promise.withResolvers(); + const [, server] = await Promise.all([ + this.writer.write({ worker, promise }), + promise.promise, + ]); + return server; + } + + async close() { + await this.writer.close(); + } + + private async set(worker: MiniflareWorkerOptions) { + this.workers.set( + worker.name as string, + buildMiniflareWorkerOptions({ + ...worker, + remoteProxyConnectionString: + await this.maybeCreateMixedModeProxy(worker), + }), + ); + if (this.miniflare) { + await this.miniflare.setOptions(await this.miniflareOptions()); + } else { + const { Miniflare } = await import("miniflare").catch(() => { + throw new Error( + "Miniflare is not installed, but is required in local mode for Workers. Please run `npm install miniflare`.", + ); + }); + + // Miniflare intercepts SIGINT and exits with 130, which is not a failure. + // No one likes to see a non-zero exit code when they Ctrl+C, so here's our workaround. + process.on("exit", (code) => { + if (code === 130) { + process.exit(0); + } + }); + + this.miniflare = new Miniflare(await this.miniflareOptions()); + await this.miniflare.ready; + } + const existing = this.servers.get(worker.name); + if (existing) { + return existing; + } + const server = new HTTPServer({ + port: worker.port ?? (await findOpenPort()), + fetch: this.createRequestHandler(worker.name as string), + }); + this.servers.set(worker.name, server); + await server.ready; + return server; + } + + private async dispose() { + await Promise.all([ + this.miniflare?.dispose(), + ...Array.from(this.servers.values()).map((server) => server.stop()), + ...Array.from(this.mixedModeProxies.values()).map((proxy) => + proxy.server.stop(), + ), + ]); + this.miniflare = undefined; + this.workers.clear(); + this.servers.clear(); + } + + private async maybeCreateMixedModeProxy( + worker: MiniflareWorkerOptions, + ): Promise { + const bindings = buildRemoteBindings(worker); + if (bindings.length === 0) { + return undefined; + } + const existing = this.mixedModeProxies.get(worker.name); + if ( + existing?.bindings.every((b) => + bindings.find((b2) => b2.name === b.name && b2.type === b.type), + ) + ) { + return existing.connectionString; + } + const proxy = await createMixedModeProxy({ + name: `mixed-mode-proxy-${crypto.randomUUID()}`, + bindings, + }); + this.mixedModeProxies.set(worker.name, proxy); + return proxy.connectionString; + } + + private createRequestHandler(name: string) { + return async (req: Request) => { + try { + if (!this.miniflare) { + return new Response( + "[Alchemy] Miniflare is not initialized. Please try again.", + { + status: 503, + }, + ); + } + const miniflare = await this.miniflare?.getWorker(name); + if (!miniflare) { + return new Response( + `[Alchemy] Cannot find worker "${name}". Please try again.`, + { + status: 503, + }, + ); + } + const res = await miniflare.fetch(req.url, { + method: req.method, + headers: req.headers as any, + body: req.body as any, + redirect: "manual", + }); + return res as unknown as Response; + } catch (error) { + logger.error(error); + return new Response( + `[Alchemy] Internal server error: ${String(error)}`, + { + status: 500, + }, + ); + } + }; + } + + private async miniflareOptions(): Promise { + const { getDefaultDevRegistryPath } = await import("miniflare"); + return { + workers: Array.from(this.workers.values()), + defaultPersistRoot: path.join(process.cwd(), ".alchemy/miniflare"), + unsafeDevRegistryPath: getDefaultDevRegistryPath(), + analyticsEngineDatasetsPersist: true, + cachePersist: true, + d1Persist: true, + durableObjectsPersist: true, + kvPersist: true, + r2Persist: true, + secretsStorePersist: true, + workflowsPersist: true, + }; + } +} + +declare global { + var _ALCHEMY_MINIFLARE_SERVER: MiniflareServer | undefined; +} + +export const miniflareServer = new Proxy({} as MiniflareServer, { + get: (_, prop: keyof MiniflareServer) => { + globalThis._ALCHEMY_MINIFLARE_SERVER ??= new MiniflareServer(); + return globalThis._ALCHEMY_MINIFLARE_SERVER[prop]; + }, +}); diff --git a/alchemy/src/cloudflare/worker/mixed-mode.ts b/alchemy/src/cloudflare/worker/mixed-mode.ts new file mode 100644 index 000000000..79544cef7 --- /dev/null +++ b/alchemy/src/cloudflare/worker/mixed-mode.ts @@ -0,0 +1,220 @@ +import type { RemoteProxyConnectionString } from "miniflare"; +import { createCloudflareApi, type CloudflareApi } from "../api.ts"; +import type { WorkerBindingSpec } from "../bindings.ts"; +import type { CloudflareApiResponse } from "../types.ts"; +import type { WorkerMetadata } from "../worker-metadata.ts"; +import { getWorkerTemplate } from "./get-worker-template.ts"; +import { HTTPServer } from "./http-server.ts"; +import { getAccountSubdomain } from "./subdomain.ts"; + +type WranglerSessionConfig = + | { + workers_dev: boolean; + minimal_mode: boolean; + } + | { + routes: string[]; + minimal_mode: boolean; + }; + +interface WorkersPreviewSession { + inspector_websocket: string; + prewarm: string; + token: string; +} + +export async function createMixedModeProxy(input: { + name: string; + bindings: WorkerBindingSpec[]; +}) { + const api = await createCloudflareApi(); + const script = await getWorkerTemplate("mixed-mode-proxy-worker"); + const [token, subdomain] = await Promise.all([ + createWorkersPreviewToken(api, { + name: input.name, + metadata: { + main_module: script.name, + compatibility_date: "2025-06-16", + bindings: input.bindings, + observability: { + enabled: false, + }, + }, + files: [script], + session: { + workers_dev: true, + minimal_mode: true, + }, + }), + getAccountSubdomain(api), + ]); + return new MixedModeProxy( + `https://${input.name}.${subdomain}.workers.dev`, + token, + input.bindings, + ); +} + +const DEBUG: boolean = false; + +export class MixedModeProxy { + server: HTTPServer; + + constructor( + readonly url: string, + readonly token: string, + readonly bindings: WorkerBindingSpec[], + ) { + this.server = new HTTPServer({ + fetch: this.fetch.bind(this), + }); + } + + get connectionString() { + const hostname = + this.server.hostname === "::" ? "localhost" : this.server.hostname; + return new URL( + `http://${hostname}:${this.server.port}`, + ) as RemoteProxyConnectionString; + } + + async fetch(req: Request) { + const origin = new URL(req.url); + const url = new URL(origin.pathname, this.url); + url.search = origin.search; + url.hash = origin.hash; + + const headers = new Headers(req.headers); + headers.set("cf-workers-preview-token", this.token); + headers.set("host", new URL(this.url).hostname); + headers.delete("cf-connecting-ip"); + + const res = await fetch(url, { + method: req.method, + headers, + body: req.body, + redirect: "manual", + }); + + // Remove headers that are not supported by miniflare + const responseHeaders = new Headers(res.headers); + responseHeaders.delete("transfer-encoding"); + responseHeaders.delete("content-encoding"); + + if (DEBUG) { + const clone = res.clone(); + console.log({ + request: { + url: url.toString(), + method: req.method, + headers, + body: req.body, + }, + response: { + status: res.status, + headers: res.headers, + body: await res.text(), + }, + }); + return new Response(clone.body, { + status: clone.status, + headers: responseHeaders, + }); + } + + return new Response(res.body, { + status: res.status, + headers: responseHeaders, + }); + } +} + +async function createWorkersPreviewToken( + api: CloudflareApi, + input: { + name: string; + metadata: WorkerMetadata; + files: File[]; + session: WranglerSessionConfig; + }, +) { + const session = await createWorkersPreviewSession(api); + const formData = new FormData(); + formData.append("metadata", JSON.stringify(input.metadata)); + for (const file of input.files) { + formData.append(file.name, file); + } + formData.append("wrangler-session-config", JSON.stringify(input.session)); + const res = await api + .post( + `/accounts/${api.accountId}/workers/scripts/${input.name}/edge-preview`, + formData, + { + headers: { + "cf-preview-upload-config-token": session.token, + }, + }, + ) + .then((res) => + parseCloudflareResponse<{ preview_token: string }>( + res, + "Failed to create workers preview token", + ), + ); + // Fire and forget prewarm call + // (see https://github.com/cloudflare/workers-sdk/blob/6c6afbd6072b96e78e67d3a863ed849c6aa49472/packages/wrangler/src/dev/create-worker-preview.ts#L338) + void prewarm(session.prewarm, res.preview_token); + return res.preview_token; +} + +async function prewarm(url: string, previewToken: string) { + const res = await fetch(url, { + headers: { + "cf-workers-preview-token": previewToken, + }, + }); + if (!res.ok) { + console.error(`Failed to prewarm worker: ${res.status} ${res.statusText}`); + } +} + +async function createWorkersPreviewSession(api: CloudflareApi) { + const { exchange_url } = await api + .get(`/accounts/${api.accountId}/workers/subdomain/edge-preview`) + .then((res) => + parseCloudflareResponse<{ + exchange_url: string; + token: string; + }>(res, "Failed to create workers preview session"), + ); + return await fetch(exchange_url).then((res) => + parseResponse( + res, + "Failed to create workers preview session", + ), + ); +} + +async function parseResponse(res: Response, message: string): Promise { + if (!res.ok) { + throw new Error(`${message} (${res.status} ${res.statusText})`); + } + const json: T = await res.json(); + return json; +} + +async function parseCloudflareResponse( + res: Response, + message: string, +): Promise { + const json: CloudflareApiResponse = await res.json(); + if (!json.success) { + throw new Error( + `${message} (${res.status} ${res.statusText} - ${json.errors.map((e) => `${e.code}: ${e.message}`).join(", ")})`, + ); + } + if (!json.result) { + throw new Error(`${message} (${res.status} ${res.statusText})`); + } + return json.result; +} diff --git a/alchemy/src/cloudflare/worker/subdomain.ts b/alchemy/src/cloudflare/worker/subdomain.ts new file mode 100644 index 000000000..18cab925f --- /dev/null +++ b/alchemy/src/cloudflare/worker/subdomain.ts @@ -0,0 +1,16 @@ +import { memoize } from "../../util/memoize.ts"; +import type { CloudflareApi } from "../api.ts"; + +export const getAccountSubdomain = memoize( + async (api: CloudflareApi) => { + const res = await api.get(`/accounts/${api.accountId}/workers/subdomain`); + if (!res.ok) { + throw new Error( + `Failed to get account subdomain: ${res.status} ${res.statusText}`, + ); + } + const json: { result?: { subdomain: string } } = await res.json(); + return json.result?.subdomain; + }, + (api) => api.accountId, +); diff --git a/alchemy/src/cloudflare/workflow.ts b/alchemy/src/cloudflare/workflow.ts index 9e9bc5cb2..806c55b1e 100644 --- a/alchemy/src/cloudflare/workflow.ts +++ b/alchemy/src/cloudflare/workflow.ts @@ -25,6 +25,13 @@ export interface WorkflowProps { * @default - bound worker script */ scriptName?: string; + dev?: { + /** + * Whether to run the workflow remotely instead of locally + * @default false + */ + remote?: boolean; + }; } export function isWorkflow(binding: Binding): binding is Workflow { diff --git a/alchemy/src/cloudflare/wrangler.json.ts b/alchemy/src/cloudflare/wrangler.json.ts index 04cb57262..8b37bb904 100644 --- a/alchemy/src/cloudflare/wrangler.json.ts +++ b/alchemy/src/cloudflare/wrangler.json.ts @@ -447,6 +447,9 @@ function processBindings( binding: string; namespace: string; }[] = []; + const containers: { + class_name: string; + }[] = []; for (const eventSource of eventSources ?? []) { if (isQueueEventSource(eventSource)) { @@ -619,6 +622,15 @@ function processBindings( }); } else if (binding.type === "secret_key") { // no-op + } else if (binding.type === "container") { + durableObjects.push({ + name: bindingName, + class_name: binding.className, + script_name: binding.scriptName, + }); + containers.push({ + class_name: binding.className, + }); } else { // biome-ignore lint/correctness/noVoidTypeReturn: it returns never return assertNever(binding); diff --git a/alchemy/src/cloudflare/zone.ts b/alchemy/src/cloudflare/zone.ts index 869835fd7..19662839f 100644 --- a/alchemy/src/cloudflare/zone.ts +++ b/alchemy/src/cloudflare/zone.ts @@ -2,7 +2,11 @@ import type { Context } from "../context.ts"; import { Resource } from "../resource.ts"; import { logger } from "../util/logger.ts"; import { handleApiError } from "./api-error.ts"; -import { createCloudflareApi, type CloudflareApiOptions } from "./api.ts"; +import { + createCloudflareApi, + type CloudflareApi, + type CloudflareApiOptions, +} from "./api.ts"; import type { AlwaysUseHTTPSValue, AutomaticHTTPSRewritesValue, @@ -554,7 +558,7 @@ async function getZoneSettings( * * @example * // Look up a zone by domain name - * const zone = await getZoneByDomain("example.com"); + * const zone = await getZoneByDomain(api, "example.com"); * if (zone) { * console.log(`Zone ID: ${zone.id}`); * console.log(`Nameservers: ${zone.nameservers.join(", ")}`); @@ -562,17 +566,12 @@ async function getZoneSettings( * * @example * // Look up a zone with custom API options - * const zone = await getZoneByDomain("example.com", { - * apiToken: myApiToken, - * accountId: "my-account-id" - * }); + * const zone = await getZoneByDomain(api, "example.com"); */ export async function getZoneByDomain( + api: CloudflareApi, domainName: string, - options: Partial = {}, ): Promise { - const api = await createCloudflareApi(options); - const response = await api.get( `/zones?name=${encodeURIComponent(domainName)}`, ); diff --git a/alchemy/src/context.ts b/alchemy/src/context.ts index bf62d28a2..043a6981a 100644 --- a/alchemy/src/context.ts +++ b/alchemy/src/context.ts @@ -53,7 +53,7 @@ export interface BaseContext { * Indicate that this resource is being replaced. * This will cause the resource to be deleted at the end of the stack's CREATE phase. */ - replace(): void; + replace(): never; /** * Terminate the resource lifecycle handler and destroy the resource. * @@ -97,7 +97,7 @@ export function context< seq: number; props: Props; state: State; - replace: () => void; + replace: () => never; }): Context { type InternalSymbols = | typeof ResourceID diff --git a/alchemy/src/destroy.ts b/alchemy/src/destroy.ts index 1fef30765..753fc01d1 100644 --- a/alchemy/src/destroy.ts +++ b/alchemy/src/destroy.ts @@ -6,10 +6,11 @@ import { ResourceFQN, ResourceID, ResourceKind, + type ResourceProps, ResourceScope, ResourceSeq, } from "./resource.ts"; -import { isScope, Scope } from "./scope.ts"; +import { isScope, type PendingDeletions, Scope } from "./scope.ts"; import { formatFQN } from "./util/cli.ts"; import { logger } from "./util/logger.ts"; @@ -18,6 +19,10 @@ export class DestroyedSignal extends Error {} export interface DestroyOptions { quiet?: boolean; strategy?: "sequential" | "parallel"; + replace?: { + props?: ResourceProps | undefined; + output?: Resource; + }; } function isScopeArgs(a: any): a is [scope: Scope, options?: DestroyOptions] { @@ -40,7 +45,8 @@ export async function destroy( } satisfies DestroyOptions; await scope.run(async () => { - // destroy all active resources + // destroy all active and pending resources + await scope.destroyPendingDeletions(); await destroyAll(Array.from(scope.resources.values()), options); // then detect orphans and destroy them @@ -89,10 +95,12 @@ export async function destroy( try { if (!quiet) { logger.task(instance[ResourceFQN], { - prefix: "deleting", - prefixColor: "redBright", + prefix: options?.replace ? "cleanup" : "deleting", + prefixColor: options?.replace ? "magenta" : "redBright", resource: formatFQN(instance[ResourceFQN]), - message: "Deleting Resource...", + message: options?.replace + ? "Cleaning Up Old Resource..." + : "Deleting Resource...", }); } @@ -109,7 +117,7 @@ export async function destroy( id: instance[ResourceID], fqn: instance[ResourceFQN], seq: instance[ResourceSeq], - props: state.props, + props: options?.replace?.props ?? state.props, state, replace: () => { throw new Error("Cannot replace a resource that is being deleted"); @@ -127,10 +135,10 @@ export async function destroy( parent: scope, }, async (scope) => { - nestedScope = scope; + nestedScope = options?.replace?.props == null ? scope : undefined; return await Provider.handler.bind(ctx)( instance[ResourceID], - state.props!, + options?.replace?.props ?? state.props!, ); }, ); @@ -146,14 +154,30 @@ export async function destroy( await destroy(nestedScope, options); } - await scope.delete(instance[ResourceID]); + if (options?.replace == null) { + if (nestedScope) { + await destroy(nestedScope, options); + } + await scope.deleteResource(instance[ResourceID]); + } else { + let pendingDeletions = + await state.output[ResourceScope].get( + "pendingDeletions", + ); + pendingDeletions = pendingDeletions?.filter( + (deletion) => deletion.resource[ResourceID] !== instance[ResourceID], + ); + await scope.set("pendingDeletions", pendingDeletions); + } if (!quiet) { logger.task(instance[ResourceFQN], { - prefix: "deleted", + prefix: options?.replace ? "cleaned" : "deleted", prefixColor: "greenBright", resource: formatFQN(instance[ResourceFQN]), - message: "Deleted Resource", + message: options?.replace + ? "Old Resource Cleanup Complete" + : "Deleted Resource", status: "success", }); } @@ -165,11 +189,14 @@ export async function destroy( export async function destroyAll( resources: Resource[], - options?: DestroyOptions, + options?: DestroyOptions & { force?: boolean }, ) { if (options?.strategy !== "parallel") { const sorted = resources.sort((a, b) => b[ResourceSeq] - a[ResourceSeq]); for (const resource of sorted) { + if (isScope(resource)) { + await resource.destroyPendingDeletions(); + } await destroy(resource, options); } } else { diff --git a/alchemy/src/docker/api.ts b/alchemy/src/docker/api.ts new file mode 100644 index 000000000..c7f3586aa --- /dev/null +++ b/alchemy/src/docker/api.ts @@ -0,0 +1,439 @@ +import { spawn } from "node:child_process"; +import { exec } from "../os/exec.ts"; + +/** + * Options for Docker API requests + */ +export interface DockerApiOptions { + /** + * Custom path to Docker binary + */ + dockerPath?: string; +} + +type VolumeInfo = { + CreatedAt: string; + Driver: string; + Labels: Record; + Mountpoint: string; + Name: string; + Options: Record; + Scope: string; +}; + +/** + * Docker API client that wraps Docker CLI commands + */ +export class DockerApi { + /** Path to Docker CLI */ + readonly dockerPath: string; + + /** + * Create a new Docker API client + * + * @param options Docker API options + */ + constructor(options: DockerApiOptions = {}) { + this.dockerPath = options.dockerPath || "docker"; + } + + /** + * Run a Docker CLI command + * + * @param args Command arguments to pass to Docker CLI + * @returns Result of the command + */ + async exec(args: string[]): Promise<{ stdout: string; stderr: string }> { + const command = `${this.dockerPath} ${args.join(" ")}`; + const result = (await exec(command, { + captureOutput: true, + shell: true, + env: process.env, + })) as { stdout: string; stderr: string }; + + return result; + } + + /** + * Check if Docker daemon is running + * + * @returns True if Docker daemon is running + */ + async isRunning(): Promise { + try { + // Use a quick, lightweight command to test if Docker is running + await this.exec(["version", "--format", "{{.Server.Version}}"]); + return true; + } catch (error) { + console.log( + `Docker daemon not running: ${error instanceof Error ? error.message : String(error)}`, + ); + return false; + } + } + + /** + * Pull Docker image + * + * @param image Image name and tag + * @returns Result of the pull command + */ + async pullImage(image: string): Promise<{ stdout: string; stderr: string }> { + return this.exec(["pull", image]); + } + + /** + * Build Docker image + * + * @param path Path to Dockerfile directory + * @param tag Tag for the image + * @param buildArgs Build arguments + * @returns Result of the build command + */ + async buildImage( + path: string, + tag: string, + buildArgs: Record = {}, + ): Promise<{ stdout: string; stderr: string }> { + const args = ["build", "-t", tag, path]; + + for (const [key, value] of Object.entries(buildArgs)) { + args.push("--build-arg", `${key}=${value}`); + } + + return this.exec(args); + } + + /** + * List Docker images + * + * @returns JSON string containing image list + */ + async listImages(): Promise { + const { stdout } = await this.exec(["images", "--format", "{{json .}}"]); + return stdout; + } + + /** + * Create Docker container + * + * @param image Image name + * @param name Container name + * @param options Container options + * @returns Container ID + */ + async createContainer( + image: string, + name: string, + options: { + ports?: Record; + env?: Record; + volumes?: Record; + cmd?: string[]; + } = {}, + ): Promise { + const args = ["create", "--name", name]; + + // Add port mappings + if (options.ports) { + for (const [hostPort, containerPort] of Object.entries(options.ports)) { + args.push("-p", `${hostPort}:${containerPort}`); + } + } + + // Add environment variables + if (options.env) { + for (const [key, value] of Object.entries(options.env)) { + args.push("-e", `${key}=${value}`); + } + } + + // Add volume mappings + if (options.volumes) { + for (const [hostPath, containerPath] of Object.entries(options.volumes)) { + args.push("-v", `${hostPath}:${containerPath}`); + } + } + + args.push(image); + + // Add command if specified + if (options.cmd && options.cmd.length > 0) { + args.push(...options.cmd); + } + + const { stdout } = await this.exec(args); + return stdout.trim(); + } + + /** + * Start Docker container + * + * @param containerId Container ID or name + */ + async startContainer(containerId: string): Promise { + await this.exec(["start", containerId]); + } + + /** + * Stop Docker container + * + * @param containerId Container ID or name + */ + async stopContainer(containerId: string): Promise { + await this.exec(["stop", containerId]); + } + + /** + * Remove Docker container + * + * @param containerId Container ID or name + * @param force Force removal + */ + async removeContainer(containerId: string, force = false): Promise { + const args = ["rm"]; + if (force) { + args.push("-f"); + } + args.push(containerId); + await this.exec(args); + } + + /** + * Get container logs + * + * @param containerId Container ID or name + * @returns Container logs + */ + async getContainerLogs(containerId: string): Promise { + const { stdout } = await this.exec(["logs", containerId]); + return stdout; + } + + /** + * Check if a container exists + * + * @param containerId Container ID or name + * @returns True if container exists + */ + async containerExists(containerId: string): Promise { + try { + await this.exec(["inspect", containerId]); + return true; + } catch (_error) { + return false; + } + } + + /** + * Create Docker network + * + * @param name Network name + * @param driver Network driver + * @returns Network ID + */ + async createNetwork(name: string, driver = "bridge"): Promise { + const { stdout } = await this.exec([ + "network", + "create", + "--driver", + driver, + name, + ]); + return stdout.trim(); + } + + /** + * Remove Docker network + * + * @param networkId Network ID or name + */ + async removeNetwork(networkId: string): Promise { + await this.exec(["network", "rm", networkId]); + } + + /** + * Connect container to network + * + * @param containerId Container ID or name + * @param networkId Network ID or name + */ + async connectNetwork( + containerId: string, + networkId: string, + options: { + aliases?: string[]; + } = {}, + ): Promise { + const args = ["network", "connect"]; + if (options.aliases) { + for (const alias of options.aliases) { + args.push("--alias", alias); + } + } + args.push(networkId, containerId); + await this.exec(args); + } + + /** + * Disconnect container from network + * + * @param containerId Container ID or name + * @param networkId Network ID or name + */ + async disconnectNetwork( + containerId: string, + networkId: string, + ): Promise { + await this.exec(["network", "disconnect", networkId, containerId]); + } + + /** + * Create Docker volume + * + * @param name Volume name + * @param driver Volume driver + * @param driverOpts Driver options + * @param labels Volume labels + * @returns Volume name + */ + async createVolume( + name: string, + driver = "local", + driverOpts: Record = {}, + labels: Record = {}, + ): Promise { + const args = ["volume", "create", "--name", name, "--driver", driver]; + + // Add driver options + for (const [key, value] of Object.entries(driverOpts)) { + args.push("--opt", `${key}=${value}`); + } + + // Add labels + for (const [key, value] of Object.entries(labels)) { + args.push("--label", `${key}=${value}`); + } + + const { stdout } = await this.exec(args); + return stdout.trim(); + } + + /** + * Remove Docker volume + * + * @param volumeName Volume name + * @param force Force removal of the volume + */ + async removeVolume(volumeName: string, force = false): Promise { + const args = ["volume", "rm"]; + if (force) { + args.push("--force"); + } + args.push(volumeName); + await this.exec(args); + } + + /** + * Get Docker volume information + * + * @param volumeName Volume name + * @returns Volume details in JSON format + */ + async inspectVolume(volumeName: string): Promise { + const { stdout } = await this.exec(["volume", "inspect", volumeName]); + try { + return JSON.parse(stdout.trim()) as VolumeInfo[]; + } catch (_error) { + return []; + } + } + + /** + * Check if a volume exists + * + * @param volumeName Volume name + * @returns True if volume exists + */ + async volumeExists(volumeName: string): Promise { + try { + await this.inspectVolume(volumeName); + return true; + } catch (_error) { + return false; + } + } + + /** + * Login to a Docker registry + * + * @param registry Registry URL + * @param username Username for authentication + * @param password Password for authentication + * @returns Promise that resolves when login is successful + */ + async login( + registry: string, + username: string, + password: string, + ): Promise { + return new Promise((resolve, reject) => { + const args = [ + "login", + registry, + "--username", + username, + "--password-stdin", + ]; + + const child = spawn(this.dockerPath, args, { + stdio: ["pipe", "pipe", "pipe"], + }); + + let stdout = ""; + let stderr = ""; + + child.stdout.on("data", (data) => { + stdout += data.toString(); + }); + + child.stderr.on("data", (data) => { + stderr += data.toString(); + }); + + child.on("close", (code) => { + if (code === 0) { + resolve(); + } else { + reject( + new Error( + `Docker login failed with exit code ${code}: ${stderr || stdout}`, + ), + ); + } + }); + + child.on("error", (err) => { + reject(new Error(`Docker login failed: ${err.message}`)); + }); + + // Write password to stdin and close the stream + child.stdin.write(password); + child.stdin.end(); + }); + } + + /** + * Logout from a Docker registry + * + * @param registry Registry URL + */ + async logout(registry: string): Promise { + try { + await this.exec(["logout", registry]); + } catch (error) { + // Ignore logout errors as they're not critical + console.warn(`Docker logout failed: ${error}`); + } + } +} diff --git a/alchemy/src/docker/container.ts b/alchemy/src/docker/container.ts new file mode 100644 index 000000000..da948066f --- /dev/null +++ b/alchemy/src/docker/container.ts @@ -0,0 +1,267 @@ +import type { Context } from "../context.ts"; +import { Resource } from "../resource.ts"; +import { DockerApi } from "./api.ts"; +import type { Image } from "./image.ts"; +import type { RemoteImage } from "./remote-image.ts"; + +/** + * Port mapping configuration + */ +export interface PortMapping { + /** + * External port on the host + */ + external: number | string; + + /** + * Internal port inside the container + */ + internal: number | string; + + /** + * Protocol (tcp or udp) + */ + protocol?: "tcp" | "udp"; +} + +/** + * Volume mapping configuration + */ +export interface VolumeMapping { + /** + * Host path + */ + hostPath: string; + + /** + * Container path + */ + containerPath: string; + + /** + * Read-only flag + */ + readOnly?: boolean; +} + +/** + * Network mapping configuration + */ +export interface NetworkMapping { + /** + * Network name or ID + */ + name: string; + + /** + * Aliases for the container in the network + */ + aliases?: string[]; +} + +/** + * Properties for creating a Docker container + */ +export interface ContainerProps { + /** + * Image to use for the container + * Can be an Alchemy Image or RemoteImage resource or a string image reference + */ + image: Image | RemoteImage | string; + + /** + * Container name + */ + name?: string; + + /** + * Command to run in the container + */ + command?: string[]; + + /** + * Environment variables + */ + environment?: Record; + + /** + * Port mappings + */ + ports?: PortMapping[]; + + /** + * Volume mappings + */ + volumes?: VolumeMapping[]; + + /** + * Restart policy + */ + restart?: "no" | "always" | "on-failure" | "unless-stopped"; + + /** + * Networks to connect to + */ + networks?: NetworkMapping[]; + + /** + * Whether to remove the container when it exits + */ + removeOnExit?: boolean; + + /** + * Start the container after creation + */ + start?: boolean; +} + +/** + * Docker Container resource + */ +export interface Container + extends Resource<"docker::Container">, + ContainerProps { + /** + * Container ID + */ + id: string; + + /** + * Container state + */ + state?: "created" | "running" | "paused" | "stopped" | "exited"; + + /** + * Time when the container was created + */ + createdAt: number; +} + +/** + * Create and manage a Docker Container + * + * @example + * // Create a simple Nginx container + * const webContainer = await Container("web", { + * image: "nginx:latest", + * ports: [ + * { external: 8080, internal: 80 } + * ], + * start: true + * }); + * + * @example + * // Create a container with environment variables and volume mounts + * const appContainer = await Container("app", { + * image: customImage, // Using an Alchemy RemoteImage resource + * environment: { + * NODE_ENV: "production", + * API_KEY: "secret-key" + * }, + * volumes: [ + * { hostPath: "./data", containerPath: "/app/data" } + * ], + * ports: [ + * { external: 3000, internal: 3000 } + * ], + * restart: "always", + * start: true + * }); + */ +export const Container = Resource( + "docker::Container", + async function ( + this: Context, + id: string, + props: ContainerProps, + ): Promise { + // Initialize Docker API client + const api = new DockerApi(); + + // Get image reference + const imageRef = + typeof props.image === "string" ? props.image : props.image.imageRef; + + // Use provided name or generate one based on resource ID + const containerName = + props.name || `alchemy-${id.replace(/[^a-zA-Z0-9_.-]/g, "-")}`; + + // Handle delete phase + if (this.phase === "delete") { + if (this.output?.id) { + // Stop container if running + await api.stopContainer(this.output.id); + + // Remove container + await api.removeContainer(this.output.id, true); + } + + // Return destroyed state + return this.destroy(); + } else { + let containerState: NonNullable = "created"; + + if (this.phase === "update") { + // Check if container already exists (for update) + const containerExists = await api.containerExists(containerName); + + if (containerExists) { + // Remove existing container for update + await api.removeContainer(containerName, true); + } + } + + // Prepare port mappings + const portMappings: Record = {}; + if (props.ports) { + for (const port of props.ports) { + const protocol = port.protocol || "tcp"; + portMappings[`${port.external}`] = `${port.internal}/${protocol}`; + } + } + + // Prepare volume mappings + const volumeMappings: Record = {}; + if (props.volumes) { + for (const volume of props.volumes) { + const readOnlyFlag = volume.readOnly ? ":ro" : ""; + volumeMappings[volume.hostPath] = + `${volume.containerPath}${readOnlyFlag}`; + } + } + + // Create new container + const containerId = await api.createContainer(imageRef, containerName, { + ports: portMappings, + env: props.environment, + volumes: volumeMappings, + cmd: props.command, + }); + + // Connect to networks if specified + if (props.networks) { + for (const network of props.networks) { + const networkId = + typeof network === "string" ? network : network.name; + await api.connectNetwork(containerId, networkId, { + aliases: network.aliases, + }); + } + } + + // Start container if requested + if (props.start) { + await api.startContainer(containerId); + containerState = "running"; + } + + // Return the resource using this() to construct output + return this({ + ...props, + id: containerId, + state: containerState, + createdAt: Date.now(), + }); + } + }, +); diff --git a/alchemy/src/docker/image.ts b/alchemy/src/docker/image.ts new file mode 100644 index 000000000..20939f2c3 --- /dev/null +++ b/alchemy/src/docker/image.ts @@ -0,0 +1,270 @@ +import fs from "node:fs/promises"; +import path from "node:path"; +import type { Context } from "../context.ts"; +import { Resource } from "../resource.ts"; +import type { Secret } from "../secret.ts"; +import { DockerApi } from "./api.ts"; + +/** + * Options for building a Docker image + */ +export interface DockerBuildOptions { + /** + * Path to the build context directory + * + * @default - the `dirname(dockerfile)` if provided or otherwise `process.cwd()` + */ + context?: string; + + /** + * Path to the Dockerfile, relative to context + * + * @default - `Dockerfile` + */ + dockerfile?: string; + + /** + * Target build platform (e.g., linux/amd64) + */ + platform?: string; + + /** + * Build arguments as key-value pairs + */ + buildArgs?: Record; + + /** + * Target build stage in multi-stage builds + */ + target?: string; + + /** + * List of images to use for cache + */ + cacheFrom?: string[]; +} + +export interface ImageRegistry { + username: string; + password: Secret; + server: string; +} + +/** + * Properties for creating a Docker image + */ +export interface ImageProps { + /** + * Repository name for the image (e.g., "username/image") + */ + name?: string; + + /** + * Tag for the image (e.g., "latest") + */ + tag?: string; + + /** + * Build configuration + */ + build?: DockerBuildOptions; + + /** + * Registry credentials + */ + registry?: ImageRegistry; + + /** + * Whether to skip pushing the image to registry + */ + skipPush?: boolean; +} + +/** + * Docker Image resource + */ +export interface Image extends Resource<"docker::Image">, ImageProps { + /** + * Image name + */ + name: string; + + /** + * Full image reference (name:tag) + */ + imageRef: string; + + /** + * Image ID + */ + imageId?: string; + + /** + * Repository digest if pushed + */ + repoDigest?: string; + + /** + * Time when the image was built + */ + builtAt: number; +} + +/** + * Build and manage a Docker image from a Dockerfile + * + * @example + * // Build a Docker image from a Dockerfile + * const appImage = await Image("app-image", { + * name: "myapp", + * tag: "latest", + * build: { + * context: "./app", + * dockerfile: "Dockerfile", + * buildArgs: { + * NODE_ENV: "production" + * } + * } + * }); + */ +export const Image = Resource( + "docker::Image", + async function ( + this: Context, + id: string, + props: ImageProps, + ): Promise { + // Initialize Docker API client + const api = new DockerApi(); + + if (this.phase === "delete") { + // No action needed for delete as Docker images aren't automatically removed + // This is intentional as other resources might depend on the same image + return this.destroy(); + } else { + // Normalize properties + const tag = props.tag || "latest"; + const name = props.name || id; + const imageRef = `${name}:${tag}`; + + let context: string; + let dockerfile: string; + if (props.build?.dockerfile && props.build?.context) { + context = path.resolve(props.build.context); + dockerfile = path.resolve(context, props.build.dockerfile); + } else if (props.build?.dockerfile) { + context = process.cwd(); + dockerfile = path.resolve(context, props.build.dockerfile); + } else if (props.build?.context) { + context = path.resolve(props.build.context); + dockerfile = path.resolve(context, "Dockerfile"); + } else { + context = process.cwd(); + dockerfile = path.resolve(context, "Dockerfile"); + } + await fs.access(context); + await fs.access(dockerfile); + + // Prepare build options + const buildOptions: Record = props.build?.buildArgs || {}; + + // Add platform if specified + let buildArgs = ["build", "-t", imageRef]; + + if (props.build?.platform) { + buildArgs.push("--platform", props.build.platform); + } + + // Add target if specified + if (props.build?.target) { + buildArgs.push("--target", props.build.target); + } + + // Add cache sources if specified + if (props.build?.cacheFrom && props.build.cacheFrom.length > 0) { + for (const cacheSource of props.build.cacheFrom) { + buildArgs.push("--cache-from", cacheSource); + } + } + + // Add build arguments + for (const [key, value] of Object.entries(buildOptions)) { + buildArgs.push("--build-arg", `${key}="${value}"`); + } + + buildArgs.push("-f", dockerfile); + + // Add context path + buildArgs.push(context); + + // Execute build command + const { stdout } = await api.exec(buildArgs); + + // Extract image ID from build output if available + const imageIdMatch = /Successfully built ([a-f0-9]+)/.exec(stdout); + const imageId = imageIdMatch ? imageIdMatch[1] : undefined; + + // Handle push if required + let repoDigest: string | undefined; + let finalImageRef = imageRef; + if (props.registry && !props.skipPush) { + const { server, username, password } = props.registry; + + // Ensure the registry server does not have trailing slash + const registryHost = server.replace(/\/$/, ""); + + // Determine if the built image already includes a registry host (e.g. ghcr.io/user/repo) + const firstSegment = imageRef.split("/")[0]; + const hasRegistryPrefix = firstSegment.includes("."); + + // Compose the target image reference that will be pushed + const targetImage = hasRegistryPrefix + ? imageRef // already fully-qualified + : `${registryHost}/${imageRef}`; + + try { + // Authenticate to registry + await api.login(registryHost, username, password.unencrypted); + + // Tag local image with fully qualified name if necessary + if (targetImage !== imageRef) { + await api.exec(["tag", imageRef, targetImage]); + } + + // Push the image + const { stdout: pushOut } = await api.exec(["push", targetImage]); + + // Attempt to extract the repo digest from push output + const digestMatch = /digest:\s+([a-z0-9]+:[a-f0-9]{64})/.exec( + pushOut, + ); + if (digestMatch) { + const digestHash = digestMatch[1]; + // Strip tag (anything after last :) to build image@digest reference + const [repoWithoutTag] = + targetImage.split(":").length > 2 + ? [targetImage] // unlikely but safety + : [targetImage.substring(0, targetImage.lastIndexOf(":"))]; + repoDigest = `${repoWithoutTag}@${digestHash}`; + } + + // Update the final image reference to point at the pushed image + finalImageRef = targetImage; + } finally { + // Always try to logout – failures are non-fatal + await api.logout(registryHost); + } + } + + // Return the resource using this() to construct output + return this({ + ...props, + name, + imageRef: finalImageRef, + imageId, + repoDigest, + builtAt: Date.now(), + }); + } + }, +); diff --git a/alchemy/src/docker/index.ts b/alchemy/src/docker/index.ts new file mode 100644 index 000000000..25ce51b82 --- /dev/null +++ b/alchemy/src/docker/index.ts @@ -0,0 +1,6 @@ +export * from "./api.ts"; +export * from "./remote-image.ts"; +export * from "./container.ts"; +export * from "./network.ts"; +export * from "./volume.ts"; +export * from "./image.ts"; diff --git a/alchemy/src/docker/network.ts b/alchemy/src/docker/network.ts new file mode 100644 index 000000000..eeeae735c --- /dev/null +++ b/alchemy/src/docker/network.ts @@ -0,0 +1,101 @@ +import type { Context } from "../context.ts"; +import { Resource } from "../resource.ts"; +import { DockerApi } from "./api.ts"; + +/** + * Properties for creating a Docker network + */ +export interface NetworkProps { + /** + * Network name + */ + name: string; + + /** + * Network driver to use + * @default "bridge" + */ + driver?: "bridge" | "host" | "none" | "overlay" | "macvlan" | (string & {}); + + /** + * Enable IPv6 on the network + * @default false + */ + enableIPv6?: boolean; + + /** + * Network-scoped alias for containers + */ + labels?: Record; +} + +/** + * Docker Network resource + */ +export interface Network extends Resource<"docker::Network">, NetworkProps { + /** + * Network ID + */ + id: string; + + /** + * Time when the network was created + */ + createdAt: number; +} + +/** + * Create and manage a Docker Network + * + * @see https://docs.docker.com/engine/network/ + * + * @example + * // Create a simple bridge network + * const appNetwork = await Network("app-network", { + * name: "app-network" + * }); + * + * @example + * // Create a custom network with driver + * const overlayNetwork = await Network("overlay-network", { + * name: "overlay-network", + * driver: "overlay", + * enableIPv6: true, + * labels: { + * "com.example.description": "Network for application services" + * } + * }); + */ +export const Network = Resource( + "docker::Network", + async function ( + this: Context, + _id: string, + props: NetworkProps, + ): Promise { + // Initialize Docker API client + const api = new DockerApi(); + + // Handle delete phase + if (this.phase === "delete") { + if (this.output?.id) { + // Remove network + await api.removeNetwork(this.output.id); + } + + // Return destroyed state + return this.destroy(); + } else { + // Create the network + props.driver = props.driver || "bridge"; + const networkId = await api.createNetwork(props.name, props.driver); + + // Return the resource using this() to construct output + return this({ + ...props, + id: networkId, + createdAt: Date.now(), + }); + } + }, +); diff --git a/alchemy/src/docker/remote-image.ts b/alchemy/src/docker/remote-image.ts new file mode 100644 index 000000000..a548d2b51 --- /dev/null +++ b/alchemy/src/docker/remote-image.ts @@ -0,0 +1,83 @@ +import type { Context } from "../context.ts"; +import { Resource } from "../resource.ts"; +import { DockerApi } from "./api.ts"; + +/** + * Properties for creating a Docker image + */ +export interface RemoteImageProps { + /** + * Docker image name (e.g., "nginx") + */ + name: string; + + /** + * Tag for the image (e.g., "latest" or "1.19-alpine") + */ + tag?: string; + + /** + * Always attempt to pull the image, even if it exists locally + */ + alwaysPull?: boolean; +} + +/** + * Docker Remote Image resource + */ +export interface RemoteImage + extends Resource<"docker::RemoteImage">, + RemoteImageProps { + /** + * Full image reference (name:tag) + */ + imageRef: string; + + /** + * Time when the image was created or pulled + */ + createdAt: number; +} + +/** + * Create or reference a Docker Remote Image + * + * @example + * // Pull the nginx image + * const nginxImage = await RemoteImage("nginx", { + * name: "nginx", + * tag: "latest" + * }); + * + */ +export const RemoteImage = Resource( + "docker::RemoteImage", + async function ( + this: Context, + _id: string, + props: RemoteImageProps, + ): Promise { + // Initialize Docker API client + const api = new DockerApi(); + + if (this.phase === "delete") { + // No action needed for delete as Docker images aren't automatically removed + // This is intentional as other resources might depend on the same image + return this.destroy(); + } else { + // Normalize properties + const tag = props.tag || "latest"; + const imageRef = `${props.name}:${tag}`; + + // Pull image + await api.pullImage(imageRef); + + // Return the resource using this() to construct output + return this({ + ...props, + imageRef, + createdAt: Date.now(), + }); + } + }, +); diff --git a/alchemy/src/docker/volume.ts b/alchemy/src/docker/volume.ts new file mode 100644 index 000000000..6271ee429 --- /dev/null +++ b/alchemy/src/docker/volume.ts @@ -0,0 +1,154 @@ +import type { Context } from "../context.ts"; +import { Resource } from "../resource.ts"; +import { DockerApi } from "./api.ts"; + +/** + * Interface for volume label + */ +export interface VolumeLabel { + /** + * Label name + */ + name: string; + + /** + * Label value + */ + value: string; +} + +/** + * Properties for creating a Docker volume + */ +export interface VolumeProps { + /** + * Volume name + */ + name: string; + + /** + * Volume driver to use + * @default "local" + */ + driver?: string; + + /** + * Driver-specific options + */ + driverOpts?: Record; + + /** + * Custom metadata labels for the volume + */ + labels?: VolumeLabel[] | Record; +} + +/** + * Docker Volume resource + */ +export interface Volume extends Resource<"docker::Volume">, VolumeProps { + /** + * Volume ID (same as name for Docker volumes) + */ + id: string; + + /** + * Volume mountpoint path on the host + */ + mountpoint?: string; + + /** + * Time when the volume was created + */ + createdAt: number; +} + +/** + * Create and manage a Docker Volume + * + * @see https://docs.docker.com/engine/reference/commandline/volume/ + * + * @example + * // Create a simple Docker volume + * const dataVolume = await Volume("data-volume", { + * name: "data-volume" + * }); + * + * @example + * // Create a Docker volume with custom driver and options + * const dbVolume = await Volume("db-data", { + * name: "db-data", + * driver: "local", + * driverOpts: { + * "type": "nfs", + * "o": "addr=10.0.0.1,rw", + * "device": ":/path/to/dir" + * }, + * labels: [ + * { name: "com.example.usage", value: "database-storage" }, + * { name: "com.example.backup", value: "weekly" } + * ] + * }); + */ +export const Volume = Resource( + "docker::Volume", + async function ( + this: Context, + _id: string, + props: VolumeProps, + ): Promise { + // Initialize Docker API client + const api = new DockerApi(); + + // Process labels to ensure consistent format + const processedLabels: Record = {}; + if (props.labels) { + if (Array.isArray(props.labels)) { + // Convert array of label objects to Record + for (const label of props.labels) { + processedLabels[label.name] = label.value; + } + } else { + // Use Record directly + Object.assign(processedLabels, props.labels); + } + } + + // Handle delete phase + if (this.phase === "delete") { + if (this.output?.name) { + // Remove volume + await api.removeVolume(this.output.name); + } + + // Return destroyed state + return this.destroy(); + } else { + // Set default driver if not provided + props.driver = props.driver || "local"; + const driverOpts = props.driverOpts || {}; + + // Create the volume + const volumeName = await api.createVolume( + props.name, + props.driver, + driverOpts, + processedLabels, + ); + + // Get volume details to retrieve mountpoint + const volumeInfos = await api.inspectVolume(volumeName); + const mountpoint = volumeInfos[0].Mountpoint; + + // Return the resource using this() to construct output + return this({ + ...props, + id: volumeName, + mountpoint, + createdAt: Date.now(), + labels: Array.isArray(props.labels) ? props.labels : undefined, + driverOpts: props.driverOpts, + }); + } + }, +); diff --git a/alchemy/src/fs/file-system-state-store.ts b/alchemy/src/fs/file-system-state-store.ts index 2ae05bb10..69c0d9fa1 100644 --- a/alchemy/src/fs/file-system-state-store.ts +++ b/alchemy/src/fs/file-system-state-store.ts @@ -7,6 +7,7 @@ import { deserializeState, type State, type StateStore } from "../state.ts"; import { ignore } from "../util/ignore.ts"; const stateRootDir = path.join(process.cwd(), ".alchemy"); +const ALCHEMY_SEPERATOR_CHAR = process.platform === "win32" ? "-" : ":"; export class FileSystemStateStore implements StateStore { public readonly dir: string; @@ -128,7 +129,10 @@ export class FileSystemStateStore implements StateStore { throw new Error(`ID cannot include colons: ${key}`); } if (key.includes("/")) { - key = key.replaceAll("/", ":"); + //todo(michael): remove this next time we do a breaking change + //* windows doesn't support ":" in file paths, but we already use ":" + //* so now we use both to prevent breaking changes` + key = key.replaceAll("/", ALCHEMY_SEPERATOR_CHAR); } return path.join(this.dir, `${key}.json`); } diff --git a/alchemy/src/fs/file.ts b/alchemy/src/fs/file.ts index cd011273e..eb73d3f80 100644 --- a/alchemy/src/fs/file.ts +++ b/alchemy/src/fs/file.ts @@ -184,9 +184,12 @@ export const File = Resource( } // Create directory and write file - await fs.promises.mkdir(path.dirname(filePath), { - recursive: true, - }); + const dirName = path.dirname(filePath); + if (dirName !== ".") { + await fs.promises.mkdir(dirName, { + recursive: true, + }); + } await fs.promises.writeFile(filePath, props.content); diff --git a/alchemy/src/github/index.ts b/alchemy/src/github/index.ts index d03e663eb..63db3dc00 100644 --- a/alchemy/src/github/index.ts +++ b/alchemy/src/github/index.ts @@ -1,3 +1,4 @@ export * from "./comment.ts"; export * from "./repository-environment.ts"; +export * from "./repository-webhook.ts"; export * from "./secret.ts"; diff --git a/alchemy/src/github/repository-webhook.ts b/alchemy/src/github/repository-webhook.ts new file mode 100644 index 000000000..490435d99 --- /dev/null +++ b/alchemy/src/github/repository-webhook.ts @@ -0,0 +1,269 @@ +import type { Context } from "../context.ts"; +import { Resource } from "../resource.ts"; +import { logger } from "../util/logger.ts"; +import { createGitHubClient, verifyGitHubAuth } from "./client.ts"; + +/** + * Properties for creating or updating a GitHub Repository Webhook + */ +export interface RepositoryWebhookProps { + /** + * Repository owner (user or organization) + */ + owner: string; + + /** + * Repository name + */ + repository: string; + + /** + * The URL to which the payloads will be delivered + */ + url: string; + + /** + * Webhook secret for payload validation + * @default undefined + */ + secret?: string; + + /** + * The media type used to serialize the payloads + * @default "application/json" + */ + contentType?: "application/json" | "application/x-www-form-urlencoded"; + + /** + * Determines whether the SSL certificate of the host for url will be verified + * @default false + */ + insecureSsl?: boolean; + + /** + * Determines if notifications are sent when the webhook is triggered + * @default true + */ + active?: boolean; + + /** + * Determines what events the hook is triggered for + * @default ["push"] + */ + events?: string[]; + + /** + * Optional GitHub API token (overrides environment variable) + * If not provided, will use GITHUB_TOKEN environment variable + * @default process.env.GITHUB_TOKEN + */ + token?: string; +} + +/** + * Output returned after Repository Webhook creation/update + */ +export interface RepositoryWebhook + extends Resource<"github::RepositoryWebhook">, + RepositoryWebhookProps { + /** + * The ID of the resource + */ + id: string; + + /** + * The numeric ID of the webhook in GitHub + */ + webhookId: number; + + /** + * The webhook URL that was configured + */ + url: string; + + /** + * Time at which the object was created + */ + createdAt: string; + + /** + * Time at which the object was last updated + */ + updatedAt: string; + + /** + * The ping URL for the webhook + */ + pingUrl: string; + + /** + * The test URL for the webhook + */ + testUrl: string; +} + +/** + * Resource for managing GitHub repository webhooks + * + * Webhooks allow external services to be notified when certain events happen in a repository. + * This resource manages the full lifecycle of repository webhooks including creation, updates, and deletion. + * + * @example + * // Create a basic webhook for push events + * const pushWebhook = await RepositoryWebhook("push-webhook", { + * owner: "my-org", + * repository: "my-repo", + * url: "https://my-service.com/github-webhook", + * events: ["push"] + * }); + * + * @example + * // Create a webhook with secret validation for multiple events + * const ciWebhook = await RepositoryWebhook("ci-webhook", { + * owner: "my-org", + * repository: "my-repo", + * url: "https://ci.example.com/webhook", + * secret: "my-webhook-secret", + * events: ["push", "pull_request", "release"], + * contentType: "application/json" + * }); + * + * @example + * // Create a webhook for all events with custom SSL settings + * const monitoringWebhook = await RepositoryWebhook("monitoring-webhook", { + * owner: "my-org", + * repository: "my-repo", + * url: "https://monitoring.internal.com/github", + * secret: "super-secret-key", + * events: ["*"], // All events + * insecureSsl: true, // For internal services with self-signed certs + * contentType: "application/x-www-form-urlencoded" + * }); + */ +export const RepositoryWebhook = Resource( + "github::RepositoryWebhook", + async function ( + this: Context, + _id: string, + props: RepositoryWebhookProps, + ): Promise { + // Create authenticated Octokit client + const octokit = await createGitHubClient({ + token: props.token, + }); + + // Verify authentication and permissions + if (!this.quiet) { + await verifyGitHubAuth(octokit, props.owner, props.repository); + } + + if (this.phase === "delete") { + if (this.output?.webhookId) { + try { + // Delete the webhook + await octokit.rest.repos.deleteWebhook({ + owner: props.owner, + repo: props.repository, + hook_id: this.output.webhookId, + }); + } catch (error: any) { + // Ignore 404 errors (webhook already deleted) + if (error.status === 404) { + logger.log("Webhook doesn't exist, ignoring"); + } else { + throw error; + } + } + } + + // Return void (a deleted resource has no content) + return this.destroy(); + } + + try { + const webhookConfig = { + url: props.url, + content_type: props.contentType || "application/json", + insecure_ssl: props.insecureSsl ? "1" : "0", + ...(props.secret && { secret: props.secret }), + }; + + const events = props.events || ["push"]; + const active = props.active !== false; // Default to true + + let webhookData; + let webhookId: number; + + if (this.phase === "update" && this.output?.webhookId) { + // Update existing webhook + const { data: updatedWebhook } = await octokit.rest.repos.updateWebhook( + { + owner: props.owner, + repo: props.repository, + hook_id: this.output.webhookId, + config: webhookConfig, + events, + active, + }, + ); + + webhookData = updatedWebhook; + webhookId = this.output.webhookId; + } else { + // Create new webhook + const { data: createdWebhook } = await octokit.rest.repos.createWebhook( + { + owner: props.owner, + repo: props.repository, + name: "web", // GitHub webhook type + config: webhookConfig, + events, + active, + }, + ); + + webhookData = createdWebhook; + webhookId = createdWebhook.id; + } + + // Return webhook details + return this({ + id: `${props.owner}/${props.repository}/webhook/${webhookId}`, + webhookId, + owner: props.owner, + repository: props.repository, + url: props.url, + secret: props.secret, + contentType: props.contentType || "application/json", + insecureSsl: props.insecureSsl, + active: props.active, + events: props.events || ["push"], + token: props.token, + createdAt: webhookData.created_at, + updatedAt: webhookData.updated_at, + pingUrl: webhookData.ping_url, + testUrl: webhookData.test_url, + }); + } catch (error: any) { + if ( + error.status === 403 && + error.message?.includes("Must have admin rights") + ) { + logger.error( + "\n⚠️ Error creating/updating GitHub webhook: You must have admin rights to the repository.", + ); + logger.error( + "Make sure your GitHub token has the required permissions (repo scope for private repos).\n", + ); + } else if (error.status === 422) { + logger.error( + "\n⚠️ Error creating/updating GitHub webhook: Invalid webhook configuration.", + ); + logger.error("Check your webhook URL and event configuration.\n"); + } else { + logger.error("Error creating/updating GitHub webhook:", error.message); + } + throw error; + } + }, +); diff --git a/alchemy/src/os/exec.ts b/alchemy/src/os/exec.ts index 30968a83a..2b64e0041 100644 --- a/alchemy/src/os/exec.ts +++ b/alchemy/src/os/exec.ts @@ -266,30 +266,72 @@ const defaultOptions: SpawnOptions = { shell: true, }; +/** + * Options for exec function + */ +export interface ExecOptions extends Partial { + /** + * Whether to capture stdout and stderr + * @default false + */ + captureOutput?: boolean; +} + /** * Execute a shell command. + * + * @param command The command to execute + * @param options Options for the command execution + * @returns Promise that resolves when the command completes. + * If captureOutput is true, resolves with { stdout, stderr } strings. */ export async function exec( command: string, - options?: Partial, -): Promise { + options?: ExecOptions, +): Promise<{ stdout: string; stderr: string } | undefined> { const [cmd, ...args] = command.split(/\s+/); + const captureOutput = options?.captureOutput === true; return new Promise((resolve, reject) => { - const child = spawn(cmd, args, { + // Set stdio to pipe only if we're capturing output + const spawnOptions = { ...defaultOptions, ...options, env: { ...defaultOptions.env, ...options?.env, }, - }); + stdio: captureOutput ? "pipe" : defaultOptions.stdio, + }; + + const child = spawn(cmd, args, spawnOptions); + + let stdout = ""; + let stderr = ""; + + if (captureOutput) { + child.stdout?.on("data", (data) => { + stdout += data.toString(); + }); + + child.stderr?.on("data", (data) => { + stderr += data.toString(); + }); + } child.on("close", (code) => { if (code === 0) { - resolve(); + if (captureOutput) { + resolve({ stdout, stderr }); + } else { + resolve(undefined); + } } else { - reject(new Error(`Command failed with exit code ${code}`)); + reject( + new Error( + `Command failed with exit code ${code}${stderr ? `: ${stderr}` : ""}`, + ), + ); } }); diff --git a/alchemy/src/resource.ts b/alchemy/src/resource.ts index 2a63de59d..1e06e60f5 100644 --- a/alchemy/src/resource.ts +++ b/alchemy/src/resource.ts @@ -2,11 +2,20 @@ import { apply } from "./apply.ts"; import type { Context } from "./context.ts"; import { Scope as _Scope, type Scope } from "./scope.ts"; -export const PROVIDERS: Map> = new Map< +declare global { + var ALCHEMY_PROVIDERS: Map>; + var ALCHEMY_DYNAMIC_RESOURCE_RESOLVERS: DynamicResourceResolver[]; +} + +export const PROVIDERS: Map< + ResourceKind, + Provider +> = (globalThis.ALCHEMY_PROVIDERS ??= new Map< ResourceKind, Provider ->(); -const DYNAMIC_RESOURCE_RESOLVERS: DynamicResourceResolver[] = []; +>()); +const DYNAMIC_RESOURCE_RESOLVERS: DynamicResourceResolver[] = + (globalThis.ALCHEMY_DYNAMIC_RESOURCE_RESOLVERS ??= []); export type DynamicResourceResolver = ( typeName: string, diff --git a/alchemy/src/scope.ts b/alchemy/src/scope.ts index e336d0512..ff304917c 100644 --- a/alchemy/src/scope.ts +++ b/alchemy/src/scope.ts @@ -1,16 +1,33 @@ import { AsyncLocalStorage } from "node:async_hooks"; +import util from "node:util"; import type { Phase } from "./alchemy.ts"; -import { destroyAll } from "./destroy.ts"; +import { destroy, destroyAll } from "./destroy.ts"; import { FileSystemStateStore } from "./fs/file-system-state-store.ts"; -import { ResourceID, type PendingResource } from "./resource.ts"; -import type { StateStore, StateStoreType } from "./state.ts"; +import { + ResourceFQN, + ResourceID, + ResourceKind, + ResourceScope, + ResourceSeq, + type PendingResource, + type Resource, + type ResourceProps, +} from "./resource.ts"; +import type { State, StateStore, StateStoreType } from "./state.ts"; import { createDummyLogger, createLoggerInstance, type LoggerApi, } from "./util/cli.ts"; +import { AsyncMutex } from "./util/mutex.ts"; import type { ITelemetryClient } from "./util/telemetry/client.ts"; +export class RootScopeStateAttemptError extends Error { + constructor() { + super("Root scope cannot contain state"); + } +} + export interface ScopeOptions { appName?: string; stage?: string; @@ -20,10 +37,16 @@ export interface ScopeOptions { stateStore?: StateStoreType; quiet?: boolean; phase?: Phase; + dev?: boolean; telemetryClient?: ITelemetryClient; logger?: LoggerApi; } +export type PendingDeletions = Array<{ + resource: Resource; + oldProps?: ResourceProps; +}>; + // TODO: support browser const DEFAULT_STAGE = process.env.ALCHEMY_STAGE ?? process.env.USER ?? "dev"; @@ -47,7 +70,7 @@ export class Scope { new AsyncLocalStorage()); public static globals: Scope[] = (globalThis.__ALCHEMY_GLOBALS__ ??= []); - public static get(): Scope | undefined { + public static getScope(): Scope | undefined { const scope = Scope.storage.getStore(); if (!scope) { if (Scope.globals.length > 0) { @@ -63,7 +86,7 @@ export class Scope { } public static get current(): Scope { - const scope = Scope.get(); + const scope = Scope.getScope(); if (!scope) throw new Error("Not running within an Alchemy Scope"); return scope; } @@ -79,8 +102,10 @@ export class Scope { public readonly stateStore: StateStoreType; public readonly quiet: boolean; public readonly phase: Phase; + public readonly dev?: boolean; public readonly logger: LoggerApi; public readonly telemetryClient: ITelemetryClient; + public readonly dataMutex: AsyncMutex; private isErrored = false; private finalized = false; @@ -89,14 +114,14 @@ export class Scope { private deferred: (() => Promise)[] = []; constructor(options: ScopeOptions) { - this.appName = options.appName; + this.appName = options.appName ?? options.parent?.appName; this.scopeName = options.scopeName ?? null; if (this.scopeName?.includes(":")) { throw new Error( `Scope name ${this.scopeName} cannot contain double colons`, ); } - this.parent = options.parent ?? Scope.get(); + this.parent = options.parent ?? Scope.getScope(); this.stage = options?.stage ?? this.parent?.stage ?? DEFAULT_STAGE; this.parent?.children.set(this.scopeName!, this); this.quiet = options.quiet ?? this.parent?.quiet ?? false; @@ -121,6 +146,14 @@ export class Scope { options.logger, ); + this.dev = options.dev ?? this.parent?.dev ?? false; + + if (this.dev) { + this.logger.warnOnce( + "Local development mode is in beta. Please report any issues to https://github.com/sam-goodwin/alchemy/issues.", + ); + } + this.stateStore = options.stateStore ?? this.parent?.stateStore ?? @@ -131,6 +164,7 @@ export class Scope { } this.telemetryClient = options.telemetryClient ?? this.parent?.telemetryClient!; + this.dataMutex = new AsyncMutex(); } public get root(): Scope { @@ -141,7 +175,7 @@ export class Scope { return root; } - public async delete(resourceID: ResourceID) { + public async deleteResource(resourceID: ResourceID) { await this.state.delete(resourceID); this.resources.delete(resourceID); } @@ -158,7 +192,7 @@ export class Scope { if (this.parent) { return [...this.parent.chain, ...thisScope]; } - return [...app, this.stage, ...thisScope]; + return [...app, ...thisScope]; } public fail() { @@ -179,10 +213,86 @@ export class Scope { return [...this.chain, resourceID].join("/"); } + /** + * Centralizes the "lock → locate the right scope → hand the caller a live + * ScopeState instance and a persist() helper". + * + * @param fn Your operation on the scope state. + * • `state` is already resolved and, if we're at the root, created. + * • `persist` will write the (possibly-mutated) state back. + */ + private async withScopeState( + fn: ( + state: State>, // current state for this.scopeName + persist: ( + next: State>, + ) => Promise, // helper to save changes + ) => Promise, + ): Promise { + return this.dataMutex.lock(async () => { + // 1. We must know where to look. + if (!this.parent || !this.scopeName) { + throw new RootScopeStateAttemptError(); + } + + // 2. Pull (or lazily create) the state bucket we care about. + const isRoot = this.parent.scopeName === this.root.scopeName; + const state = + (await this.parent.state.get(this.scopeName)) ?? + (isRoot + ? { + //todo(michael): should this have a different type cause its root? + kind: "alchemy::Scope", + id: this.scopeName!, + fqn: this.root.fqn(this.scopeName!), + seq: this.seq(), + status: "created", + data: {}, + output: { + [ResourceID]: this.scopeName!, + [ResourceFQN]: this.root.fqn(this.scopeName!), + [ResourceKind]: "alchemy::Scope", + [ResourceScope]: this, + [ResourceSeq]: this.seq(), + }, + props: {}, + } + : undefined); + + if (!state) throw new RootScopeStateAttemptError(); + + return fn(state, (updated) => + this.parent!.state.set(this.scopeName!, updated), + ); + }); + } + + public async set(key: string, value: T): Promise { + return this.withScopeState(async (state, persist) => { + state.data[key] = value; + await persist(state); // only one line to save! + }); + } + + public async get(key: string): Promise { + return this.withScopeState(async (state) => state.data[key]); + } + + public async delete(key: string): Promise { + return this.withScopeState(async (state, persist) => { + delete state.data[key]; + await persist(state); + }); + } + public async run(fn: (scope: Scope) => Promise): Promise { return Scope.storage.run(this, () => fn(this)); } + [util.inspect.custom]() { + return `Scope(${this.chain.join("/")})`; + } + [Symbol.asyncDispose]() { return this.finalize(); } @@ -198,7 +308,11 @@ export class Scope { return null; } - public async finalize() { + public async finalize(force?: boolean) { + const shouldForce = + force || + this.parent === undefined || + this?.parent?.scopeName === this.root.scopeName; if (this.phase === "read") { this.rootTelemetryClient?.record({ event: "app.success", @@ -206,7 +320,7 @@ export class Scope { }); return; } - if (this.finalized) { + if (this.finalized && !shouldForce) { return; } if (this.parent === undefined && Scope.globals.length > 0) { @@ -227,12 +341,32 @@ export class Scope { const orphanIds = Array.from( resourceIds.filter((id) => !aliveIds.has(id)), ); + + if (shouldForce) { + await this.destroyPendingDeletions(); + await Promise.all( + Array.from(this.children.values()).map((child) => + child.finalize(shouldForce), + ), + ); + } + const orphans = await Promise.all( orphanIds.map(async (id) => (await this.state.get(id))!.output), + ).then((orphans) => + orphans.filter( + (orphan) => + //we never want to mark the stage scope as an orphan + !( + orphan[ResourceKind] === "alchemy::Scope" && + orphan[ResourceFQN] === this.root.fqn(this.stage) + ), + ), ); await destroyAll(orphans, { quiet: this.quiet, strategy: "sequential", + force: shouldForce, }); this.rootTelemetryClient?.record({ event: "app.success", @@ -250,6 +384,31 @@ export class Scope { await this.rootTelemetryClient?.finalize(); } + public async destroyPendingDeletions() { + const pendingDeletions = + (await this.get("pendingDeletions").catch((e) => { + if (e instanceof RootScopeStateAttemptError) { + return []; + } + throw e; + })) ?? []; + if (pendingDeletions) { + for (const { resource, oldProps } of pendingDeletions) { + //todo(michael): ugly hack due to the way scope is serialized + const realResource = this.resources.get(resource[ResourceID])!; + resource[ResourceScope] = realResource?.[ResourceScope] ?? this; + await destroy(resource, { + quiet: this.quiet, + strategy: "sequential", + replace: { + props: oldProps, + output: resource, + }, + }); + } + } + } + /** * Defers execution of a function until the Alchemy application finalizes. */ diff --git a/alchemy/src/sentry/project.ts b/alchemy/src/sentry/project.ts index 7f6b91f32..f55fb82bc 100644 --- a/alchemy/src/sentry/project.ts +++ b/alchemy/src/sentry/project.ts @@ -347,7 +347,7 @@ export const Project = Resource( } if (!response.ok) { - throw new Error(`API error: ${response.statusText}`); + throw new Error(`API error: ${await response.text()}`); } const data = (await response.json()) as Omit< diff --git a/alchemy/src/stripe/price.ts b/alchemy/src/stripe/price.ts index ff9338faa..4cd7486ac 100644 --- a/alchemy/src/stripe/price.ts +++ b/alchemy/src/stripe/price.ts @@ -32,6 +32,12 @@ export interface PriceRecurring { * `last_ever` for picking the last usage record ever (across period bounds) or `max` which picks the usage record with the maximum reported usage during a period. */ aggregateUsage?: Stripe.PriceCreateParams.Recurring.AggregateUsage; + + /** + * The ID of the billing meter this price is associated with. + * Only applicable when usageType = 'metered'. + */ + meter?: string; } type TaxBehavior = Stripe.PriceCreateParams.TaxBehavior; @@ -320,6 +326,25 @@ export interface Price extends Resource<"stripe::Price">, PriceProps { * } * ] * }); + * + * @example + * // Create a metered price with billing meter for API usage tracking + * const meteredPrice = await Price("api-usage-price", { + * product: "prod_xyz", + * currency: "usd", + * billingScheme: "tiered", + * tiersMode: "graduated", + * recurring: { + * interval: "month", + * usageType: "metered", + * meter: "meter_123abc" // Associate with billing meter + * }, + * tiers: [ + * { upTo: 10000, unitAmountDecimal: "0" }, + * { upTo: 25000, unitAmountDecimal: "0.002" }, + * { upTo: "inf", flatAmountDecimal: "3000" } + * ] + * }); */ export const Price = Resource( "stripe::Price", @@ -370,6 +395,9 @@ export const Price = Resource( expand: ["tiers"], }; + // Note: Stripe doesn't allow updating recurring fields (including meter) after price creation + // If meter needs to be changed, a new price must be created + price = await stripe.prices.update(this.output.id, updateParams); } else { // Create new price @@ -401,6 +429,21 @@ export const Price = Resource( usage_type: props.recurring.usageType, aggregate_usage: props.recurring.aggregateUsage, }; + + // Add meter to recurring if present (only for metered usage type) + if (props.recurring.meter) { + if (props.recurring.usageType !== "metered") { + throw new Error( + "Meter can only be set for prices with recurring.usageType = 'metered'", + ); + } + // Extend the recurring params with meter + ( + createParams.recurring as Stripe.PriceCreateParams.Recurring & { + meter: string; + } + ).meter = props.recurring.meter; + } } // Add tier configuration if present @@ -452,7 +495,7 @@ export const Price = Resource( // Need to retrieve with expanded tiers price = await stripe.prices.retrieve(existingPrices.data[0].id, { expand: ["tiers"], - } as any); + }); } } else { price = await stripe.prices.create(createParams); @@ -472,6 +515,9 @@ export const Price = Resource( .usage_type as Stripe.PriceCreateParams.Recurring.UsageType, aggregateUsage: price.recurring .aggregate_usage as Stripe.PriceCreateParams.Recurring.AggregateUsage, + meter: ( + price.recurring as Stripe.Price.Recurring & { meter?: string } + ).meter, } : undefined; diff --git a/alchemy/src/util/assert-never.ts b/alchemy/src/util/assert-never.ts index b5c390dee..0c382b675 100644 --- a/alchemy/src/util/assert-never.ts +++ b/alchemy/src/util/assert-never.ts @@ -1,3 +1,3 @@ -export function assertNever(value: never): never { - throw new Error(`Unexpected value: ${value}`); +export function assertNever(value: never, message?: string): never { + throw new Error(message ?? `Unexpected value: ${value}`); } diff --git a/alchemy/src/util/cli.ts b/alchemy/src/util/cli.ts index 5f25eb317..dd21d060d 100644 --- a/alchemy/src/util/cli.ts +++ b/alchemy/src/util/cli.ts @@ -2,6 +2,10 @@ import packageJson from "../../package.json" with { type: "json" }; import type { Phase } from "../alchemy.ts"; import { dedent } from "./dedent.ts"; +declare global { + var _ALCHEMY_WARNINGS: Set | undefined; +} + // ANSI color codes const colors = { reset: "\x1b[0m", @@ -17,9 +21,7 @@ type ColorName = keyof typeof colors; // Check if colors should be disabled const shouldDisableColors = (): boolean => { - return Boolean( - process.env.CI || process.env.NO_COLOR || !process.stdout.isTTY, - ); + return Boolean(process.env.NO_COLOR); }; // Apply color if colors are enabled @@ -30,27 +32,28 @@ const colorize = (text: string, color: ColorName): string => { return `${colors[color]}${text}${colors.reset}`; }; -export type Task = { +export interface Task { prefix?: string; - prefixColor?: string; + prefixColor?: ColorName; resource?: string; message: string; status?: "pending" | "success" | "failure"; -}; +} -export type LoggerApi = { +export interface LoggerApi { log: (...args: unknown[]) => void; warn: (...args: unknown[]) => void; + warnOnce: (message: string) => void; error: (...args: unknown[]) => void; task: (id: string, data: Task) => void; exit: () => void; -}; +} -type AlchemyInfo = { +interface AlchemyInfo { phase: Phase; stage: string; appName: string; -}; +} let loggerApi: LoggerApi | null = null; export const createLoggerInstance = ( @@ -69,6 +72,7 @@ export const createDummyLogger = (): LoggerApi => { log: () => {}, error: () => {}, warn: () => {}, + warnOnce: () => {}, task: () => {}, exit: () => {}, }; @@ -89,6 +93,12 @@ export const createFallbackLogger = (alchemyInfo: AlchemyInfo): LoggerApi => { console.error(colorize("ERROR", "redBright"), ...args), warn: (...args: unknown[]) => console.warn(colorize("WARN", "yellowBright"), ...args), + warnOnce: (message: string) => { + globalThis._ALCHEMY_WARNINGS ??= new Set(); + if (globalThis._ALCHEMY_WARNINGS.has(message)) return; + globalThis._ALCHEMY_WARNINGS.add(message); + console.warn(colorize("WARN", "yellowBright"), message); + }, task: (_id: string, data: Task) => { const prefix = data.prefix ? `[${data.prefix}]` : ""; diff --git a/alchemy/src/util/deferred-promise.ts b/alchemy/src/util/deferred-promise.ts new file mode 100644 index 000000000..4f7412daa --- /dev/null +++ b/alchemy/src/util/deferred-promise.ts @@ -0,0 +1,19 @@ +export class DeferredPromise { + private promise = Promise.withResolvers(); + + status: "pending" | "fulfilled" | "rejected" = "pending"; + + get value() { + return this.promise.promise; + } + + resolve(value: T) { + this.status = "fulfilled"; + this.promise.resolve(value); + } + + reject(reason?: any) { + this.status = "rejected"; + this.promise.reject(reason); + } +} diff --git a/alchemy/src/util/detect-package-manager.ts b/alchemy/src/util/detect-package-manager.ts new file mode 100644 index 000000000..53c7df8c4 --- /dev/null +++ b/alchemy/src/util/detect-package-manager.ts @@ -0,0 +1,23 @@ +import fs from "node:fs"; + +export type PackageManager = "bun" | "pnpm" | "yarn" | "npm"; + +export function detectPackageManager(): PackageManager { + if (fs.existsSync("bun.lockb")) return "bun"; + if (fs.existsSync("pnpm-lock.yaml")) return "pnpm"; + if (fs.existsSync("yarn.lock")) return "yarn"; + + if (process.env.npm_execpath?.includes("bun")) { + return "bun"; + } + + const userAgent = process.env.npm_config_user_agent; + if (userAgent) { + if (userAgent.startsWith("bun")) return "bun"; + if (userAgent.startsWith("pnpm")) return "pnpm"; + if (userAgent.startsWith("yarn")) return "yarn"; + if (userAgent.startsWith("npm")) return "npm"; + } + + return "npm"; +} diff --git a/alchemy/src/util/exists.ts b/alchemy/src/util/exists.ts new file mode 100644 index 000000000..70b3f0f96 --- /dev/null +++ b/alchemy/src/util/exists.ts @@ -0,0 +1,10 @@ +import { access } from "node:fs/promises"; + +export async function exists(path: string): Promise { + try { + await access(path); + return true; + } catch { + return false; + } +} diff --git a/alchemy/src/util/find-open-port.ts b/alchemy/src/util/find-open-port.ts new file mode 100644 index 000000000..bfec7fc08 --- /dev/null +++ b/alchemy/src/util/find-open-port.ts @@ -0,0 +1,27 @@ +import net from "node:net"; + +export async function findOpenPort(min = 1337, max = 65535): Promise { + for (let port = min; port <= max; port++) { + if (await isPortAvailable("0.0.0.0", port)) { + return port; + } + } + throw new Error(`No open port found between ${min} and ${max}`); +} + +async function isPortAvailable(host: string, port: number): Promise { + return new Promise((resolve) => { + const server = net.createServer(); + server.unref(); + + server.on("error", () => { + // Port is in use + resolve(false); + }); + + server.listen(port, host, () => { + // Port is available + server.close(() => resolve(true)); + }); + }); +} diff --git a/alchemy/src/util/logger.ts b/alchemy/src/util/logger.ts index 6221b2fbc..579ec7620 100644 --- a/alchemy/src/util/logger.ts +++ b/alchemy/src/util/logger.ts @@ -4,11 +4,12 @@ import type { LoggerApi } from "./cli.ts"; export const logger = new Proxy({} as LoggerApi, { get: (_, prop: keyof LoggerApi) => { const logger = - Scope.get()?.logger ?? + Scope.getScope()?.logger ?? ({ log: console.log, error: console.error, warn: console.warn, + warnOnce: console.warn, task: () => {}, exit: () => {}, } as LoggerApi); diff --git a/alchemy/src/util/memoize.ts b/alchemy/src/util/memoize.ts new file mode 100644 index 000000000..d935e5431 --- /dev/null +++ b/alchemy/src/util/memoize.ts @@ -0,0 +1,23 @@ +type AsyncReturnType = T extends (...args: any[]) => Promise + ? R + : T; + +export function memoize Promise>( + fn: F, + keyFn: (...args: Parameters) => string, +) { + const cache = new Map>>(); + return async (...args: Parameters): Promise> => { + const key = keyFn(...args); + const cached = cache.get(key); + if (cached) { + return await cached; + } + const promise = fn(...args).catch((error) => { + cache.delete(key); + throw error; + }); + cache.set(key, promise); + return await promise; + }; +} diff --git a/alchemy/src/util/mutex.ts b/alchemy/src/util/mutex.ts new file mode 100644 index 000000000..2cbe6a8a0 --- /dev/null +++ b/alchemy/src/util/mutex.ts @@ -0,0 +1,33 @@ +/** + * Simple async mutex that ensures only one async operation executes at a time + */ +export class AsyncMutex { + private locked = false; + private queue: Array<() => void> = []; + + async lock(operation: () => Promise): Promise { + return new Promise((resolve, reject) => { + const execute = async () => { + this.locked = true; + try { + const result = await operation(); + resolve(result); + } catch (error) { + reject(error); + } finally { + this.locked = false; + const next = this.queue.shift(); + if (next) { + next(); + } + } + }; + + if (this.locked) { + this.queue.push(execute); + } else { + execute(); + } + }); + } +} diff --git a/alchemy/src/util/safe-fetch.ts b/alchemy/src/util/safe-fetch.ts index 0a8661b15..76bdede05 100644 --- a/alchemy/src/util/safe-fetch.ts +++ b/alchemy/src/util/safe-fetch.ts @@ -4,7 +4,7 @@ import { logger } from "./logger.ts"; export async function safeFetch( url: string | URL | Request, options: RequestInit = {}, - retries = 3, + retries = 10, ) { let latestErr: any; for (let attempt = 1; attempt <= retries; attempt++) { diff --git a/alchemy/templates/astro/.gitignore b/alchemy/templates/astro/.gitignore new file mode 100644 index 000000000..5c1a9a98f --- /dev/null +++ b/alchemy/templates/astro/.gitignore @@ -0,0 +1,27 @@ +# build output +dist/ + +# generated types +.astro/ + +# dependencies +node_modules/ + +# logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# environment variables +.env +.env.production + +# macOS-specific files +.DS_Store + +# jetbrains setting folder +.idea/ + +.alchemy/ +.env \ No newline at end of file diff --git a/alchemy/templates/astro/.vscode/extensions.json b/alchemy/templates/astro/.vscode/extensions.json new file mode 100644 index 000000000..22a15055d --- /dev/null +++ b/alchemy/templates/astro/.vscode/extensions.json @@ -0,0 +1,4 @@ +{ + "recommendations": ["astro-build.astro-vscode"], + "unwantedRecommendations": [] +} diff --git a/alchemy/templates/astro/.vscode/launch.json b/alchemy/templates/astro/.vscode/launch.json new file mode 100644 index 000000000..d64220976 --- /dev/null +++ b/alchemy/templates/astro/.vscode/launch.json @@ -0,0 +1,11 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "command": "./node_modules/.bin/astro dev", + "name": "Development server", + "request": "launch", + "type": "node-terminal" + } + ] +} diff --git a/alchemy/templates/astro/README.md b/alchemy/templates/astro/README.md new file mode 100644 index 000000000..ed7ed7a53 --- /dev/null +++ b/alchemy/templates/astro/README.md @@ -0,0 +1,48 @@ +# Astro Starter Kit: Basics + +```sh +bun create astro@latest -- --template basics +``` + +[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/withastro/astro/tree/latest/examples/basics) +[![Open with CodeSandbox](https://assets.codesandbox.io/github/button-edit-lime.svg)](https://codesandbox.io/p/sandbox/github/withastro/astro/tree/latest/examples/basics) +[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/withastro/astro?devcontainer_path=.devcontainer/basics/devcontainer.json) + +> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun! + +![just-the-basics](https://github.com/withastro/astro/assets/2244813/a0a5533c-a856-4198-8470-2d67b1d7c554) + +## 🚀 Project Structure + +Inside of your Astro project, you'll see the following folders and files: + +```text +/ +├── public/ +│ └── favicon.svg +├── src/ +│ ├── layouts/ +│ │ └── Layout.astro +│ └── pages/ +│ └── index.astro +└── package.json +``` + +To learn more about the folder structure of an Astro project, refer to [our guide on project structure](https://docs.astro.build/en/basics/project-structure/). + +## 🧞 Commands + +All commands are run from the root of the project, from a terminal: + +| Command | Action | +| :------------------------ | :----------------------------------------------- | +| `bun install` | Installs dependencies | +| `bun dev` | Starts local dev server at `localhost:4321` | +| `bun build` | Build your production site to `./dist/` | +| `bun preview` | Preview your build locally, before deploying | +| `bun astro ...` | Run CLI commands like `astro add`, `astro check` | +| `bun astro -- --help` | Get help using the Astro CLI | + +## 👀 Want to learn more? + +Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat). diff --git a/alchemy/templates/astro/_env.example b/alchemy/templates/astro/_env.example new file mode 100644 index 000000000..bb4cbbe94 --- /dev/null +++ b/alchemy/templates/astro/_env.example @@ -0,0 +1 @@ +ALCHEMY_PASSWORD=change-me \ No newline at end of file diff --git a/alchemy/templates/astro/_gitignore b/alchemy/templates/astro/_gitignore new file mode 100644 index 000000000..5c1a9a98f --- /dev/null +++ b/alchemy/templates/astro/_gitignore @@ -0,0 +1,27 @@ +# build output +dist/ + +# generated types +.astro/ + +# dependencies +node_modules/ + +# logs +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* + +# environment variables +.env +.env.production + +# macOS-specific files +.DS_Store + +# jetbrains setting folder +.idea/ + +.alchemy/ +.env \ No newline at end of file diff --git a/alchemy/templates/astro/alchemy.run.ts b/alchemy/templates/astro/alchemy.run.ts new file mode 100644 index 000000000..1cf2608c0 --- /dev/null +++ b/alchemy/templates/astro/alchemy.run.ts @@ -0,0 +1,16 @@ +/// + +import alchemy from "alchemy"; +import { Astro } from "alchemy/cloudflare"; + +const app = await alchemy("my-alchemy-app"); + +export const worker = await Astro("website", { + command: "bun run build", +}); + +console.log({ + url: worker.url, +}); + +await app.finalize(); diff --git a/alchemy/templates/astro/astro.config.mjs b/alchemy/templates/astro/astro.config.mjs new file mode 100644 index 000000000..f58ae10b2 --- /dev/null +++ b/alchemy/templates/astro/astro.config.mjs @@ -0,0 +1,8 @@ +import { defineConfig } from 'astro/config'; +import cloudflare from '@astrojs/cloudflare'; + +// https://astro.build/config +export default defineConfig({ + output: 'server', + adapter: cloudflare(), +}); diff --git a/alchemy/templates/astro/package.json b/alchemy/templates/astro/package.json new file mode 100644 index 000000000..cf445ada3 --- /dev/null +++ b/alchemy/templates/astro/package.json @@ -0,0 +1,23 @@ +{ + "type": "module", + "name": "@alchemy.run/astro-template", + "version": "0.0.1", + "scripts": { + "astro": "astro", + "build": "astro check && astro build", + "deploy": "bun --env-file=./.env ./alchemy.run.ts", + "destroy": "bun --env-file=./.env ./alchemy.run.ts --destroy", + "dev": "astro dev", + "preview": "astro preview" + }, + "dependencies": { + "astro": "^5.10.0" + }, + "devDependencies": { + "alchemy": "workspace:*", + "@astrojs/cloudflare": "^12.6.0", + "miniflare": "^4.20250617.3", + "@cloudflare/workers-types": "^4.20250620.0", + "typescript": "^5.8.3" + } +} diff --git a/alchemy/templates/astro/public/favicon.svg b/alchemy/templates/astro/public/favicon.svg new file mode 100644 index 000000000..f157bd1c5 --- /dev/null +++ b/alchemy/templates/astro/public/favicon.svg @@ -0,0 +1,9 @@ + + + + diff --git a/alchemy/templates/astro/src/assets/astro.svg b/alchemy/templates/astro/src/assets/astro.svg new file mode 100644 index 000000000..8cf8fb0c7 --- /dev/null +++ b/alchemy/templates/astro/src/assets/astro.svg @@ -0,0 +1 @@ + diff --git a/alchemy/templates/astro/src/assets/background.svg b/alchemy/templates/astro/src/assets/background.svg new file mode 100644 index 000000000..4b2be0ac0 --- /dev/null +++ b/alchemy/templates/astro/src/assets/background.svg @@ -0,0 +1 @@ + diff --git a/alchemy/templates/astro/src/components/Welcome.astro b/alchemy/templates/astro/src/components/Welcome.astro new file mode 100644 index 000000000..52e033341 --- /dev/null +++ b/alchemy/templates/astro/src/components/Welcome.astro @@ -0,0 +1,210 @@ +--- +import astroLogo from '../assets/astro.svg'; +import background from '../assets/background.svg'; +--- + + + + diff --git a/alchemy/templates/astro/src/layouts/Layout.astro b/alchemy/templates/astro/src/layouts/Layout.astro new file mode 100644 index 000000000..e455c6106 --- /dev/null +++ b/alchemy/templates/astro/src/layouts/Layout.astro @@ -0,0 +1,22 @@ + + + + + + + + Astro Basics + + + + + + + diff --git a/alchemy/templates/astro/src/pages/api/hello.ts b/alchemy/templates/astro/src/pages/api/hello.ts new file mode 100644 index 000000000..13682427c --- /dev/null +++ b/alchemy/templates/astro/src/pages/api/hello.ts @@ -0,0 +1,19 @@ +import type { APIRoute } from 'astro'; + +export const GET: APIRoute = async ({ request }) => { + // Access Cloudflare runtime context + const runtime = request.cf; + + return new Response(JSON.stringify({ + message: "Hello from Astro API on Cloudflare!", + timestamp: new Date().toISOString(), + colo: runtime?.colo || "unknown", + country: runtime?.country || "unknown", + city: runtime?.city || "unknown", + }), { + status: 200, + headers: { + 'Content-Type': 'application/json', + }, + }); +}; diff --git a/alchemy/templates/astro/src/pages/index.astro b/alchemy/templates/astro/src/pages/index.astro new file mode 100644 index 000000000..c04f3602b --- /dev/null +++ b/alchemy/templates/astro/src/pages/index.astro @@ -0,0 +1,11 @@ +--- +import Welcome from '../components/Welcome.astro'; +import Layout from '../layouts/Layout.astro'; + +// Welcome to Astro! Wondering what to do next? Check out the Astro documentation at https://docs.astro.build +// Don't want to use any of this? Delete everything in this file, the `assets`, `components`, and `layouts` directories, and start fresh. +--- + + + + diff --git a/alchemy/templates/astro/tsconfig.json b/alchemy/templates/astro/tsconfig.json new file mode 100644 index 000000000..9294135ff --- /dev/null +++ b/alchemy/templates/astro/tsconfig.json @@ -0,0 +1,18 @@ +{ + "extends": "astro/tsconfigs/strict", + "include": [ + "alchemy.run.ts", + "types/**/*.ts", + ".astro/types.d.ts", + "**/*" + ], + "exclude": [ + "dist" + ], + "compilerOptions": { + "types": [ + "@cloudflare/workers-types", + "./types/env.d.ts" + ] + } +} diff --git a/alchemy/templates/astro/types/env.d.ts b/alchemy/templates/astro/types/env.d.ts new file mode 100644 index 000000000..f371005c3 --- /dev/null +++ b/alchemy/templates/astro/types/env.d.ts @@ -0,0 +1,16 @@ +// This file infers types for the cloudflare:workers environment from your Alchemy Worker. +// @see https://alchemy.run/docs/concepts/bindings.html#type-safe-bindings + +import type { worker } from "../alchemy.run.ts"; + +export type CloudflareEnv = typeof worker.Env; + +declare global { + type Env = CloudflareEnv; +} + +declare module "cloudflare:workers" { + namespace Cloudflare { + export interface Env extends CloudflareEnv {} + } +} diff --git a/alchemy/templates/astro/wrangler.jsonc b/alchemy/templates/astro/wrangler.jsonc new file mode 100644 index 000000000..76612dae2 --- /dev/null +++ b/alchemy/templates/astro/wrangler.jsonc @@ -0,0 +1,6 @@ +{ + "name": "website", + "main": "dist/_worker.js/index.js", + "compatibility_date": "2025-04-20", + "assets": { "binding": "ASSETS", "directory": "dist" } +} diff --git a/alchemy/templates/nuxt/.gitignore b/alchemy/templates/nuxt/.gitignore new file mode 100644 index 000000000..31defa16b --- /dev/null +++ b/alchemy/templates/nuxt/.gitignore @@ -0,0 +1,27 @@ +# Nuxt dev/build outputs +.output +.data +.nuxt +.nitro +.cache +dist + +# Node dependencies +node_modules + +# Logs +logs +*.log + +# Misc +.DS_Store +.fleet +.idea + +# Local env files +.env +.env.* +!.env.example + +.alchemy/ +.env \ No newline at end of file diff --git a/alchemy/templates/nuxt/README.md b/alchemy/templates/nuxt/README.md new file mode 100644 index 000000000..25b58212c --- /dev/null +++ b/alchemy/templates/nuxt/README.md @@ -0,0 +1,75 @@ +# Nuxt Minimal Starter + +Look at the [Nuxt documentation](https://nuxt.com/docs/getting-started/introduction) to learn more. + +## Setup + +Make sure to install dependencies: + +```bash +# npm +npm install + +# pnpm +pnpm install + +# yarn +yarn install + +# bun +bun install +``` + +## Development Server + +Start the development server on `http://localhost:3000`: + +```bash +# npm +npm run dev + +# pnpm +pnpm dev + +# yarn +yarn dev + +# bun +bun run dev +``` + +## Production + +Build the application for production: + +```bash +# npm +npm run build + +# pnpm +pnpm build + +# yarn +yarn build + +# bun +bun run build +``` + +Locally preview production build: + +```bash +# npm +npm run preview + +# pnpm +pnpm preview + +# yarn +yarn preview + +# bun +bun run preview +``` + +Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information. diff --git a/alchemy/templates/nuxt/_env b/alchemy/templates/nuxt/_env new file mode 100644 index 000000000..bb4cbbe94 --- /dev/null +++ b/alchemy/templates/nuxt/_env @@ -0,0 +1 @@ +ALCHEMY_PASSWORD=change-me \ No newline at end of file diff --git a/alchemy/templates/nuxt/_env.example b/alchemy/templates/nuxt/_env.example new file mode 100644 index 000000000..bb4cbbe94 --- /dev/null +++ b/alchemy/templates/nuxt/_env.example @@ -0,0 +1 @@ +ALCHEMY_PASSWORD=change-me \ No newline at end of file diff --git a/alchemy/templates/nuxt/_gitignore b/alchemy/templates/nuxt/_gitignore new file mode 100644 index 000000000..31defa16b --- /dev/null +++ b/alchemy/templates/nuxt/_gitignore @@ -0,0 +1,27 @@ +# Nuxt dev/build outputs +.output +.data +.nuxt +.nitro +.cache +dist + +# Node dependencies +node_modules + +# Logs +logs +*.log + +# Misc +.DS_Store +.fleet +.idea + +# Local env files +.env +.env.* +!.env.example + +.alchemy/ +.env \ No newline at end of file diff --git a/alchemy/templates/nuxt/alchemy.run.ts b/alchemy/templates/nuxt/alchemy.run.ts new file mode 100644 index 000000000..d761e5b52 --- /dev/null +++ b/alchemy/templates/nuxt/alchemy.run.ts @@ -0,0 +1,16 @@ +/// + +import alchemy from "alchemy"; +import { Nuxt } from "alchemy/cloudflare"; + +const app = await alchemy("my-alchemy-app"); + +export const worker = await Nuxt("website", { + command: "bun run build", +}); + +console.log({ + url: worker.url, +}); + +await app.finalize(); diff --git a/alchemy/templates/nuxt/app.vue b/alchemy/templates/nuxt/app.vue new file mode 100644 index 000000000..09f935bbb --- /dev/null +++ b/alchemy/templates/nuxt/app.vue @@ -0,0 +1,6 @@ + diff --git a/alchemy/templates/nuxt/nuxt.config.ts b/alchemy/templates/nuxt/nuxt.config.ts new file mode 100644 index 000000000..b06729905 --- /dev/null +++ b/alchemy/templates/nuxt/nuxt.config.ts @@ -0,0 +1,13 @@ +// https://nuxt.com/docs/api/configuration/nuxt-config +export default defineNuxtConfig({ + compatibilityDate: '2025-05-15', + devtools: { enabled: true }, + nitro: { + preset: "cloudflare_module", + cloudflare: { + deployConfig: true, + nodeCompat: true + } + }, + modules: ["nitro-cloudflare-dev"], +}); diff --git a/alchemy/templates/nuxt/package.json b/alchemy/templates/nuxt/package.json new file mode 100644 index 000000000..8f511b669 --- /dev/null +++ b/alchemy/templates/nuxt/package.json @@ -0,0 +1,26 @@ +{ + "type": "module", + "name": "@alchemy.run/nuxt-template", + "private": true, + "scripts": { + "build": "nuxt build", + "deploy": "bun --env-file=./.env ./alchemy.run.ts", + "destroy": "bun --env-file=./.env ./alchemy.run.ts --destroy", + "dev": "nuxt dev", + "generate": "nuxt generate", + "postinstall": "nuxt prepare", + "preview": "nuxt preview" + }, + "dependencies": { + "nuxt": "^3.17.5", + "vue": "^3.5.16", + "vue-router": "^4.5.1" + }, + "devDependencies": { + "alchemy": "workspace:*", + "@cloudflare/workers-types": "^4.20250620.0", + "miniflare": "^4.20250617.3", + "nitro-cloudflare-dev": "^0.2.2", + "typescript": "^5.8.3" + } +} diff --git a/alchemy/templates/nuxt/public/favicon.ico b/alchemy/templates/nuxt/public/favicon.ico new file mode 100644 index 000000000..18993ad91 Binary files /dev/null and b/alchemy/templates/nuxt/public/favicon.ico differ diff --git a/alchemy/templates/nuxt/public/robots.txt b/alchemy/templates/nuxt/public/robots.txt new file mode 100644 index 000000000..0ad279c73 --- /dev/null +++ b/alchemy/templates/nuxt/public/robots.txt @@ -0,0 +1,2 @@ +User-Agent: * +Disallow: diff --git a/alchemy/templates/nuxt/server/api/hello.ts b/alchemy/templates/nuxt/server/api/hello.ts new file mode 100644 index 000000000..b61f21772 --- /dev/null +++ b/alchemy/templates/nuxt/server/api/hello.ts @@ -0,0 +1,7 @@ +// see: https://nuxt.com/docs/guide/directory-structure/server + +export default defineEventHandler((event) => { + return { + hello: "world", + }; +}); diff --git a/alchemy/templates/nuxt/server/middleware/auth.ts b/alchemy/templates/nuxt/server/middleware/auth.ts new file mode 100644 index 000000000..793cf6bb5 --- /dev/null +++ b/alchemy/templates/nuxt/server/middleware/auth.ts @@ -0,0 +1,5 @@ +// see: https://nuxt.com/docs/guide/directory-structure/server#server-middleware + +export default defineEventHandler((event) => { + event.context.auth = { user: 123 } +}); diff --git a/alchemy/templates/nuxt/server/middleware/hello.ts b/alchemy/templates/nuxt/server/middleware/hello.ts new file mode 100644 index 000000000..f283d9e9f --- /dev/null +++ b/alchemy/templates/nuxt/server/middleware/hello.ts @@ -0,0 +1,5 @@ +// see: https://nuxt.com/docs/guide/directory-structure/server#server-middleware + +export default defineEventHandler((event) => { + console.log('New request: ' + getRequestURL(event)) +}) diff --git a/alchemy/templates/nuxt/server/tsconfig.json b/alchemy/templates/nuxt/server/tsconfig.json new file mode 100644 index 000000000..b9ed69c19 --- /dev/null +++ b/alchemy/templates/nuxt/server/tsconfig.json @@ -0,0 +1,3 @@ +{ + "extends": "../.nuxt/tsconfig.server.json" +} diff --git a/alchemy/templates/nuxt/tsconfig.json b/alchemy/templates/nuxt/tsconfig.json new file mode 100644 index 000000000..4a5652557 --- /dev/null +++ b/alchemy/templates/nuxt/tsconfig.json @@ -0,0 +1,15 @@ +{ + // https://nuxt.com/docs/guide/concepts/typescript + "extends": "./.nuxt/tsconfig.json", + "compilerOptions": { + "types": [ + "@cloudflare/workers-types", + "./types/env.d.ts" + ] + }, + "include": [ + "alchemy.run.ts", + "types/**/*.ts", + "server/**/*.ts" + ] +} diff --git a/alchemy/templates/nuxt/types/env.d.ts b/alchemy/templates/nuxt/types/env.d.ts new file mode 100644 index 000000000..f371005c3 --- /dev/null +++ b/alchemy/templates/nuxt/types/env.d.ts @@ -0,0 +1,16 @@ +// This file infers types for the cloudflare:workers environment from your Alchemy Worker. +// @see https://alchemy.run/docs/concepts/bindings.html#type-safe-bindings + +import type { worker } from "../alchemy.run.ts"; + +export type CloudflareEnv = typeof worker.Env; + +declare global { + type Env = CloudflareEnv; +} + +declare module "cloudflare:workers" { + namespace Cloudflare { + export interface Env extends CloudflareEnv {} + } +} diff --git a/alchemy/templates/react-router/.gitignore b/alchemy/templates/react-router/.gitignore new file mode 100644 index 000000000..43b9564b7 --- /dev/null +++ b/alchemy/templates/react-router/.gitignore @@ -0,0 +1,15 @@ +.DS_Store +/node_modules/ +*.tsbuildinfo + +# React Router +/.react-router/ +/build/ + +# Cloudflare +.mf +.wrangler +.dev.vars* + +.alchemy/ +.env diff --git a/alchemy/templates/react-router/.vscode/settings.json b/alchemy/templates/react-router/.vscode/settings.json new file mode 100644 index 000000000..0126e59b8 --- /dev/null +++ b/alchemy/templates/react-router/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "files.associations": { + "wrangler.json": "jsonc" + } +} \ No newline at end of file diff --git a/alchemy/templates/react-router/README.md b/alchemy/templates/react-router/README.md new file mode 100644 index 000000000..e83f9fc9d --- /dev/null +++ b/alchemy/templates/react-router/README.md @@ -0,0 +1,79 @@ +# Welcome to React Router! + +A modern, production-ready template for building full-stack React applications using React Router. + +## Features + +- 🚀 Server-side rendering +- ⚡️ Hot Module Replacement (HMR) +- 📦 Asset bundling and optimization +- 🔄 Data loading and mutations +- 🔒 TypeScript by default +- 🎉 TailwindCSS for styling +- 📖 [React Router docs](https://reactrouter.com/) + +## Getting Started + +### Installation + +Install the dependencies: + +```bash +npm install +``` + +### Development + +Start the development server with HMR: + +```bash +npm run dev +``` + +Your application will be available at `http://localhost:5173`. + +## Previewing the Production Build + +Preview the production build locally: + +```bash +npm run preview +``` + +## Building for Production + +Create a production build: + +```bash +npm run build +``` + +## Deployment + +Deployment is done using the Wrangler CLI. + +To build and deploy directly to production: + +```sh +npm run deploy +``` + +To deploy a preview URL: + +```sh +npx wrangler versions upload +``` + +You can then promote a version to production after verification or roll it out progressively. + +```sh +npx wrangler versions deploy +``` + +## Styling + +This template comes with [Tailwind CSS](https://tailwindcss.com/) already configured for a simple default starting experience. You can use whatever CSS framework you prefer. + +--- + +Built with ❤️ using React Router. diff --git a/alchemy/templates/react-router/_env b/alchemy/templates/react-router/_env new file mode 100644 index 000000000..bb4cbbe94 --- /dev/null +++ b/alchemy/templates/react-router/_env @@ -0,0 +1 @@ +ALCHEMY_PASSWORD=change-me \ No newline at end of file diff --git a/alchemy/templates/react-router/_env.example b/alchemy/templates/react-router/_env.example new file mode 100644 index 000000000..bb4cbbe94 --- /dev/null +++ b/alchemy/templates/react-router/_env.example @@ -0,0 +1 @@ +ALCHEMY_PASSWORD=change-me \ No newline at end of file diff --git a/alchemy/templates/react-router/_gitignore b/alchemy/templates/react-router/_gitignore new file mode 100644 index 000000000..43b9564b7 --- /dev/null +++ b/alchemy/templates/react-router/_gitignore @@ -0,0 +1,15 @@ +.DS_Store +/node_modules/ +*.tsbuildinfo + +# React Router +/.react-router/ +/build/ + +# Cloudflare +.mf +.wrangler +.dev.vars* + +.alchemy/ +.env diff --git a/alchemy/templates/react-router/alchemy.run.ts b/alchemy/templates/react-router/alchemy.run.ts new file mode 100644 index 000000000..36b0a51ef --- /dev/null +++ b/alchemy/templates/react-router/alchemy.run.ts @@ -0,0 +1,17 @@ +/// + +import alchemy from "alchemy"; +import { ReactRouter } from "alchemy/cloudflare"; + +const app = await alchemy("my-alchemy-app"); + +export const worker = await ReactRouter("website", { + main: "workers/app.ts", + command: "bun run build", +}); + +console.log({ + url: worker.url, +}); + +await app.finalize(); diff --git a/alchemy/templates/react-router/app/app.css b/alchemy/templates/react-router/app/app.css new file mode 100644 index 000000000..9b4c3ef90 --- /dev/null +++ b/alchemy/templates/react-router/app/app.css @@ -0,0 +1,15 @@ +@import "tailwindcss" source("."); + +@theme { + --font-sans: "Inter", ui-sans-serif, system-ui, sans-serif, + "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; +} + +html, +body { + @apply bg-white dark:bg-gray-950; + + @media (prefers-color-scheme: dark) { + color-scheme: dark; + } +} diff --git a/alchemy/templates/react-router/app/entry.server.tsx b/alchemy/templates/react-router/app/entry.server.tsx new file mode 100644 index 000000000..0d843dbb1 --- /dev/null +++ b/alchemy/templates/react-router/app/entry.server.tsx @@ -0,0 +1,43 @@ +import type { AppLoadContext, EntryContext } from "react-router"; +import { ServerRouter } from "react-router"; +import { isbot } from "isbot"; +import { renderToReadableStream } from "react-dom/server"; + +export default async function handleRequest( + request: Request, + responseStatusCode: number, + responseHeaders: Headers, + routerContext: EntryContext, + _loadContext: AppLoadContext +) { + let shellRendered = false; + const userAgent = request.headers.get("user-agent"); + + const body = await renderToReadableStream( + , + { + onError(error: unknown) { + responseStatusCode = 500; + // Log streaming rendering errors from inside the shell. Don't log + // errors encountered during initial shell rendering since they'll + // reject and get logged in handleDocumentRequest. + if (shellRendered) { + console.error(error); + } + }, + } + ); + shellRendered = true; + + // Ensure requests from bots and SPA Mode renders wait for all content to load before responding + // https://react.dev/reference/react-dom/server/renderToPipeableStream#waiting-for-all-content-to-load-for-crawlers-and-static-generation + if ((userAgent && isbot(userAgent)) || routerContext.isSpaMode) { + await body.allReady; + } + + responseHeaders.set("Content-Type", "text/html"); + return new Response(body, { + headers: responseHeaders, + status: responseStatusCode, + }); +} diff --git a/alchemy/templates/react-router/app/root.tsx b/alchemy/templates/react-router/app/root.tsx new file mode 100644 index 000000000..9fc663618 --- /dev/null +++ b/alchemy/templates/react-router/app/root.tsx @@ -0,0 +1,75 @@ +import { + isRouteErrorResponse, + Links, + Meta, + Outlet, + Scripts, + ScrollRestoration, +} from "react-router"; + +import type { Route } from "./+types/root"; +import "./app.css"; + +export const links: Route.LinksFunction = () => [ + { rel: "preconnect", href: "https://fonts.googleapis.com" }, + { + rel: "preconnect", + href: "https://fonts.gstatic.com", + crossOrigin: "anonymous", + }, + { + rel: "stylesheet", + href: "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap", + }, +]; + +export function Layout({ children }: { children: React.ReactNode }) { + return ( + + + + + + + + + {children} + + + + + ); +} + +export default function App() { + return ; +} + +export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) { + let message = "Oops!"; + let details = "An unexpected error occurred."; + let stack: string | undefined; + + if (isRouteErrorResponse(error)) { + message = error.status === 404 ? "404" : "Error"; + details = + error.status === 404 + ? "The requested page could not be found." + : error.statusText || details; + } else if (import.meta.env.DEV && error && error instanceof Error) { + details = error.message; + stack = error.stack; + } + + return ( +
+

{message}

+

{details}

+ {stack && ( +
+          {stack}
+        
+ )} +
+ ); +} diff --git a/alchemy/templates/react-router/app/routes.ts b/alchemy/templates/react-router/app/routes.ts new file mode 100644 index 000000000..102b40258 --- /dev/null +++ b/alchemy/templates/react-router/app/routes.ts @@ -0,0 +1,3 @@ +import { type RouteConfig, index } from "@react-router/dev/routes"; + +export default [index("routes/home.tsx")] satisfies RouteConfig; diff --git a/alchemy/templates/react-router/app/routes/home.tsx b/alchemy/templates/react-router/app/routes/home.tsx new file mode 100644 index 000000000..a8642a037 --- /dev/null +++ b/alchemy/templates/react-router/app/routes/home.tsx @@ -0,0 +1,17 @@ +import type { Route } from "./+types/home"; +import { Welcome } from "../welcome/welcome"; + +export function meta({}: Route.MetaArgs) { + return [ + { title: "New React Router App" }, + { name: "description", content: "Welcome to React Router!" }, + ]; +} + +export function loader({ context }: Route.LoaderArgs) { + return { message: context.cloudflare.env.VALUE_FROM_CLOUDFLARE }; +} + +export default function Home({ loaderData }: Route.ComponentProps) { + return ; +} diff --git a/alchemy/templates/react-router/app/welcome/logo-dark.svg b/alchemy/templates/react-router/app/welcome/logo-dark.svg new file mode 100644 index 000000000..dd8202894 --- /dev/null +++ b/alchemy/templates/react-router/app/welcome/logo-dark.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/alchemy/templates/react-router/app/welcome/logo-light.svg b/alchemy/templates/react-router/app/welcome/logo-light.svg new file mode 100644 index 000000000..73284929d --- /dev/null +++ b/alchemy/templates/react-router/app/welcome/logo-light.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/alchemy/templates/react-router/app/welcome/welcome.tsx b/alchemy/templates/react-router/app/welcome/welcome.tsx new file mode 100644 index 000000000..0134ed801 --- /dev/null +++ b/alchemy/templates/react-router/app/welcome/welcome.tsx @@ -0,0 +1,90 @@ +import logoDark from "./logo-dark.svg"; +import logoLight from "./logo-light.svg"; + +export function Welcome({ message }: { message: string }) { + return ( +
+
+
+
+ React Router + React Router +
+
+
+ +
+
+
+ ); +} + +const resources = [ + { + href: "https://reactrouter.com/docs", + text: "React Router Docs", + icon: ( + + + + ), + }, + { + href: "https://rmx.as/discord", + text: "Join Discord", + icon: ( + + + + ), + }, +]; diff --git a/alchemy/templates/react-router/build/client/assets/chunk-NL6KNZEE-CfxFt-pq.js b/alchemy/templates/react-router/build/client/assets/chunk-NL6KNZEE-CfxFt-pq.js new file mode 100644 index 000000000..8295d9877 --- /dev/null +++ b/alchemy/templates/react-router/build/client/assets/chunk-NL6KNZEE-CfxFt-pq.js @@ -0,0 +1,51 @@ +var Bt={exports:{}},tt={};/** + * @license React + * react-jsx-runtime.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var Fr;function wa(){if(Fr)return tt;Fr=1;var e=Symbol.for("react.transitional.element"),t=Symbol.for("react.fragment");function r(a,n,o){var l=null;if(o!==void 0&&(l=""+o),n.key!==void 0&&(l=""+n.key),"key"in n){o={};for(var s in n)s!=="key"&&(o[s]=n[s])}else o=n;return n=o.ref,{$$typeof:e,type:a,key:l,ref:n!==void 0?n:null,props:o}}return tt.Fragment=t,tt.jsx=r,tt.jsxs=r,tt}var jr;function Ea(){return jr||(jr=1,Bt.exports=wa()),Bt.exports}var fl=Ea(),Wt={exports:{}},W={};/** + * @license React + * react.production.js + * + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */var Hr;function Ra(){if(Hr)return W;Hr=1;var e=Symbol.for("react.transitional.element"),t=Symbol.for("react.portal"),r=Symbol.for("react.fragment"),a=Symbol.for("react.strict_mode"),n=Symbol.for("react.profiler"),o=Symbol.for("react.consumer"),l=Symbol.for("react.context"),s=Symbol.for("react.forward_ref"),i=Symbol.for("react.suspense"),u=Symbol.for("react.memo"),c=Symbol.for("react.lazy"),p=Symbol.iterator;function f(m){return m===null||typeof m!="object"?null:(m=p&&m[p]||m["@@iterator"],typeof m=="function"?m:null)}var g={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},w=Object.assign,b={};function S(m,L,A){this.props=m,this.context=L,this.refs=b,this.updater=A||g}S.prototype.isReactComponent={},S.prototype.setState=function(m,L){if(typeof m!="object"&&typeof m!="function"&&m!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,m,L,"setState")},S.prototype.forceUpdate=function(m){this.updater.enqueueForceUpdate(this,m,"forceUpdate")};function R(){}R.prototype=S.prototype;function O(m,L,A){this.props=m,this.context=L,this.refs=b,this.updater=A||g}var T=O.prototype=new R;T.constructor=O,w(T,S.prototype),T.isPureReactComponent=!0;var M=Array.isArray,x={H:null,A:null,T:null,S:null,V:null},y=Object.prototype.hasOwnProperty;function D(m,L,A,k,Y,ee){return A=ee.ref,{$$typeof:e,type:m,key:L,ref:A!==void 0?A:null,props:ee}}function U(m,L){return D(m.type,L,void 0,void 0,void 0,m.props)}function j(m){return typeof m=="object"&&m!==null&&m.$$typeof===e}function K(m){var L={"=":"=0",":":"=2"};return"$"+m.replace(/[=:]/g,function(A){return L[A]})}var re=/\/+/g;function se(m,L){return typeof m=="object"&&m!==null&&m.key!=null?K(""+m.key):L.toString(36)}function oe(){}function X(m){switch(m.status){case"fulfilled":return m.value;case"rejected":throw m.reason;default:switch(typeof m.status=="string"?m.then(oe,oe):(m.status="pending",m.then(function(L){m.status==="pending"&&(m.status="fulfilled",m.value=L)},function(L){m.status==="pending"&&(m.status="rejected",m.reason=L)})),m.status){case"fulfilled":return m.value;case"rejected":throw m.reason}}throw m}function Q(m,L,A,k,Y){var ee=typeof m;(ee==="undefined"||ee==="boolean")&&(m=null);var z=!1;if(m===null)z=!0;else switch(ee){case"bigint":case"string":case"number":z=!0;break;case"object":switch(m.$$typeof){case e:case t:z=!0;break;case c:return z=m._init,Q(z(m._payload),L,A,k,Y)}}if(z)return Y=Y(m),z=k===""?"."+se(m,0):k,M(Y)?(A="",z!=null&&(A=z.replace(re,"$&/")+"/"),Q(Y,L,A,"",function($t){return $t})):Y!=null&&(j(Y)&&(Y=U(Y,A+(Y.key==null||m&&m.key===Y.key?"":(""+Y.key).replace(re,"$&/")+"/")+z)),L.push(Y)),1;z=0;var xe=k===""?".":k+":";if(M(m))for(var ne=0;ne{const f=function(){};return f.prototype=Object.create(null),f})();function l(f,g){const w=new o,b=f.length;if(b<2)return w;const S=(g==null?void 0:g.decode)||c;let R=0;do{const O=f.indexOf("=",R);if(O===-1)break;const T=f.indexOf(";",R),M=T===-1?b:T;if(O>M){R=f.lastIndexOf(";",O-1)+1;continue}const x=s(f,R,O),y=i(f,O,x),D=f.slice(x,y);if(w[D]===void 0){let U=s(f,O+1,M),j=i(f,M,U);const K=S(f.slice(U,j));w[D]=K}R=M+1}while(Rw;){const b=f.charCodeAt(--g);if(b!==32&&b!==9)return g+1}return w}function u(f,g,w){const b=(w==null?void 0:w.encode)||encodeURIComponent;if(!e.test(f))throw new TypeError(`argument name is invalid: ${f}`);const S=b(g);if(!t.test(S))throw new TypeError(`argument val is invalid: ${g}`);let R=f+"="+S;if(!w)return R;if(w.maxAge!==void 0){if(!Number.isInteger(w.maxAge))throw new TypeError(`option maxAge is invalid: ${w.maxAge}`);R+="; Max-Age="+w.maxAge}if(w.domain){if(!r.test(w.domain))throw new TypeError(`option domain is invalid: ${w.domain}`);R+="; Domain="+w.domain}if(w.path){if(!a.test(w.path))throw new TypeError(`option path is invalid: ${w.path}`);R+="; Path="+w.path}if(w.expires){if(!p(w.expires)||!Number.isFinite(w.expires.valueOf()))throw new TypeError(`option expires is invalid: ${w.expires}`);R+="; Expires="+w.expires.toUTCString()}if(w.httpOnly&&(R+="; HttpOnly"),w.secure&&(R+="; Secure"),w.partitioned&&(R+="; Partitioned"),w.priority)switch(typeof w.priority=="string"?w.priority.toLowerCase():void 0){case"low":R+="; Priority=Low";break;case"medium":R+="; Priority=Medium";break;case"high":R+="; Priority=High";break;default:throw new TypeError(`option priority is invalid: ${w.priority}`)}if(w.sameSite)switch(typeof w.sameSite=="string"?w.sameSite.toLowerCase():w.sameSite){case!0:case"strict":R+="; SameSite=Strict";break;case"lax":R+="; SameSite=Lax";break;case"none":R+="; SameSite=None";break;default:throw new TypeError(`option sameSite is invalid: ${w.sameSite}`)}return R}function c(f){if(f.indexOf("%")===-1)return f;try{return decodeURIComponent(f)}catch{return f}}function p(f){return n.call(f)==="[object Date]"}return rt}Sa();/** + * react-router v7.6.2 + * + * Copyright (c) Remix Software Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE.md file in the root directory of this source tree. + * + * @license MIT + */var pn=e=>{throw TypeError(e)},xa=(e,t,r)=>t.has(e)||pn("Cannot "+r),Yt=(e,t,r)=>(xa(e,t,"read from private field"),r?r.call(e):t.get(e)),La=(e,t,r)=>t.has(e)?pn("Cannot add the same private member more than once"):t instanceof WeakSet?t.add(e):t.set(e,r),Br="popstate";function hl(e={}){function t(a,n){let{pathname:o,search:l,hash:s}=a.location;return st("",{pathname:o,search:l,hash:s},n.state&&n.state.usr||null,n.state&&n.state.key||"default")}function r(a,n){return typeof n=="string"?n:ke(n)}return Pa(t,r,null,e)}function J(e,t){if(e===!1||e===null||typeof e>"u")throw new Error(t)}function ae(e,t){if(!e){typeof console<"u"&&console.warn(t);try{throw new Error(t)}catch{}}}function Ca(){return Math.random().toString(36).substring(2,10)}function Wr(e,t){return{usr:e.state,key:e.key,idx:t}}function st(e,t,r=null,a){return{pathname:typeof e=="string"?e:e.pathname,search:"",hash:"",...typeof t=="string"?Ne(t):t,state:r,key:t&&t.key||a||Ca()}}function ke({pathname:e="/",search:t="",hash:r=""}){return t&&t!=="?"&&(e+=t.charAt(0)==="?"?t:"?"+t),r&&r!=="#"&&(e+=r.charAt(0)==="#"?r:"#"+r),e}function Ne(e){let t={};if(e){let r=e.indexOf("#");r>=0&&(t.hash=e.substring(r),e=e.substring(0,r));let a=e.indexOf("?");a>=0&&(t.search=e.substring(a),e=e.substring(0,a)),e&&(t.pathname=e)}return t}function Pa(e,t,r,a={}){let{window:n=document.defaultView,v5Compat:o=!1}=a,l=n.history,s="POP",i=null,u=c();u==null&&(u=0,l.replaceState({...l.state,idx:u},""));function c(){return(l.state||{idx:null}).idx}function p(){s="POP";let S=c(),R=S==null?null:S-u;u=S,i&&i({action:s,location:b.location,delta:R})}function f(S,R){s="PUSH";let O=st(b.location,S,R);u=c()+1;let T=Wr(O,u),M=b.createHref(O);try{l.pushState(T,"",M)}catch(x){if(x instanceof DOMException&&x.name==="DataCloneError")throw x;n.location.assign(M)}o&&i&&i({action:s,location:b.location,delta:1})}function g(S,R){s="REPLACE";let O=st(b.location,S,R);u=c();let T=Wr(O,u),M=b.createHref(O);l.replaceState(T,"",M),o&&i&&i({action:s,location:b.location,delta:0})}function w(S){return yn(S)}let b={get action(){return s},get location(){return e(n,l)},listen(S){if(i)throw new Error("A history only accepts one active listener");return n.addEventListener(Br,p),i=S,()=>{n.removeEventListener(Br,p),i=null}},createHref(S){return t(n,S)},createURL:w,encodeLocation(S){let R=w(S);return{pathname:R.pathname,search:R.search,hash:R.hash}},push:f,replace:g,go(S){return l.go(S)}};return b}function yn(e,t=!1){let r="http://localhost";typeof window<"u"&&(r=window.location.origin!=="null"?window.location.origin:window.location.href),J(r,"No window.location.(origin|href) available to create URL");let a=typeof e=="string"?e:ke(e);return a=a.replace(/ $/,"%20"),!t&&a.startsWith("//")&&(a=r+a),new URL(a,r)}var it,Yr=class{constructor(e){if(La(this,it,new Map),e)for(let[t,r]of e)this.set(t,r)}get(e){if(Yt(this,it).has(e))return Yt(this,it).get(e);if(e.defaultValue!==void 0)return e.defaultValue;throw new Error("No value found for context")}set(e,t){Yt(this,it).set(e,t)}};it=new WeakMap;var Ta=new Set(["lazy","caseSensitive","path","id","index","children"]);function Ma(e){return Ta.has(e)}var _a=new Set(["lazy","caseSensitive","path","id","index","unstable_middleware","children"]);function Da(e){return _a.has(e)}function Oa(e){return e.index===!0}function Mt(e,t,r=[],a={}){return e.map((n,o)=>{let l=[...r,String(o)],s=typeof n.id=="string"?n.id:l.join("-");if(J(n.index!==!0||!n.children,"Cannot specify children on an index route"),J(!a[s],`Found a route id collision on id "${s}". Route id's must be globally unique within Data Router usages`),Oa(n)){let i={...n,...t(n),id:s};return a[s]=i,i}else{let i={...n,...t(n),id:s,children:void 0};return a[s]=i,n.children&&(i.children=Mt(n.children,t,l,a)),i}})}function Le(e,t,r="/"){return xt(e,t,r,!1)}function xt(e,t,r,a){let n=typeof t=="string"?Ne(t):t,o=ge(n.pathname||"/",r);if(o==null)return null;let l=gn(e);ka(l);let s=null;for(let i=0;s==null&&i{let i={relativePath:s===void 0?o.path||"":s,caseSensitive:o.caseSensitive===!0,childrenIndex:l,route:o};i.relativePath.startsWith("/")&&(J(i.relativePath.startsWith(a),`Absolute route path "${i.relativePath}" nested under path "${a}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`),i.relativePath=i.relativePath.slice(a.length));let u=Ce([a,i.relativePath]),c=r.concat(i);o.children&&o.children.length>0&&(J(o.index!==!0,`Index routes must not have child routes. Please remove all child routes from route path "${u}".`),gn(o.children,t,c,u)),!(o.path==null&&!o.index)&&t.push({path:u,score:Ha(u,o.index),routesMeta:c})};return e.forEach((o,l)=>{var s;if(o.path===""||!((s=o.path)!=null&&s.includes("?")))n(o,l);else for(let i of wn(o.path))n(o,l,i)}),t}function wn(e){let t=e.split("/");if(t.length===0)return[];let[r,...a]=t,n=r.endsWith("?"),o=r.replace(/\?$/,"");if(a.length===0)return n?[o,""]:[o];let l=wn(a.join("/")),s=[];return s.push(...l.map(i=>i===""?o:[o,i].join("/"))),n&&s.push(...l),s.map(i=>e.startsWith("/")&&i===""?"/":i)}function ka(e){e.sort((t,r)=>t.score!==r.score?r.score-t.score:Ua(t.routesMeta.map(a=>a.childrenIndex),r.routesMeta.map(a=>a.childrenIndex)))}var Aa=/^:[\w-]+$/,Na=3,Ia=2,$a=1,Fa=10,ja=-2,Vr=e=>e==="*";function Ha(e,t){let r=e.split("/"),a=r.length;return r.some(Vr)&&(a+=ja),t&&(a+=Ia),r.filter(n=>!Vr(n)).reduce((n,o)=>n+(Aa.test(o)?Na:o===""?$a:Fa),a)}function Ua(e,t){return e.length===t.length&&e.slice(0,-1).every((a,n)=>a===t[n])?e[e.length-1]-t[t.length-1]:0}function za(e,t,r=!1){let{routesMeta:a}=e,n={},o="/",l=[];for(let s=0;s{if(c==="*"){let w=s[f]||"";l=o.slice(0,o.length-w.length).replace(/(.)\/+$/,"$1")}const g=s[f];return p&&!g?u[c]=void 0:u[c]=(g||"").replace(/%2F/g,"/"),u},{}),pathname:o,pathnameBase:l,pattern:e}}function En(e,t=!1,r=!0){ae(e==="*"||!e.endsWith("*")||e.endsWith("/*"),`Route path "${e}" will be treated as if it were "${e.replace(/\*$/,"/*")}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${e.replace(/\*$/,"/*")}".`);let a=[],n="^"+e.replace(/\/*\*?$/,"").replace(/^\/*/,"/").replace(/[\\.*+^${}|()[\]]/g,"\\$&").replace(/\/:([\w-]+)(\?)?/g,(l,s,i)=>(a.push({paramName:s,isOptional:i!=null}),i?"/?([^\\/]+)?":"/([^\\/]+)"));return e.endsWith("*")?(a.push({paramName:"*"}),n+=e==="*"||e==="/*"?"(.*)$":"(?:\\/(.+)|\\/*)$"):r?n+="\\/*$":e!==""&&e!=="/"&&(n+="(?:(?=\\/|$))"),[new RegExp(n,t?void 0:"i"),a]}function Ba(e){try{return e.split("/").map(t=>decodeURIComponent(t).replace(/\//g,"%2F")).join("/")}catch(t){return ae(!1,`The URL path "${e}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${t}).`),e}}function ge(e,t){if(t==="/")return e;if(!e.toLowerCase().startsWith(t.toLowerCase()))return null;let r=t.endsWith("/")?t.length-1:t.length,a=e.charAt(r);return a&&a!=="/"?null:e.slice(r)||"/"}function Wa(e,t="/"){let{pathname:r,search:a="",hash:n=""}=typeof e=="string"?Ne(e):e;return{pathname:r?r.startsWith("/")?r:Ya(r,t):t,search:Ja(a),hash:Ga(n)}}function Ya(e,t){let r=t.replace(/\/+$/,"").split("/");return e.split("/").forEach(n=>{n===".."?r.length>1&&r.pop():n!=="."&&r.push(n)}),r.length>1?r.join("/"):"/"}function Vt(e,t,r,a){return`Cannot include a '${e}' character in a manually specified \`to.${t}\` field [${JSON.stringify(a)}]. Please separate it out to the \`to.${r}\` field. Alternatively you may provide the full path as a string in and the router will parse it for you.`}function Rn(e){return e.filter((t,r)=>r===0||t.route.path&&t.route.path.length>0)}function ur(e){let t=Rn(e);return t.map((r,a)=>a===t.length-1?r.pathname:r.pathnameBase)}function cr(e,t,r,a=!1){let n;typeof e=="string"?n=Ne(e):(n={...e},J(!n.pathname||!n.pathname.includes("?"),Vt("?","pathname","search",n)),J(!n.pathname||!n.pathname.includes("#"),Vt("#","pathname","hash",n)),J(!n.search||!n.search.includes("#"),Vt("#","search","hash",n)));let o=e===""||n.pathname==="",l=o?"/":n.pathname,s;if(l==null)s=r;else{let p=t.length-1;if(!a&&l.startsWith("..")){let f=l.split("/");for(;f[0]==="..";)f.shift(),p-=1;n.pathname=f.join("/")}s=p>=0?t[p]:"/"}let i=Wa(n,s),u=l&&l!=="/"&&l.endsWith("/"),c=(o||l===".")&&r.endsWith("/");return!i.pathname.endsWith("/")&&(u||c)&&(i.pathname+="/"),i}var Ce=e=>e.join("/").replace(/\/\/+/g,"/"),Va=e=>e.replace(/\/+$/,"").replace(/^\/*/,"/"),Ja=e=>!e||e==="?"?"":e.startsWith("?")?e:"?"+e,Ga=e=>!e||e==="#"?"":e.startsWith("#")?e:"#"+e,Xa=class{constructor(e,t){this.type="DataWithResponseInit",this.data=e,this.init=t||null}};function qa(e,t){return new Xa(e,typeof t=="number"?{status:t}:t)}var Ka=(e,t=302)=>{let r=t;typeof r=="number"?r={status:r}:typeof r.status>"u"&&(r.status=302);let a=new Headers(r.headers);return a.set("Location",e),new Response(null,{...r,headers:a})},Ae=class{constructor(e,t,r,a=!1){this.status=e,this.statusText=t||"",this.internal=a,r instanceof Error?(this.data=r.toString(),this.error=r):this.data=r}};function ze(e){return e!=null&&typeof e.status=="number"&&typeof e.statusText=="string"&&typeof e.internal=="boolean"&&"data"in e}var bn=["POST","PUT","PATCH","DELETE"],Qa=new Set(bn),Za=["GET",...bn],eo=new Set(Za),to=new Set([301,302,303,307,308]),ro=new Set([307,308]),Jt={state:"idle",location:void 0,formMethod:void 0,formAction:void 0,formEncType:void 0,formData:void 0,json:void 0,text:void 0},no={state:"idle",data:void 0,formMethod:void 0,formAction:void 0,formEncType:void 0,formData:void 0,json:void 0,text:void 0},nt={state:"unblocked",proceed:void 0,reset:void 0,location:void 0},dr=/^(?:[a-z][a-z0-9+.-]*:|\/\/)/i,ao=e=>({hasErrorBoundary:!!e.hasErrorBoundary}),Sn="remix-router-transitions",xn=Symbol("ResetLoaderData");function ml(e){const t=e.window?e.window:typeof window<"u"?window:void 0,r=typeof t<"u"&&typeof t.document<"u"&&typeof t.document.createElement<"u";J(e.routes.length>0,"You must provide a non-empty routes array to createRouter");let a=e.hydrationRouteProperties||[],n=e.mapRouteProperties||ao,o={},l=Mt(e.routes,n,void 0,o),s,i=e.basename||"/",u=e.dataStrategy||uo,c={unstable_middleware:!1,...e.future},p=null,f=new Set,g=null,w=null,b=null,S=e.hydrationData!=null,R=Le(l,e.history.location,i),O=!1,T=null,M;if(R==null&&!e.patchRoutesOnNavigation){let d=Re(404,{pathname:e.history.location.pathname}),{matches:v,route:E}=an(l);M=!0,R=v,T={[E.id]:d}}else if(R&&!e.hydrationData&&pt(R,l,e.history.location.pathname).active&&(R=null),R)if(R.some(d=>d.route.lazy))M=!1;else if(!R.some(d=>d.route.loader))M=!0;else{let d=e.hydrationData?e.hydrationData.loaderData:null,v=e.hydrationData?e.hydrationData.errors:null;if(v){let E=R.findIndex(C=>v[C.route.id]!==void 0);M=R.slice(0,E+1).every(C=>!er(C.route,d,v))}else M=R.every(E=>!er(E.route,d,v))}else{M=!1,R=[];let d=pt(null,l,e.history.location.pathname);d.active&&d.matches&&(O=!0,R=d.matches)}let x,y={historyAction:e.history.action,location:e.history.location,matches:R,initialized:M,navigation:Jt,restoreScrollPosition:e.hydrationData!=null?!1:null,preventScrollReset:!1,revalidation:"idle",loaderData:e.hydrationData&&e.hydrationData.loaderData||{},actionData:e.hydrationData&&e.hydrationData.actionData||null,errors:e.hydrationData&&e.hydrationData.errors||T,fetchers:new Map,blockers:new Map},D="POP",U=!1,j,K=!1,re=new Map,se=null,oe=!1,X=!1,Q=new Set,G=new Map,ue=0,de=-1,ce=new Map,m=new Set,L=new Map,A=new Map,k=new Set,Y=new Map,ee,z=null;function xe(){if(p=e.history.listen(({action:d,location:v,delta:E})=>{if(ee){ee(),ee=void 0;return}ae(Y.size===0||E!=null,"You are trying to use a blocker on a POP navigation to a location that was not created by @remix-run/router. This will fail silently in production. This can happen if you are navigating outside the router via `window.history.pushState`/`window.location.hash` instead of using router navigation APIs. This can also happen if you are using createHashRouter and the user manually changes the URL.");let C=Ar({currentLocation:y.location,nextLocation:v,historyAction:d});if(C&&E!=null){let P=new Promise(N=>{ee=N});e.history.go(E*-1),mt(C,{state:"blocked",location:v,proceed(){mt(C,{state:"proceeding",proceed:void 0,reset:void 0,location:v}),P.then(()=>e.history.go(E))},reset(){let N=new Map(y.blockers);N.set(C,nt),he({blockers:N})}});return}return Ie(d,v)}),r){Ro(t,re);let d=()=>bo(t,re);t.addEventListener("pagehide",d),se=()=>t.removeEventListener("pagehide",d)}return y.initialized||Ie("POP",y.location,{initialHydration:!0}),x}function ne(){p&&p(),se&&se(),f.clear(),j&&j.abort(),y.fetchers.forEach((d,v)=>jt(v)),y.blockers.forEach((d,v)=>kr(v))}function $t(d){return f.add(d),()=>f.delete(d)}function he(d,v={}){y={...y,...d};let E=[],C=[];y.fetchers.forEach((P,N)=>{P.state==="idle"&&(k.has(N)?E.push(N):C.push(N))}),k.forEach(P=>{!y.fetchers.has(P)&&!G.has(P)&&E.push(P)}),[...f].forEach(P=>P(y,{deletedFetchers:E,viewTransitionOpts:v.viewTransitionOpts,flushSync:v.flushSync===!0})),E.forEach(P=>jt(P)),C.forEach(P=>y.fetchers.delete(P))}function We(d,v,{flushSync:E}={}){var F,B;let C=y.actionData!=null&&y.navigation.formMethod!=null&&ve(y.navigation.formMethod)&&y.navigation.state==="loading"&&((F=d.state)==null?void 0:F._isRedirect)!==!0,P;v.actionData?Object.keys(v.actionData).length>0?P=v.actionData:P=null:C?P=y.actionData:P=null;let N=v.loaderData?rn(y.loaderData,v.loaderData,v.matches||[],v.errors):y.loaderData,H=y.blockers;H.size>0&&(H=new Map(H),H.forEach(($,V)=>H.set(V,nt)));let _=U===!0||y.navigation.formMethod!=null&&ve(y.navigation.formMethod)&&((B=d.state)==null?void 0:B._isRedirect)!==!0;s&&(l=s,s=void 0),oe||D==="POP"||(D==="PUSH"?e.history.push(d,d.state):D==="REPLACE"&&e.history.replace(d,d.state));let I;if(D==="POP"){let $=re.get(y.location.pathname);$&&$.has(d.pathname)?I={currentLocation:y.location,nextLocation:d}:re.has(d.pathname)&&(I={currentLocation:d,nextLocation:y.location})}else if(K){let $=re.get(y.location.pathname);$?$.add(d.pathname):($=new Set([d.pathname]),re.set(y.location.pathname,$)),I={currentLocation:y.location,nextLocation:d}}he({...v,actionData:P,loaderData:N,historyAction:D,location:d,initialized:!0,navigation:Jt,revalidation:"idle",restoreScrollPosition:Ir(d,v.matches||y.matches),preventScrollReset:_,blockers:H},{viewTransitionOpts:I,flushSync:E===!0}),D="POP",U=!1,K=!1,oe=!1,X=!1,z==null||z.resolve(),z=null}async function Cr(d,v){if(typeof d=="number"){e.history.go(d);return}let E=Zt(y.location,y.matches,i,d,v==null?void 0:v.fromRouteId,v==null?void 0:v.relative),{path:C,submission:P,error:N}=Jr(!1,E,v),H=y.location,_=st(y.location,C,v&&v.state);_={..._,...e.history.encodeLocation(_)};let I=v&&v.replace!=null?v.replace:void 0,F="PUSH";I===!0?F="REPLACE":I===!1||P!=null&&ve(P.formMethod)&&P.formAction===y.location.pathname+y.location.search&&(F="REPLACE");let B=v&&"preventScrollReset"in v?v.preventScrollReset===!0:void 0,$=(v&&v.flushSync)===!0,V=Ar({currentLocation:H,nextLocation:_,historyAction:F});if(V){mt(V,{state:"blocked",location:_,proceed(){mt(V,{state:"proceeding",proceed:void 0,reset:void 0,location:_}),Cr(d,v)},reset(){let te=new Map(y.blockers);te.set(V,nt),he({blockers:te})}});return}await Ie(F,_,{submission:P,pendingError:N,preventScrollReset:B,replace:v&&v.replace,enableViewTransition:v&&v.viewTransition,flushSync:$})}function oa(){z||(z=So()),Ft(),he({revalidation:"loading"});let d=z.promise;return y.navigation.state==="submitting"?d:y.navigation.state==="idle"?(Ie(y.historyAction,y.location,{startUninterruptedRevalidation:!0}),d):(Ie(D||y.historyAction,y.navigation.location,{overrideNavigation:y.navigation,enableViewTransition:K===!0}),d)}async function Ie(d,v,E){j&&j.abort(),j=null,D=d,oe=(E&&E.startUninterruptedRevalidation)===!0,pa(y.location,y.matches),U=(E&&E.preventScrollReset)===!0,K=(E&&E.enableViewTransition)===!0;let C=s||l,P=E&&E.overrideNavigation,N=E!=null&&E.initialHydration&&y.matches&&y.matches.length>0&&!O?y.matches:Le(C,v,i),H=(E&&E.flushSync)===!0;if(N&&y.initialized&&!X&&yo(y.location,v)&&!(E&&E.submission&&ve(E.submission.formMethod))){We(v,{matches:N},{flushSync:H});return}let _=pt(N,C,v.pathname);if(_.active&&_.matches&&(N=_.matches),!N){let{error:fe,notFoundMatches:pe,route:q}=Ht(v.pathname);We(v,{matches:pe,loaderData:{},errors:{[q.id]:fe}},{flushSync:H});return}j=new AbortController;let I=Ve(e.history,v,j.signal,E&&E.submission),F=new Yr(e.unstable_getContext?await e.unstable_getContext():void 0),B;if(E&&E.pendingError)B=[He(N).route.id,{type:"error",error:E.pendingError}];else if(E&&E.submission&&ve(E.submission.formMethod)){let fe=await ia(I,v,E.submission,N,F,_.active,E&&E.initialHydration===!0,{replace:E.replace,flushSync:H});if(fe.shortCircuited)return;if(fe.pendingActionResult){let[pe,q]=fe.pendingActionResult;if(ye(q)&&ze(q.error)&&q.error.status===404){j=null,We(v,{matches:fe.matches,loaderData:{},errors:{[pe]:q.error}});return}}N=fe.matches||N,B=fe.pendingActionResult,P=Gt(v,E.submission),H=!1,_.active=!1,I=Ve(e.history,I.url,I.signal)}let{shortCircuited:$,matches:V,loaderData:te,errors:le}=await la(I,v,N,F,_.active,P,E&&E.submission,E&&E.fetcherSubmission,E&&E.replace,E&&E.initialHydration===!0,H,B);$||(j=null,We(v,{matches:V||N,...nn(B),loaderData:te,errors:le}))}async function ia(d,v,E,C,P,N,H,_={}){Ft();let I=wo(v,E);if(he({navigation:I},{flushSync:_.flushSync===!0}),N){let $=await yt(C,v.pathname,d.signal);if($.type==="aborted")return{shortCircuited:!0};if($.type==="error"){let V=He($.partialMatches).route.id;return{matches:$.partialMatches,pendingActionResult:[V,{type:"error",error:$.error}]}}else if($.matches)C=$.matches;else{let{notFoundMatches:V,error:te,route:le}=Ht(v.pathname);return{matches:V,pendingActionResult:[le.id,{type:"error",error:te}]}}}let F,B=lt(C,v);if(!B.route.action&&!B.route.lazy)F={type:"error",error:Re(405,{method:d.method,pathname:v.pathname,routeId:B.route.id})};else{let $=Je(n,o,d,C,B,H?[]:a,P),V=await qe(d,$,P,null);if(F=V[B.route.id],!F){for(let te of C)if(V[te.route.id]){F=V[te.route.id];break}}if(d.signal.aborted)return{shortCircuited:!0}}if(Ue(F)){let $;return _&&_.replace!=null?$=_.replace:$=Zr(F.response.headers.get("Location"),new URL(d.url),i)===y.location.pathname+y.location.search,await $e(d,F,!0,{submission:E,replace:$}),{shortCircuited:!0}}if(ye(F)){let $=He(C,B.route.id);return(_&&_.replace)!==!0&&(D="PUSH"),{matches:C,pendingActionResult:[$.route.id,F,B.route.id]}}return{matches:C,pendingActionResult:[B.route.id,F]}}async function la(d,v,E,C,P,N,H,_,I,F,B,$){let V=N||Gt(v,H),te=H||_||on(V),le=!oe&&!F;if(P){if(le){let Ee=Pr($);he({navigation:V,...Ee!==void 0?{actionData:Ee}:{}},{flushSync:B})}let Z=await yt(E,v.pathname,d.signal);if(Z.type==="aborted")return{shortCircuited:!0};if(Z.type==="error"){let Ee=He(Z.partialMatches).route.id;return{matches:Z.partialMatches,loaderData:{},errors:{[Ee]:Z.error}}}else if(Z.matches)E=Z.matches;else{let{error:Ee,notFoundMatches:De,route:gt}=Ht(v.pathname);return{matches:De,loaderData:{},errors:{[gt.id]:Ee}}}}let fe=s||l,{dsMatches:pe,revalidatingFetchers:q}=Gr(d,C,n,o,e.history,y,E,te,v,F?[]:a,F===!0,X,Q,k,L,m,fe,i,e.patchRoutesOnNavigation!=null,$);if(de=++ue,!e.dataStrategy&&!pe.some(Z=>Z.shouldLoad)&&q.length===0){let Z=Dr();return We(v,{matches:E,loaderData:{},errors:$&&ye($[1])?{[$[0]]:$[1].error}:null,...nn($),...Z?{fetchers:new Map(y.fetchers)}:{}},{flushSync:B}),{shortCircuited:!0}}if(le){let Z={};if(!P){Z.navigation=V;let Ee=Pr($);Ee!==void 0&&(Z.actionData=Ee)}q.length>0&&(Z.fetchers=sa(q)),he(Z,{flushSync:B})}q.forEach(Z=>{_e(Z.key),Z.controller&&G.set(Z.key,Z.controller)});let Ke=()=>q.forEach(Z=>_e(Z.key));j&&j.signal.addEventListener("abort",Ke);let{loaderResults:Fe,fetcherResults:Qe}=await Tr(pe,q,d,C);if(d.signal.aborted)return{shortCircuited:!0};j&&j.signal.removeEventListener("abort",Ke),q.forEach(Z=>G.delete(Z.key));let we=wt(Fe);if(we)return await $e(d,we.result,!0,{replace:I}),{shortCircuited:!0};if(we=wt(Qe),we)return m.add(we.key),await $e(d,we.result,!0,{replace:I}),{shortCircuited:!0};let{loaderData:Ze,errors:et}=tn(y,E,Fe,$,q,Qe);F&&y.errors&&(et={...y.errors,...et});let Ut=Dr(),je=Or(de),vt=Ut||je||q.length>0;return{matches:E,loaderData:Ze,errors:et,...vt?{fetchers:new Map(y.fetchers)}:{}}}function Pr(d){if(d&&!ye(d[1]))return{[d[0]]:d[1].data};if(y.actionData)return Object.keys(y.actionData).length===0?null:y.actionData}function sa(d){return d.forEach(v=>{let E=y.fetchers.get(v.key),C=at(void 0,E?E.data:void 0);y.fetchers.set(v.key,C)}),new Map(y.fetchers)}async function ua(d,v,E,C){_e(d);let P=(C&&C.flushSync)===!0,N=s||l,H=Zt(y.location,y.matches,i,E,v,C==null?void 0:C.relative),_=Le(N,H,i),I=pt(_,N,H);if(I.active&&I.matches&&(_=I.matches),!_){Te(d,v,Re(404,{pathname:H}),{flushSync:P});return}let{path:F,submission:B,error:$}=Jr(!0,H,C);if($){Te(d,v,$,{flushSync:P});return}let V=lt(_,F),te=new Yr(e.unstable_getContext?await e.unstable_getContext():void 0),le=(C&&C.preventScrollReset)===!0;if(B&&ve(B.formMethod)){await ca(d,v,F,V,_,te,I.active,P,le,B);return}L.set(d,{routeId:v,path:F}),await da(d,v,F,V,_,te,I.active,P,le,B)}async function ca(d,v,E,C,P,N,H,_,I,F){Ft(),L.delete(d);function B(ie){if(!ie.route.action&&!ie.route.lazy){let Ye=Re(405,{method:F.formMethod,pathname:E,routeId:v});return Te(d,v,Ye,{flushSync:_}),!0}return!1}if(!H&&B(C))return;let $=y.fetchers.get(d);Me(d,Eo(F,$),{flushSync:_});let V=new AbortController,te=Ve(e.history,E,V.signal,F);if(H){let ie=await yt(P,E,te.signal,d);if(ie.type==="aborted")return;if(ie.type==="error"){Te(d,v,ie.error,{flushSync:_});return}else if(ie.matches){if(P=ie.matches,C=lt(P,E),B(C))return}else{Te(d,v,Re(404,{pathname:E}),{flushSync:_});return}}G.set(d,V);let le=ue,fe=Je(n,o,te,P,C,a,N),q=(await qe(te,fe,N,d))[C.route.id];if(te.signal.aborted){G.get(d)===V&&G.delete(d);return}if(k.has(d)){if(Ue(q)||ye(q)){Me(d,Oe(void 0));return}}else{if(Ue(q))if(G.delete(d),de>le){Me(d,Oe(void 0));return}else return m.add(d),Me(d,at(F)),$e(te,q,!1,{fetcherSubmission:F,preventScrollReset:I});if(ye(q)){Te(d,v,q.error);return}}let Ke=y.navigation.location||y.location,Fe=Ve(e.history,Ke,V.signal),Qe=s||l,we=y.navigation.state!=="idle"?Le(Qe,y.navigation.location,i):y.matches;J(we,"Didn't find any matches after fetcher action");let Ze=++ue;ce.set(d,Ze);let et=at(F,q.data);y.fetchers.set(d,et);let{dsMatches:Ut,revalidatingFetchers:je}=Gr(Fe,N,n,o,e.history,y,we,F,Ke,a,!1,X,Q,k,L,m,Qe,i,e.patchRoutesOnNavigation!=null,[C.route.id,q]);je.filter(ie=>ie.key!==d).forEach(ie=>{let Ye=ie.key,$r=y.fetchers.get(Ye),ga=at(void 0,$r?$r.data:void 0);y.fetchers.set(Ye,ga),_e(Ye),ie.controller&&G.set(Ye,ie.controller)}),he({fetchers:new Map(y.fetchers)});let vt=()=>je.forEach(ie=>_e(ie.key));V.signal.addEventListener("abort",vt);let{loaderResults:Z,fetcherResults:Ee}=await Tr(Ut,je,Fe,N);if(V.signal.aborted)return;if(V.signal.removeEventListener("abort",vt),ce.delete(d),G.delete(d),je.forEach(ie=>G.delete(ie.key)),y.fetchers.has(d)){let ie=Oe(q.data);y.fetchers.set(d,ie)}let De=wt(Z);if(De)return $e(Fe,De.result,!1,{preventScrollReset:I});if(De=wt(Ee),De)return m.add(De.key),$e(Fe,De.result,!1,{preventScrollReset:I});let{loaderData:gt,errors:zt}=tn(y,we,Z,void 0,je,Ee);Or(Ze),y.navigation.state==="loading"&&Ze>de?(J(D,"Expected pending action"),j&&j.abort(),We(y.navigation.location,{matches:we,loaderData:gt,errors:zt,fetchers:new Map(y.fetchers)})):(he({errors:zt,loaderData:rn(y.loaderData,gt,we,zt),fetchers:new Map(y.fetchers)}),X=!1)}async function da(d,v,E,C,P,N,H,_,I,F){let B=y.fetchers.get(d);Me(d,at(F,B?B.data:void 0),{flushSync:_});let $=new AbortController,V=Ve(e.history,E,$.signal);if(H){let q=await yt(P,E,V.signal,d);if(q.type==="aborted")return;if(q.type==="error"){Te(d,v,q.error,{flushSync:_});return}else if(q.matches)P=q.matches,C=lt(P,E);else{Te(d,v,Re(404,{pathname:E}),{flushSync:_});return}}G.set(d,$);let te=ue,le=Je(n,o,V,P,C,a,N),pe=(await qe(V,le,N,d))[C.route.id];if(G.get(d)===$&&G.delete(d),!V.signal.aborted){if(k.has(d)){Me(d,Oe(void 0));return}if(Ue(pe))if(de>te){Me(d,Oe(void 0));return}else{m.add(d),await $e(V,pe,!1,{preventScrollReset:I});return}if(ye(pe)){Te(d,v,pe.error);return}Me(d,Oe(pe.data))}}async function $e(d,v,E,{submission:C,fetcherSubmission:P,preventScrollReset:N,replace:H}={}){v.response.headers.has("X-Remix-Revalidate")&&(X=!0);let _=v.response.headers.get("Location");J(_,"Expected a Location header on the redirect Response"),_=Zr(_,new URL(d.url),i);let I=st(y.location,_,{_isRedirect:!0});if(r){let le=!1;if(v.response.headers.has("X-Remix-Reload-Document"))le=!0;else if(dr.test(_)){const fe=yn(_,!0);le=fe.origin!==t.location.origin||ge(fe.pathname,i)==null}if(le){H?t.location.replace(_):t.location.assign(_);return}}j=null;let F=H===!0||v.response.headers.has("X-Remix-Replace")?"REPLACE":"PUSH",{formMethod:B,formAction:$,formEncType:V}=y.navigation;!C&&!P&&B&&$&&V&&(C=on(y.navigation));let te=C||P;if(ro.has(v.response.status)&&te&&ve(te.formMethod))await Ie(F,I,{submission:{...te,formAction:_},preventScrollReset:N||U,enableViewTransition:E?K:void 0});else{let le=Gt(I,C);await Ie(F,I,{overrideNavigation:le,fetcherSubmission:P,preventScrollReset:N||U,enableViewTransition:E?K:void 0})}}async function qe(d,v,E,C){let P,N={};try{P=await co(u,d,v,C,E,!1)}catch(H){return v.filter(_=>_.shouldLoad).forEach(_=>{N[_.route.id]={type:"error",error:H}}),N}if(d.signal.aborted)return N;for(let[H,_]of Object.entries(P))if(vo(_)){let I=_.result;N[H]={type:"redirect",response:mo(I,d,H,v,i)}}else N[H]=await ho(_);return N}async function Tr(d,v,E,C){let P=qe(E,d,C,null),N=Promise.all(v.map(async I=>{if(I.matches&&I.match&&I.request&&I.controller){let B=(await qe(I.request,I.matches,C,I.key))[I.match.route.id];return{[I.key]:B}}else return Promise.resolve({[I.key]:{type:"error",error:Re(404,{pathname:I.path})}})})),H=await P,_=(await N).reduce((I,F)=>Object.assign(I,F),{});return{loaderResults:H,fetcherResults:_}}function Ft(){X=!0,L.forEach((d,v)=>{G.has(v)&&Q.add(v),_e(v)})}function Me(d,v,E={}){y.fetchers.set(d,v),he({fetchers:new Map(y.fetchers)},{flushSync:(E&&E.flushSync)===!0})}function Te(d,v,E,C={}){let P=He(y.matches,v);jt(d),he({errors:{[P.route.id]:E},fetchers:new Map(y.fetchers)},{flushSync:(C&&C.flushSync)===!0})}function Mr(d){return A.set(d,(A.get(d)||0)+1),k.has(d)&&k.delete(d),y.fetchers.get(d)||no}function jt(d){let v=y.fetchers.get(d);G.has(d)&&!(v&&v.state==="loading"&&ce.has(d))&&_e(d),L.delete(d),ce.delete(d),m.delete(d),k.delete(d),Q.delete(d),y.fetchers.delete(d)}function fa(d){let v=(A.get(d)||0)-1;v<=0?(A.delete(d),k.add(d)):A.set(d,v),he({fetchers:new Map(y.fetchers)})}function _e(d){let v=G.get(d);v&&(v.abort(),G.delete(d))}function _r(d){for(let v of d){let E=Mr(v),C=Oe(E.data);y.fetchers.set(v,C)}}function Dr(){let d=[],v=!1;for(let E of m){let C=y.fetchers.get(E);J(C,`Expected fetcher: ${E}`),C.state==="loading"&&(m.delete(E),d.push(E),v=!0)}return _r(d),v}function Or(d){let v=[];for(let[E,C]of ce)if(C0}function ha(d,v){let E=y.blockers.get(d)||nt;return Y.get(d)!==v&&Y.set(d,v),E}function kr(d){y.blockers.delete(d),Y.delete(d)}function mt(d,v){let E=y.blockers.get(d)||nt;J(E.state==="unblocked"&&v.state==="blocked"||E.state==="blocked"&&v.state==="blocked"||E.state==="blocked"&&v.state==="proceeding"||E.state==="blocked"&&v.state==="unblocked"||E.state==="proceeding"&&v.state==="unblocked",`Invalid blocker state transition: ${E.state} -> ${v.state}`);let C=new Map(y.blockers);C.set(d,v),he({blockers:C})}function Ar({currentLocation:d,nextLocation:v,historyAction:E}){if(Y.size===0)return;Y.size>1&&ae(!1,"A router only supports one blocker at a time");let C=Array.from(Y.entries()),[P,N]=C[C.length-1],H=y.blockers.get(P);if(!(H&&H.state==="proceeding")&&N({currentLocation:d,nextLocation:v,historyAction:E}))return P}function Ht(d){let v=Re(404,{pathname:d}),E=s||l,{matches:C,route:P}=an(E);return{notFoundMatches:C,route:P,error:v}}function ma(d,v,E){if(g=d,b=v,w=E||null,!S&&y.navigation===Jt){S=!0;let C=Ir(y.location,y.matches);C!=null&&he({restoreScrollPosition:C})}return()=>{g=null,b=null,w=null}}function Nr(d,v){return w&&w(d,v.map(C=>vn(C,y.loaderData)))||d.key}function pa(d,v){if(g&&b){let E=Nr(d,v);g[E]=b()}}function Ir(d,v){if(g){let E=Nr(d,v),C=g[E];if(typeof C=="number")return C}return null}function pt(d,v,E){if(e.patchRoutesOnNavigation)if(d){if(Object.keys(d[0].params).length>0)return{active:!0,matches:xt(v,E,i,!0)}}else return{active:!0,matches:xt(v,E,i,!0)||[]};return{active:!1,matches:null}}async function yt(d,v,E,C){if(!e.patchRoutesOnNavigation)return{type:"success",matches:d};let P=d;for(;;){let N=s==null,H=s||l,_=o;try{await e.patchRoutesOnNavigation({signal:E,path:v,matches:P,fetcherKey:C,patch:(B,$)=>{E.aborted||Xr(B,$,H,_,n)}})}catch(B){return{type:"error",error:B,partialMatches:P}}finally{N&&!E.aborted&&(l=[...l])}if(E.aborted)return{type:"aborted"};let I=Le(H,v,i);if(I)return{type:"success",matches:I};let F=xt(H,v,i,!0);if(!F||P.length===F.length&&P.every((B,$)=>B.route.id===F[$].route.id))return{type:"success",matches:null};P=F}}function ya(d){o={},s=Mt(d,n,void 0,o)}function va(d,v){let E=s==null;Xr(d,v,s||l,o,n),E&&(l=[...l],he({}))}return x={get basename(){return i},get future(){return c},get state(){return y},get routes(){return l},get window(){return t},initialize:xe,subscribe:$t,enableScrollRestoration:ma,navigate:Cr,fetch:ua,revalidate:oa,createHref:d=>e.history.createHref(d),encodeLocation:d=>e.history.encodeLocation(d),getFetcher:Mr,deleteFetcher:fa,dispose:ne,getBlocker:ha,deleteBlocker:kr,patchRoutes:va,_internalFetchControllers:G,_internalSetRoutes:ya},x}function oo(e){return e!=null&&("formData"in e&&e.formData!=null||"body"in e&&e.body!==void 0)}function Zt(e,t,r,a,n,o){let l,s;if(n){l=[];for(let u of t)if(l.push(u),u.route.id===n){s=u;break}}else l=t,s=t[t.length-1];let i=cr(a||".",ur(l),ge(e.pathname,r)||e.pathname,o==="path");if(a==null&&(i.search=e.search,i.hash=e.hash),(a==null||a===""||a===".")&&s){let u=hr(i.search);if(s.route.index&&!u)i.search=i.search?i.search.replace(/^\?/,"?index&"):"?index";else if(!s.route.index&&u){let c=new URLSearchParams(i.search),p=c.getAll("index");c.delete("index"),p.filter(g=>g).forEach(g=>c.append("index",g));let f=c.toString();i.search=f?`?${f}`:""}}return r!=="/"&&(i.pathname=i.pathname==="/"?r:Ce([r,i.pathname])),ke(i)}function Jr(e,t,r){if(!r||!oo(r))return{path:t};if(r.formMethod&&!go(r.formMethod))return{path:t,error:Re(405,{method:r.formMethod})};let a=()=>({path:t,error:Re(400,{type:"invalid-body"})}),o=(r.formMethod||"get").toUpperCase(),l=_n(t);if(r.body!==void 0){if(r.formEncType==="text/plain"){if(!ve(o))return a();let p=typeof r.body=="string"?r.body:r.body instanceof FormData||r.body instanceof URLSearchParams?Array.from(r.body.entries()).reduce((f,[g,w])=>`${f}${g}=${w} +`,""):String(r.body);return{path:t,submission:{formMethod:o,formAction:l,formEncType:r.formEncType,formData:void 0,json:void 0,text:p}}}else if(r.formEncType==="application/json"){if(!ve(o))return a();try{let p=typeof r.body=="string"?JSON.parse(r.body):r.body;return{path:t,submission:{formMethod:o,formAction:l,formEncType:r.formEncType,formData:void 0,json:p,text:void 0}}}catch{return a()}}}J(typeof FormData=="function","FormData is not available in this environment");let s,i;if(r.formData)s=rr(r.formData),i=r.formData;else if(r.body instanceof FormData)s=rr(r.body),i=r.body;else if(r.body instanceof URLSearchParams)s=r.body,i=en(s);else if(r.body==null)s=new URLSearchParams,i=new FormData;else try{s=new URLSearchParams(r.body),i=en(s)}catch{return a()}let u={formMethod:o,formAction:l,formEncType:r&&r.formEncType||"application/x-www-form-urlencoded",formData:i,json:void 0,text:void 0};if(ve(u.formMethod))return{path:t,submission:u};let c=Ne(t);return e&&c.search&&hr(c.search)&&s.append("index",""),c.search=`?${s}`,{path:ke(c),submission:u}}function Gr(e,t,r,a,n,o,l,s,i,u,c,p,f,g,w,b,S,R,O,T){var oe;let M=T?ye(T[1])?T[1].error:T[1].data:void 0,x=n.createURL(o.location),y=n.createURL(i),D;if(c&&o.errors){let X=Object.keys(o.errors)[0];D=l.findIndex(Q=>Q.route.id===X)}else if(T&&ye(T[1])){let X=T[0];D=l.findIndex(Q=>Q.route.id===X)-1}let U=T?T[1].statusCode:void 0,j=U&&U>=400,K={currentUrl:x,currentParams:((oe=o.matches[0])==null?void 0:oe.params)||{},nextUrl:y,nextParams:l[0].params,...s,actionResult:M,actionStatus:U},re=l.map((X,Q)=>{let{route:G}=X,ue=null;if(D!=null&&Q>D?ue=!1:G.lazy?ue=!0:G.loader==null?ue=!1:c?ue=er(G,o.loaderData,o.errors):io(o.loaderData,o.matches[Q],X)&&(ue=!0),ue!==null)return tr(r,a,e,X,u,t,ue);let de=j?!1:p||x.pathname+x.search===y.pathname+y.search||x.search!==y.search||lo(o.matches[Q],X),ce={...K,defaultShouldRevalidate:de},m=Dt(X,ce);return tr(r,a,e,X,u,t,m,ce)}),se=[];return w.forEach((X,Q)=>{if(c||!l.some(k=>k.route.id===X.routeId)||g.has(Q))return;let G=o.fetchers.get(Q),ue=G&&G.state!=="idle"&&G.data===void 0,de=Le(S,X.path,R);if(!de){if(O&&ue)return;se.push({key:Q,routeId:X.routeId,path:X.path,matches:null,match:null,request:null,controller:null});return}if(b.has(Q))return;let ce=lt(de,X.path),m=new AbortController,L=Ve(n,X.path,m.signal),A=null;if(f.has(Q))f.delete(Q),A=Je(r,a,L,de,ce,u,t);else if(ue)p&&(A=Je(r,a,L,de,ce,u,t));else{let k={...K,defaultShouldRevalidate:j?!1:p};Dt(ce,k)&&(A=Je(r,a,L,de,ce,u,t,k))}A&&se.push({key:Q,routeId:X.routeId,path:X.path,matches:A,match:ce,request:L,controller:m})}),{dsMatches:re,revalidatingFetchers:se}}function er(e,t,r){if(e.lazy)return!0;if(!e.loader)return!1;let a=t!=null&&e.id in t,n=r!=null&&r[e.id]!==void 0;return!a&&n?!1:typeof e.loader=="function"&&e.loader.hydrate===!0?!0:!a&&!n}function io(e,t,r){let a=!t||r.route.id!==t.route.id,n=!e.hasOwnProperty(r.route.id);return a||n}function lo(e,t){let r=e.route.path;return e.pathname!==t.pathname||r!=null&&r.endsWith("*")&&e.params["*"]!==t.params["*"]}function Dt(e,t){if(e.route.shouldRevalidate){let r=e.route.shouldRevalidate(t);if(typeof r=="boolean")return r}return t.defaultShouldRevalidate}function Xr(e,t,r,a,n){let o;if(e){let i=a[e];J(i,`No route found to patch children into: routeId = ${e}`),i.children||(i.children=[]),o=i.children}else o=r;let l=t.filter(i=>!o.some(u=>Ln(i,u))),s=Mt(l,n,[e||"_","patch",String((o==null?void 0:o.length)||"0")],a);o.push(...s)}function Ln(e,t){return"id"in e&&"id"in t&&e.id===t.id?!0:e.index===t.index&&e.path===t.path&&e.caseSensitive===t.caseSensitive?(!e.children||e.children.length===0)&&(!t.children||t.children.length===0)?!0:e.children.every((r,a)=>{var n;return(n=t.children)==null?void 0:n.some(o=>Ln(r,o))}):!1}var qr=new WeakMap,Cn=({key:e,route:t,manifest:r,mapRouteProperties:a})=>{let n=r[t.id];if(J(n,"No route found in manifest"),!n.lazy||typeof n.lazy!="object")return;let o=n.lazy[e];if(!o)return;let l=qr.get(n);l||(l={},qr.set(n,l));let s=l[e];if(s)return s;let i=(async()=>{let u=Ma(e),p=n[e]!==void 0&&e!=="hasErrorBoundary";if(u)ae(!u,"Route property "+e+" is not a supported lazy route property. This property will be ignored."),l[e]=Promise.resolve();else if(p)ae(!1,`Route "${n.id}" has a static property "${e}" defined. The lazy property will be ignored.`);else{let f=await o();f!=null&&(Object.assign(n,{[e]:f}),Object.assign(n,a(n)))}typeof n.lazy=="object"&&(n.lazy[e]=void 0,Object.values(n.lazy).every(f=>f===void 0)&&(n.lazy=void 0))})();return l[e]=i,i},Kr=new WeakMap;function so(e,t,r,a,n){let o=r[e.id];if(J(o,"No route found in manifest"),!e.lazy)return{lazyRoutePromise:void 0,lazyHandlerPromise:void 0};if(typeof e.lazy=="function"){let c=Kr.get(o);if(c)return{lazyRoutePromise:c,lazyHandlerPromise:c};let p=(async()=>{J(typeof e.lazy=="function","No lazy route function found");let f=await e.lazy(),g={};for(let w in f){let b=f[w];if(b===void 0)continue;let S=Da(w),O=o[w]!==void 0&&w!=="hasErrorBoundary";S?ae(!S,"Route property "+w+" is not a supported property to be returned from a lazy route function. This property will be ignored."):O?ae(!O,`Route "${o.id}" has a static property "${w}" defined but its lazy function is also returning a value for this property. The lazy route property "${w}" will be ignored.`):g[w]=b}Object.assign(o,g),Object.assign(o,{...a(o),lazy:void 0})})();return Kr.set(o,p),p.catch(()=>{}),{lazyRoutePromise:p,lazyHandlerPromise:p}}let l=Object.keys(e.lazy),s=[],i;for(let c of l){if(n&&n.includes(c))continue;let p=Cn({key:c,route:e,manifest:r,mapRouteProperties:a});p&&(s.push(p),c===t&&(i=p))}let u=s.length>0?Promise.all(s).then(()=>{}):void 0;return u==null||u.catch(()=>{}),i==null||i.catch(()=>{}),{lazyRoutePromise:u,lazyHandlerPromise:i}}async function Qr(e){let t=e.matches.filter(n=>n.shouldLoad),r={};return(await Promise.all(t.map(n=>n.resolve()))).forEach((n,o)=>{r[t[o].route.id]=n}),r}async function uo(e){return e.matches.some(t=>t.route.unstable_middleware)?Pn(e,!1,()=>Qr(e),(t,r)=>({[r]:{type:"error",result:t}})):Qr(e)}async function Pn(e,t,r,a){let{matches:n,request:o,params:l,context:s}=e,i={handlerResult:void 0};try{let u=n.flatMap(p=>p.route.unstable_middleware?p.route.unstable_middleware.map(f=>[p.route.id,f]):[]),c=await Tn({request:o,params:l,context:s},u,t,i,r);return t?c:i.handlerResult}catch(u){if(!i.middlewareError)throw u;let c=await a(i.middlewareError.error,i.middlewareError.routeId);return i.handlerResult?Object.assign(i.handlerResult,c):c}}async function Tn(e,t,r,a,n,o=0){let{request:l}=e;if(l.signal.aborted)throw l.signal.reason?l.signal.reason:new Error(`Request aborted without an \`AbortSignal.reason\`: ${l.method} ${l.url}`);let s=t[o];if(!s)return a.handlerResult=await n(),a.handlerResult;let[i,u]=s,c=!1,p,f=async()=>{if(c)throw new Error("You may only call `next()` once per middleware");c=!0,await Tn(e,t,r,a,n,o+1)};try{let g=await u({request:e.request,params:e.params,context:e.context},f);return c?g===void 0?p:g:f()}catch(g){throw a.middlewareError?a.middlewareError.error!==g&&(a.middlewareError={routeId:i,error:g}):a.middlewareError={routeId:i,error:g},g}}function Mn(e,t,r,a,n){let o=Cn({key:"unstable_middleware",route:a.route,manifest:t,mapRouteProperties:e}),l=so(a.route,ve(r.method)?"action":"loader",t,e,n);return{middleware:o,route:l.lazyRoutePromise,handler:l.lazyHandlerPromise}}function tr(e,t,r,a,n,o,l,s=null){let i=!1,u=Mn(e,t,r,a,n);return{...a,_lazyPromises:u,shouldLoad:l,unstable_shouldRevalidateArgs:s,unstable_shouldCallHandler(c){return i=!0,s?typeof c=="boolean"?Dt(a,{...s,defaultShouldRevalidate:c}):Dt(a,s):l},resolve(c){return i||l||c&&r.method==="GET"&&(a.route.lazy||a.route.loader)?fo({request:r,match:a,lazyHandlerPromise:u==null?void 0:u.handler,lazyRoutePromise:u==null?void 0:u.route,handlerOverride:c,scopedContext:o}):Promise.resolve({type:"data",result:void 0})}}}function Je(e,t,r,a,n,o,l,s=null){return a.map(i=>i.route.id!==n.route.id?{...i,shouldLoad:!1,unstable_shouldRevalidateArgs:s,unstable_shouldCallHandler:()=>!1,_lazyPromises:Mn(e,t,r,i,o),resolve:()=>Promise.resolve({type:"data",result:void 0})}:tr(e,t,r,i,o,l,!0,s))}async function co(e,t,r,a,n,o){r.some(u=>{var c;return(c=u._lazyPromises)==null?void 0:c.middleware})&&await Promise.all(r.map(u=>{var c;return(c=u._lazyPromises)==null?void 0:c.middleware}));let l={request:t,params:r[0].params,context:n,matches:r},i=await e({...l,fetcherKey:a,unstable_runClientMiddleware:u=>{let c=l;return Pn(c,!1,()=>u({...c,fetcherKey:a,unstable_runClientMiddleware:()=>{throw new Error("Cannot call `unstable_runClientMiddleware()` from within an `unstable_runClientMiddleware` handler")}}),(p,f)=>({[f]:{type:"error",result:p}}))}});try{await Promise.all(r.flatMap(u=>{var c,p;return[(c=u._lazyPromises)==null?void 0:c.handler,(p=u._lazyPromises)==null?void 0:p.route]}))}catch{}return i}async function fo({request:e,match:t,lazyHandlerPromise:r,lazyRoutePromise:a,handlerOverride:n,scopedContext:o}){let l,s,i=ve(e.method),u=i?"action":"loader",c=p=>{let f,g=new Promise((S,R)=>f=R);s=()=>f(),e.signal.addEventListener("abort",s);let w=S=>typeof p!="function"?Promise.reject(new Error(`You cannot call the handler for a route which defines a boolean "${u}" [routeId: ${t.route.id}]`)):p({request:e,params:t.params,context:o},...S!==void 0?[S]:[]),b=(async()=>{try{return{type:"data",result:await(n?n(R=>w(R)):w())}}catch(S){return{type:"error",result:S}}})();return Promise.race([b,g])};try{let p=i?t.route.action:t.route.loader;if(r||a)if(p){let f,[g]=await Promise.all([c(p).catch(w=>{f=w}),r,a]);if(f!==void 0)throw f;l=g}else{await r;let f=i?t.route.action:t.route.loader;if(f)[l]=await Promise.all([c(f),a]);else if(u==="action"){let g=new URL(e.url),w=g.pathname+g.search;throw Re(405,{method:e.method,pathname:w,routeId:t.route.id})}else return{type:"data",result:void 0}}else if(p)l=await c(p);else{let f=new URL(e.url),g=f.pathname+f.search;throw Re(404,{pathname:g})}}catch(p){return{type:"error",result:p}}finally{s&&e.signal.removeEventListener("abort",s)}return l}async function ho(e){var a,n,o,l,s,i;let{result:t,type:r}=e;if(fr(t)){let u;try{let c=t.headers.get("Content-Type");c&&/\bapplication\/json\b/.test(c)?t.body==null?u=null:u=await t.json():u=await t.text()}catch(c){return{type:"error",error:c}}return r==="error"?{type:"error",error:new Ae(t.status,t.statusText,u),statusCode:t.status,headers:t.headers}:{type:"data",data:u,statusCode:t.status,headers:t.headers}}return r==="error"?nr(t)?t.data instanceof Error?{type:"error",error:t.data,statusCode:(a=t.init)==null?void 0:a.status,headers:(n=t.init)!=null&&n.headers?new Headers(t.init.headers):void 0}:{type:"error",error:new Ae(((o=t.init)==null?void 0:o.status)||500,void 0,t.data),statusCode:ze(t)?t.status:void 0,headers:(l=t.init)!=null&&l.headers?new Headers(t.init.headers):void 0}:{type:"error",error:t,statusCode:ze(t)?t.status:void 0}:nr(t)?{type:"data",data:t.data,statusCode:(s=t.init)==null?void 0:s.status,headers:(i=t.init)!=null&&i.headers?new Headers(t.init.headers):void 0}:{type:"data",data:t}}function mo(e,t,r,a,n){let o=e.headers.get("Location");if(J(o,"Redirects returned/thrown from loaders/actions must have a Location header"),!dr.test(o)){let l=a.slice(0,a.findIndex(s=>s.route.id===r)+1);o=Zt(new URL(t.url),l,n,o),e.headers.set("Location",o)}return e}function Zr(e,t,r){if(dr.test(e)){let a=e,n=a.startsWith("//")?new URL(t.protocol+a):new URL(a),o=ge(n.pathname,r)!=null;if(n.origin===t.origin&&o)return n.pathname+n.search+n.hash}return e}function Ve(e,t,r,a){let n=e.createURL(_n(t)).toString(),o={signal:r};if(a&&ve(a.formMethod)){let{formMethod:l,formEncType:s}=a;o.method=l.toUpperCase(),s==="application/json"?(o.headers=new Headers({"Content-Type":s}),o.body=JSON.stringify(a.json)):s==="text/plain"?o.body=a.text:s==="application/x-www-form-urlencoded"&&a.formData?o.body=rr(a.formData):o.body=a.formData}return new Request(n,o)}function rr(e){let t=new URLSearchParams;for(let[r,a]of e.entries())t.append(r,typeof a=="string"?a:a.name);return t}function en(e){let t=new FormData;for(let[r,a]of e.entries())t.append(r,a);return t}function po(e,t,r,a=!1,n=!1){let o={},l=null,s,i=!1,u={},c=r&&ye(r[1])?r[1].error:void 0;return e.forEach(p=>{if(!(p.route.id in t))return;let f=p.route.id,g=t[f];if(J(!Ue(g),"Cannot handle redirect results in processLoaderData"),ye(g)){let w=g.error;if(c!==void 0&&(w=c,c=void 0),l=l||{},n)l[f]=w;else{let b=He(e,f);l[b.route.id]==null&&(l[b.route.id]=w)}a||(o[f]=xn),i||(i=!0,s=ze(g.error)?g.error.status:500),g.headers&&(u[f]=g.headers)}else o[f]=g.data,g.statusCode&&g.statusCode!==200&&!i&&(s=g.statusCode),g.headers&&(u[f]=g.headers)}),c!==void 0&&r&&(l={[r[0]]:c},r[2]&&(o[r[2]]=void 0)),{loaderData:o,errors:l,statusCode:s||200,loaderHeaders:u}}function tn(e,t,r,a,n,o){let{loaderData:l,errors:s}=po(t,r,a);return n.filter(i=>!i.matches||i.matches.some(u=>u.shouldLoad)).forEach(i=>{let{key:u,match:c,controller:p}=i,f=o[u];if(J(f,"Did not find corresponding fetcher result"),!(p&&p.signal.aborted))if(ye(f)){let g=He(e.matches,c==null?void 0:c.route.id);s&&s[g.route.id]||(s={...s,[g.route.id]:f.error}),e.fetchers.delete(u)}else if(Ue(f))J(!1,"Unhandled fetcher revalidation redirect");else{let g=Oe(f.data);e.fetchers.set(u,g)}}),{loaderData:l,errors:s}}function rn(e,t,r,a){let n=Object.entries(t).filter(([,o])=>o!==xn).reduce((o,[l,s])=>(o[l]=s,o),{});for(let o of r){let l=o.route.id;if(!t.hasOwnProperty(l)&&e.hasOwnProperty(l)&&o.route.loader&&(n[l]=e[l]),a&&a.hasOwnProperty(l))break}return n}function nn(e){return e?ye(e[1])?{actionData:{}}:{actionData:{[e[0]]:e[1].data}}:{}}function He(e,t){return(t?e.slice(0,e.findIndex(a=>a.route.id===t)+1):[...e]).reverse().find(a=>a.route.hasErrorBoundary===!0)||e[0]}function an(e){let t=e.length===1?e[0]:e.find(r=>r.index||!r.path||r.path==="/")||{id:"__shim-error-route__"};return{matches:[{params:{},pathname:"",pathnameBase:"",route:t}],route:t}}function Re(e,{pathname:t,routeId:r,method:a,type:n,message:o}={}){let l="Unknown Server Error",s="Unknown @remix-run/router error";return e===400?(l="Bad Request",a&&t&&r?s=`You made a ${a} request to "${t}" but did not provide a \`loader\` for route "${r}", so there is no way to handle the request.`:n==="invalid-body"&&(s="Unable to encode submission body")):e===403?(l="Forbidden",s=`Route "${r}" does not match URL "${t}"`):e===404?(l="Not Found",s=`No route matches URL "${t}"`):e===405&&(l="Method Not Allowed",a&&t&&r?s=`You made a ${a.toUpperCase()} request to "${t}" but did not provide an \`action\` for route "${r}", so there is no way to handle the request.`:a&&(s=`Invalid request method "${a.toUpperCase()}"`)),new Ae(e||500,l,new Error(s),!0)}function wt(e){let t=Object.entries(e);for(let r=t.length-1;r>=0;r--){let[a,n]=t[r];if(Ue(n))return{key:a,result:n}}}function _n(e){let t=typeof e=="string"?Ne(e):e;return ke({...t,hash:""})}function yo(e,t){return e.pathname!==t.pathname||e.search!==t.search?!1:e.hash===""?t.hash!=="":e.hash===t.hash?!0:t.hash!==""}function vo(e){return fr(e.result)&&to.has(e.result.status)}function ye(e){return e.type==="error"}function Ue(e){return(e&&e.type)==="redirect"}function nr(e){return typeof e=="object"&&e!=null&&"type"in e&&"data"in e&&"init"in e&&e.type==="DataWithResponseInit"}function fr(e){return e!=null&&typeof e.status=="number"&&typeof e.statusText=="string"&&typeof e.headers=="object"&&typeof e.body<"u"}function go(e){return eo.has(e.toUpperCase())}function ve(e){return Qa.has(e.toUpperCase())}function hr(e){return new URLSearchParams(e).getAll("index").some(t=>t==="")}function lt(e,t){let r=typeof t=="string"?Ne(t).search:t.search;if(e[e.length-1].route.index&&hr(r||""))return e[e.length-1];let a=Rn(e);return a[a.length-1]}function on(e){let{formMethod:t,formAction:r,formEncType:a,text:n,formData:o,json:l}=e;if(!(!t||!r||!a)){if(n!=null)return{formMethod:t,formAction:r,formEncType:a,formData:void 0,json:void 0,text:n};if(o!=null)return{formMethod:t,formAction:r,formEncType:a,formData:o,json:void 0,text:void 0};if(l!==void 0)return{formMethod:t,formAction:r,formEncType:a,formData:void 0,json:l,text:void 0}}}function Gt(e,t){return t?{state:"loading",location:e,formMethod:t.formMethod,formAction:t.formAction,formEncType:t.formEncType,formData:t.formData,json:t.json,text:t.text}:{state:"loading",location:e,formMethod:void 0,formAction:void 0,formEncType:void 0,formData:void 0,json:void 0,text:void 0}}function wo(e,t){return{state:"submitting",location:e,formMethod:t.formMethod,formAction:t.formAction,formEncType:t.formEncType,formData:t.formData,json:t.json,text:t.text}}function at(e,t){return e?{state:"loading",formMethod:e.formMethod,formAction:e.formAction,formEncType:e.formEncType,formData:e.formData,json:e.json,text:e.text,data:t}:{state:"loading",formMethod:void 0,formAction:void 0,formEncType:void 0,formData:void 0,json:void 0,text:void 0,data:t}}function Eo(e,t){return{state:"submitting",formMethod:e.formMethod,formAction:e.formAction,formEncType:e.formEncType,formData:e.formData,json:e.json,text:e.text,data:t?t.data:void 0}}function Oe(e){return{state:"idle",formMethod:void 0,formAction:void 0,formEncType:void 0,formData:void 0,json:void 0,text:void 0,data:e}}function Ro(e,t){try{let r=e.sessionStorage.getItem(Sn);if(r){let a=JSON.parse(r);for(let[n,o]of Object.entries(a||{}))o&&Array.isArray(o)&&t.set(n,new Set(o||[]))}}catch{}}function bo(e,t){if(t.size>0){let r={};for(let[a,n]of t)r[a]=[...n];try{e.sessionStorage.setItem(Sn,JSON.stringify(r))}catch(a){ae(!1,`Failed to save applied view transitions in sessionStorage (${a}).`)}}}function So(){let e,t,r=new Promise((a,n)=>{e=async o=>{a(o);try{await r}catch{}},t=async o=>{n(o);try{await r}catch{}}});return{promise:r,resolve:e,reject:t}}var Be=h.createContext(null);Be.displayName="DataRouter";var Ge=h.createContext(null);Ge.displayName="DataRouterState";var mr=h.createContext({isTransitioning:!1});mr.displayName="ViewTransition";var Dn=h.createContext(new Map);Dn.displayName="Fetchers";var xo=h.createContext(null);xo.displayName="Await";var be=h.createContext(null);be.displayName="Navigation";var kt=h.createContext(null);kt.displayName="Location";var Se=h.createContext({outlet:null,matches:[],isDataRoute:!1});Se.displayName="Route";var pr=h.createContext(null);pr.displayName="RouteError";function Lo(e,{relative:t}={}){J(ct(),"useHref() may be used only in the context of a component.");let{basename:r,navigator:a}=h.useContext(be),{hash:n,pathname:o,search:l}=dt(e,{relative:t}),s=o;return r!=="/"&&(s=o==="/"?r:Ce([r,o])),a.createHref({pathname:s,search:l,hash:n})}function ct(){return h.useContext(kt)!=null}function Pe(){return J(ct(),"useLocation() may be used only in the context of a component."),h.useContext(kt).location}var On="You should call navigate() in a React.useEffect(), not when your component is first rendered.";function kn(e){h.useContext(be).static||h.useLayoutEffect(e)}function Co(){let{isDataRoute:e}=h.useContext(Se);return e?Ho():Po()}function Po(){J(ct(),"useNavigate() may be used only in the context of a component.");let e=h.useContext(Be),{basename:t,navigator:r}=h.useContext(be),{matches:a}=h.useContext(Se),{pathname:n}=Pe(),o=JSON.stringify(ur(a)),l=h.useRef(!1);return kn(()=>{l.current=!0}),h.useCallback((i,u={})=>{if(ae(l.current,On),!l.current)return;if(typeof i=="number"){r.go(i);return}let c=cr(i,JSON.parse(o),n,u.relative==="path");e==null&&t!=="/"&&(c.pathname=c.pathname==="/"?t:Ce([t,c.pathname])),(u.replace?r.replace:r.push)(c,u.state,u)},[t,r,o,n,e])}var To=h.createContext(null);function Mo(e){let t=h.useContext(Se).outlet;return t&&h.createElement(To.Provider,{value:e},t)}function An(){let{matches:e}=h.useContext(Se),t=e[e.length-1];return t?t.params:{}}function dt(e,{relative:t}={}){let{matches:r}=h.useContext(Se),{pathname:a}=Pe(),n=JSON.stringify(ur(r));return h.useMemo(()=>cr(e,JSON.parse(n),a,t==="path"),[e,n,a,t])}function _o(e,t,r,a){J(ct(),"useRoutes() may be used only in the context of a component.");let{navigator:n}=h.useContext(be),{matches:o}=h.useContext(Se),l=o[o.length-1],s=l?l.params:{},i=l?l.pathname:"/",u=l?l.pathnameBase:"/",c=l&&l.route;{let R=c&&c.path||"";$n(i,!c||R.endsWith("*")||R.endsWith("*?"),`You rendered descendant (or called \`useRoutes()\`) at "${i}" (under ) but the parent route path has no trailing "*". This means if you navigate deeper, the parent won't match anymore and therefore the child routes will never render. + +Please change the parent to .`)}let p=Pe(),f;f=p;let g=f.pathname||"/",w=g;if(u!=="/"){let R=u.replace(/^\//,"").split("/");w="/"+g.replace(/^\//,"").split("/").slice(R.length).join("/")}let b=Le(e,{pathname:w});return ae(c||b!=null,`No routes matched location "${f.pathname}${f.search}${f.hash}" `),ae(b==null||b[b.length-1].route.element!==void 0||b[b.length-1].route.Component!==void 0||b[b.length-1].route.lazy!==void 0,`Matched leaf route at location "${f.pathname}${f.search}${f.hash}" does not have an element or Component. This means it will render an with a null value by default resulting in an "empty" page.`),No(b&&b.map(R=>Object.assign({},R,{params:Object.assign({},s,R.params),pathname:Ce([u,n.encodeLocation?n.encodeLocation(R.pathname).pathname:R.pathname]),pathnameBase:R.pathnameBase==="/"?u:Ce([u,n.encodeLocation?n.encodeLocation(R.pathnameBase).pathname:R.pathnameBase])})),o,r,a)}function Do(){let e=gr(),t=ze(e)?`${e.status} ${e.statusText}`:e instanceof Error?e.message:JSON.stringify(e),r=e instanceof Error?e.stack:null,a="rgba(200,200,200, 0.5)",n={padding:"0.5rem",backgroundColor:a},o={padding:"2px 4px",backgroundColor:a},l=null;return console.error("Error handled by React Router default ErrorBoundary:",e),l=h.createElement(h.Fragment,null,h.createElement("p",null,"💿 Hey developer 👋"),h.createElement("p",null,"You can provide a way better UX than this when your app throws errors by providing your own ",h.createElement("code",{style:o},"ErrorBoundary")," or"," ",h.createElement("code",{style:o},"errorElement")," prop on your route.")),h.createElement(h.Fragment,null,h.createElement("h2",null,"Unexpected Application Error!"),h.createElement("h3",{style:{fontStyle:"italic"}},t),r?h.createElement("pre",{style:n},r):null,l)}var Oo=h.createElement(Do,null),ko=class extends h.Component{constructor(e){super(e),this.state={location:e.location,revalidation:e.revalidation,error:e.error}}static getDerivedStateFromError(e){return{error:e}}static getDerivedStateFromProps(e,t){return t.location!==e.location||t.revalidation!=="idle"&&e.revalidation==="idle"?{error:e.error,location:e.location,revalidation:e.revalidation}:{error:e.error!==void 0?e.error:t.error,location:t.location,revalidation:e.revalidation||t.revalidation}}componentDidCatch(e,t){console.error("React Router caught the following error during render",e,t)}render(){return this.state.error!==void 0?h.createElement(Se.Provider,{value:this.props.routeContext},h.createElement(pr.Provider,{value:this.state.error,children:this.props.component})):this.props.children}};function Ao({routeContext:e,match:t,children:r}){let a=h.useContext(Be);return a&&a.static&&a.staticContext&&(t.route.errorElement||t.route.ErrorBoundary)&&(a.staticContext._deepestRenderedBoundaryId=t.route.id),h.createElement(Se.Provider,{value:e},r)}function No(e,t=[],r=null,a=null){if(e==null){if(!r)return null;if(r.errors)e=r.matches;else if(t.length===0&&!r.initialized&&r.matches.length>0)e=r.matches;else return null}let n=e,o=r==null?void 0:r.errors;if(o!=null){let i=n.findIndex(u=>u.route.id&&(o==null?void 0:o[u.route.id])!==void 0);J(i>=0,`Could not find a matching route for errors on route IDs: ${Object.keys(o).join(",")}`),n=n.slice(0,Math.min(n.length,i+1))}let l=!1,s=-1;if(r)for(let i=0;i=0?n=n.slice(0,s+1):n=[n[0]];break}}}return n.reduceRight((i,u,c)=>{let p,f=!1,g=null,w=null;r&&(p=o&&u.route.id?o[u.route.id]:void 0,g=u.route.errorElement||Oo,l&&(s<0&&c===0?($n("route-fallback",!1,"No `HydrateFallback` element provided to render during initial hydration"),f=!0,w=null):s===c&&(f=!0,w=u.route.hydrateFallbackElement||null)));let b=t.concat(n.slice(0,c+1)),S=()=>{let R;return p?R=g:f?R=w:u.route.Component?R=h.createElement(u.route.Component,null):u.route.element?R=u.route.element:R=i,h.createElement(Ao,{match:u,routeContext:{outlet:i,matches:b,isDataRoute:r!=null},children:R})};return r&&(u.route.ErrorBoundary||u.route.errorElement||c===0)?h.createElement(ko,{location:r.location,revalidation:r.revalidation,component:g,error:p,children:S(),routeContext:{outlet:null,matches:b,isDataRoute:!0}}):S()},null)}function yr(e){return`${e} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`}function Io(e){let t=h.useContext(Be);return J(t,yr(e)),t}function ft(e){let t=h.useContext(Ge);return J(t,yr(e)),t}function $o(e){let t=h.useContext(Se);return J(t,yr(e)),t}function ht(e){let t=$o(e),r=t.matches[t.matches.length-1];return J(r.route.id,`${e} can only be used on routes that contain a unique "id"`),r.route.id}function Fo(){return ht("useRouteId")}function jo(){return ft("useNavigation").navigation}function vr(){let{matches:e,loaderData:t}=ft("useMatches");return h.useMemo(()=>e.map(r=>vn(r,t)),[e,t])}function Nn(){let e=ft("useLoaderData"),t=ht("useLoaderData");return e.loaderData[t]}function In(){let e=ft("useActionData"),t=ht("useLoaderData");return e.actionData?e.actionData[t]:void 0}function gr(){var a;let e=h.useContext(pr),t=ft("useRouteError"),r=ht("useRouteError");return e!==void 0?e:(a=t.errors)==null?void 0:a[r]}function Ho(){let{router:e}=Io("useNavigate"),t=ht("useNavigate"),r=h.useRef(!1);return kn(()=>{r.current=!0}),h.useCallback(async(n,o={})=>{ae(r.current,On),r.current&&(typeof n=="number"?e.navigate(n):await e.navigate(n,{fromRouteId:t,...o}))},[e,t])}var ln={};function $n(e,t,r){!t&&!ln[e]&&(ln[e]=!0,ae(!1,r))}var sn={};function un(e,t){!e&&!sn[t]&&(sn[t]=!0,console.warn(t))}function pl(e){let t={hasErrorBoundary:e.hasErrorBoundary||e.ErrorBoundary!=null||e.errorElement!=null};return e.Component&&(e.element&&ae(!1,"You should not include both `Component` and `element` on your route - `Component` will be used."),Object.assign(t,{element:h.createElement(e.Component),Component:void 0})),e.HydrateFallback&&(e.hydrateFallbackElement&&ae(!1,"You should not include both `HydrateFallback` and `hydrateFallbackElement` on your route - `HydrateFallback` will be used."),Object.assign(t,{hydrateFallbackElement:h.createElement(e.HydrateFallback),HydrateFallback:void 0})),e.ErrorBoundary&&(e.errorElement&&ae(!1,"You should not include both `ErrorBoundary` and `errorElement` on your route - `ErrorBoundary` will be used."),Object.assign(t,{errorElement:h.createElement(e.ErrorBoundary),ErrorBoundary:void 0})),t}var yl=["HydrateFallback","hydrateFallbackElement"],Uo=class{constructor(){this.status="pending",this.promise=new Promise((e,t)=>{this.resolve=r=>{this.status==="pending"&&(this.status="resolved",e(r))},this.reject=r=>{this.status==="pending"&&(this.status="rejected",t(r))}})}};function vl({router:e,flushSync:t}){let[r,a]=h.useState(e.state),[n,o]=h.useState(),[l,s]=h.useState({isTransitioning:!1}),[i,u]=h.useState(),[c,p]=h.useState(),[f,g]=h.useState(),w=h.useRef(new Map),b=h.useCallback((T,{deletedFetchers:M,flushSync:x,viewTransitionOpts:y})=>{T.fetchers.forEach((U,j)=>{U.data!==void 0&&w.current.set(j,U.data)}),M.forEach(U=>w.current.delete(U)),un(x===!1||t!=null,'You provided the `flushSync` option to a router update, but you are not using the `` from `react-router/dom` so `ReactDOM.flushSync()` is unavailable. Please update your app to `import { RouterProvider } from "react-router/dom"` and ensure you have `react-dom` installed as a dependency to use the `flushSync` option.');let D=e.window!=null&&e.window.document!=null&&typeof e.window.document.startViewTransition=="function";if(un(y==null||D,"You provided the `viewTransition` option to a router update, but you do not appear to be running in a DOM environment as `window.startViewTransition` is not available."),!y||!D){t&&x?t(()=>a(T)):h.startTransition(()=>a(T));return}if(t&&x){t(()=>{c&&(i&&i.resolve(),c.skipTransition()),s({isTransitioning:!0,flushSync:!0,currentLocation:y.currentLocation,nextLocation:y.nextLocation})});let U=e.window.document.startViewTransition(()=>{t(()=>a(T))});U.finished.finally(()=>{t(()=>{u(void 0),p(void 0),o(void 0),s({isTransitioning:!1})})}),t(()=>p(U));return}c?(i&&i.resolve(),c.skipTransition(),g({state:T,currentLocation:y.currentLocation,nextLocation:y.nextLocation})):(o(T),s({isTransitioning:!0,flushSync:!1,currentLocation:y.currentLocation,nextLocation:y.nextLocation}))},[e.window,t,c,i]);h.useLayoutEffect(()=>e.subscribe(b),[e,b]),h.useEffect(()=>{l.isTransitioning&&!l.flushSync&&u(new Uo)},[l]),h.useEffect(()=>{if(i&&n&&e.window){let T=n,M=i.promise,x=e.window.document.startViewTransition(async()=>{h.startTransition(()=>a(T)),await M});x.finished.finally(()=>{u(void 0),p(void 0),o(void 0),s({isTransitioning:!1})}),p(x)}},[n,i,e.window]),h.useEffect(()=>{i&&n&&r.location.key===n.location.key&&i.resolve()},[i,c,r.location,n]),h.useEffect(()=>{!l.isTransitioning&&f&&(o(f.state),s({isTransitioning:!0,flushSync:!1,currentLocation:f.currentLocation,nextLocation:f.nextLocation}),g(void 0))},[l.isTransitioning,f]);let S=h.useMemo(()=>({createHref:e.createHref,encodeLocation:e.encodeLocation,go:T=>e.navigate(T),push:(T,M,x)=>e.navigate(T,{state:M,preventScrollReset:x==null?void 0:x.preventScrollReset}),replace:(T,M,x)=>e.navigate(T,{replace:!0,state:M,preventScrollReset:x==null?void 0:x.preventScrollReset})}),[e]),R=e.basename||"/",O=h.useMemo(()=>({router:e,navigator:S,static:!1,basename:R}),[e,S,R]);return h.createElement(h.Fragment,null,h.createElement(Be.Provider,{value:O},h.createElement(Ge.Provider,{value:r},h.createElement(Dn.Provider,{value:w.current},h.createElement(mr.Provider,{value:l},h.createElement(Wo,{basename:R,location:r.location,navigationType:r.historyAction,navigator:S},h.createElement(zo,{routes:e.routes,future:e.future,state:r})))))),null)}var zo=h.memo(Bo);function Bo({routes:e,future:t,state:r}){return _o(e,void 0,r,t)}function gl(e){return Mo(e.context)}function Wo({basename:e="/",children:t=null,location:r,navigationType:a="POP",navigator:n,static:o=!1}){J(!ct(),"You cannot render a inside another . You should never have more than one in your app.");let l=e.replace(/^\/*/,"/"),s=h.useMemo(()=>({basename:l,navigator:n,static:o,future:{}}),[l,n,o]);typeof r=="string"&&(r=Ne(r));let{pathname:i="/",search:u="",hash:c="",state:p=null,key:f="default"}=r,g=h.useMemo(()=>{let w=ge(i,l);return w==null?null:{location:{pathname:w,search:u,hash:c,state:p,key:f},navigationType:a}},[l,i,u,c,p,f,a]);return ae(g!=null,` is not able to match the URL "${i}${u}${c}" because it does not start with the basename, so the won't render anything.`),g==null?null:h.createElement(be.Provider,{value:s},h.createElement(kt.Provider,{children:t,value:g}))}function wl(e){return function(){const r={params:An(),loaderData:Nn(),actionData:In(),matches:vr()};return h.createElement(e,r)}}function El(e){return function(){const r={params:An(),loaderData:Nn(),actionData:In(),error:gr()};return h.createElement(e,r)}}var Lt="get",Ct="application/x-www-form-urlencoded";function At(e){return e!=null&&typeof e.tagName=="string"}function Yo(e){return At(e)&&e.tagName.toLowerCase()==="button"}function Vo(e){return At(e)&&e.tagName.toLowerCase()==="form"}function Jo(e){return At(e)&&e.tagName.toLowerCase()==="input"}function Go(e){return!!(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)}function Xo(e,t){return e.button===0&&(!t||t==="_self")&&!Go(e)}var Et=null;function qo(){if(Et===null)try{new FormData(document.createElement("form"),0),Et=!1}catch{Et=!0}return Et}var Ko=new Set(["application/x-www-form-urlencoded","multipart/form-data","text/plain"]);function Xt(e){return e!=null&&!Ko.has(e)?(ae(!1,`"${e}" is not a valid \`encType\` for \`
\`/\`\` and will default to "${Ct}"`),null):e}function Qo(e,t){let r,a,n,o,l;if(Vo(e)){let s=e.getAttribute("action");a=s?ge(s,t):null,r=e.getAttribute("method")||Lt,n=Xt(e.getAttribute("enctype"))||Ct,o=new FormData(e)}else if(Yo(e)||Jo(e)&&(e.type==="submit"||e.type==="image")){let s=e.form;if(s==null)throw new Error('Cannot submit a + + {result &&
{result}
} + + ); +} diff --git a/alchemy/templates/rwsdk/src/app/pages/user/functions.ts b/alchemy/templates/rwsdk/src/app/pages/user/functions.ts new file mode 100644 index 000000000..fa4ec3e73 --- /dev/null +++ b/alchemy/templates/rwsdk/src/app/pages/user/functions.ts @@ -0,0 +1,171 @@ +"use server"; +import { + generateRegistrationOptions, + generateAuthenticationOptions, + verifyRegistrationResponse, + verifyAuthenticationResponse, + RegistrationResponseJSON, + AuthenticationResponseJSON, +} from "@simplewebauthn/server"; + +import { sessions } from "@/session/store"; +import { requestInfo } from "rwsdk/worker"; +import { db } from "@/db"; +import { env } from "cloudflare:workers"; + +const IS_DEV = process.env.NODE_ENV === "development"; + +function getWebAuthnConfig(request: Request) { + const rpID = env.WEBAUTHN_RP_ID ?? new URL(request.url).hostname; + const rpName = IS_DEV ? "Development App" : env.WEBAUTHN_APP_NAME; + return { + rpName, + rpID, + }; +} + +export async function startPasskeyRegistration(username: string) { + const { rpName, rpID } = getWebAuthnConfig(requestInfo.request); + const { headers } = requestInfo; + + const options = await generateRegistrationOptions({ + rpName, + rpID, + userName: username, + authenticatorSelection: { + // Require the authenticator to store the credential, enabling a username-less login experience + residentKey: "required", + // Prefer user verification (biometric, PIN, etc.), but allow authentication even if it's not available + userVerification: "preferred", + }, + }); + + await sessions.save(headers, { challenge: options.challenge }); + + return options; +} + +export async function startPasskeyLogin() { + const { rpID } = getWebAuthnConfig(requestInfo.request); + const { headers } = requestInfo; + + const options = await generateAuthenticationOptions({ + rpID, + userVerification: "preferred", + allowCredentials: [], + }); + + await sessions.save(headers, { challenge: options.challenge }); + + return options; +} + +export async function finishPasskeyRegistration( + username: string, + registration: RegistrationResponseJSON, +) { + const { request, headers } = requestInfo; + const { origin } = new URL(request.url); + + const session = await sessions.load(request); + const challenge = session?.challenge; + + if (!challenge) { + return false; + } + + const verification = await verifyRegistrationResponse({ + response: registration, + expectedChallenge: challenge, + expectedOrigin: origin, + expectedRPID: env.WEBAUTHN_RP_ID || new URL(request.url).hostname, + }); + + if (!verification.verified || !verification.registrationInfo) { + return false; + } + + await sessions.save(headers, { challenge: null }); + + const user = await db.user.create({ + data: { + username, + }, + }); + + await db.credential.create({ + data: { + userId: user.id, + credentialId: verification.registrationInfo.credential.id, + publicKey: verification.registrationInfo.credential.publicKey, + counter: verification.registrationInfo.credential.counter, + }, + }); + + return true; +} + +export async function finishPasskeyLogin(login: AuthenticationResponseJSON) { + const { request, headers } = requestInfo; + const { origin } = new URL(request.url); + + const session = await sessions.load(request); + const challenge = session?.challenge; + + if (!challenge) { + return false; + } + + const credential = await db.credential.findUnique({ + where: { + credentialId: login.id, + }, + }); + + if (!credential) { + return false; + } + + const verification = await verifyAuthenticationResponse({ + response: login, + expectedChallenge: challenge, + expectedOrigin: origin, + expectedRPID: env.WEBAUTHN_RP_ID || new URL(request.url).hostname, + requireUserVerification: false, + credential: { + id: credential.credentialId, + publicKey: credential.publicKey, + counter: credential.counter, + }, + }); + + if (!verification.verified) { + return false; + } + + await db.credential.update({ + where: { + credentialId: login.id, + }, + data: { + counter: verification.authenticationInfo.newCounter, + }, + }); + + const user = await db.user.findUnique({ + where: { + id: credential.userId, + }, + }); + + if (!user) { + return false; + } + + await sessions.save(headers, { + userId: user.id, + challenge: null, + }); + + return true; +} diff --git a/alchemy/templates/rwsdk/src/app/pages/user/routes.ts b/alchemy/templates/rwsdk/src/app/pages/user/routes.ts new file mode 100644 index 000000000..6c5ed5803 --- /dev/null +++ b/alchemy/templates/rwsdk/src/app/pages/user/routes.ts @@ -0,0 +1,17 @@ +import { route } from "rwsdk/router"; +import { Login } from "./Login"; +import { sessions } from "@/session/store"; + +export const userRoutes = [ + route("/login", [Login]), + route("/logout", async function ({ request }) { + const headers = new Headers(); + await sessions.remove(request, headers); + headers.set("Location", "/"); + + return new Response(null, { + status: 302, + headers, + }); + }), +]; diff --git a/alchemy/templates/rwsdk/src/app/shared/links.ts b/alchemy/templates/rwsdk/src/app/shared/links.ts new file mode 100644 index 000000000..3488039cc --- /dev/null +++ b/alchemy/templates/rwsdk/src/app/shared/links.ts @@ -0,0 +1,3 @@ +import { defineLinks } from "rwsdk/router"; + +export const link = defineLinks(["/", "/user/login", "/user/logout"]); diff --git a/alchemy/templates/rwsdk/src/client.tsx b/alchemy/templates/rwsdk/src/client.tsx new file mode 100644 index 000000000..b903d900a --- /dev/null +++ b/alchemy/templates/rwsdk/src/client.tsx @@ -0,0 +1,3 @@ +import { initClient } from "rwsdk/client"; + +initClient(); diff --git a/alchemy/templates/rwsdk/src/db.ts b/alchemy/templates/rwsdk/src/db.ts new file mode 100644 index 000000000..1a3ee9aa0 --- /dev/null +++ b/alchemy/templates/rwsdk/src/db.ts @@ -0,0 +1,26 @@ +import { PrismaClient } from "@generated/prisma"; +import { PrismaD1 } from "@prisma/adapter-d1"; + +export type * from "@generated/prisma"; + +export let db: PrismaClient; + +// context(justinvdm, 21-05-2025): We need to instantiate the client via a +// function rather that at the module level for several reasons: +// * For prisma-client-js generator or cases where there are dynamic import to +// the prisma wasm modules, we need to make sure we are instantiating the +// prisma client later in the flow when the wasm would have been initialized +// * So that we can encapsulate workarounds, e.g. see `SELECT 1` workaround +// below +export const setupDb = async (env: Env) => { + db = new PrismaClient({ + // context(justinvdm, 21-05-2025): prisma-client generated type appears to + // consider D1 adapter incompatible, though in runtime (dev and production) + // it works + // @ts-ignore + adapter: new PrismaD1(env.DB), + }); + + // context(justinvdm, 21-05-2025): https://github.com/cloudflare/workers-sdk/pull/8283 + await db.$queryRaw`SELECT 1`; +}; diff --git a/alchemy/templates/rwsdk/src/scripts/seed.ts b/alchemy/templates/rwsdk/src/scripts/seed.ts new file mode 100644 index 000000000..262e330b2 --- /dev/null +++ b/alchemy/templates/rwsdk/src/scripts/seed.ts @@ -0,0 +1,21 @@ +import { defineScript } from "rwsdk/worker"; +import { db, setupDb } from "@/db"; +import { env } from "cloudflare:workers"; + +export default defineScript(async () => { + await setupDb(env); + + await db.$executeRawUnsafe(`\ + DELETE FROM User; + DELETE FROM sqlite_sequence; + `); + + await db.user.create({ + data: { + id: "1", + username: "testuser", + }, + }); + + console.log("🌱 Finished seeding"); +}); diff --git a/alchemy/templates/rwsdk/src/session/durableObject.ts b/alchemy/templates/rwsdk/src/session/durableObject.ts new file mode 100644 index 000000000..61332f0ce --- /dev/null +++ b/alchemy/templates/rwsdk/src/session/durableObject.ts @@ -0,0 +1,63 @@ +import { MAX_SESSION_DURATION } from "rwsdk/auth"; +import { DurableObject } from "cloudflare:workers"; + +export interface Session { + userId?: string | null; + challenge?: string | null; + createdAt: number; +} + +export class SessionDurableObject extends DurableObject { + private session: Session | undefined = undefined; + constructor(state: DurableObjectState, env: Env) { + super(state, env); + this.session = undefined; + } + + async saveSession({ + userId = null, + challenge = null, + }: { + userId?: string | null; + challenge?: string | null; + }): Promise { + const session: Session = { + userId, + challenge, + createdAt: Date.now(), + }; + + await this.ctx.storage.put("session", session); + this.session = session; + return session; + } + + async getSession(): Promise<{ value: Session } | { error: string }> { + if (this.session) { + return { value: this.session }; + } + + const session = await this.ctx.storage.get("session"); + + if (!session) { + return { + error: "Invalid session", + }; + } + + if (session.createdAt + MAX_SESSION_DURATION < Date.now()) { + await this.revokeSession(); + return { + error: "Session expired", + }; + } + + this.session = session; + return { value: session }; + } + + async revokeSession() { + await this.ctx.storage.delete("session"); + this.session = undefined; + } +} diff --git a/alchemy/templates/rwsdk/src/session/store.ts b/alchemy/templates/rwsdk/src/session/store.ts new file mode 100644 index 000000000..5609e2491 --- /dev/null +++ b/alchemy/templates/rwsdk/src/session/store.ts @@ -0,0 +1,13 @@ +import { defineDurableSession } from "rwsdk/auth"; + +export let sessions: ReturnType; + +const createSessionStore = (env: Env) => + defineDurableSession({ + sessionDurableObject: env.SESSION_DURABLE_OBJECT, + }); + +export const setupSessionStore = (env: Env) => { + sessions = createSessionStore(env); + return sessions; +}; diff --git a/alchemy/templates/rwsdk/src/worker.tsx b/alchemy/templates/rwsdk/src/worker.tsx new file mode 100644 index 000000000..870f3fcee --- /dev/null +++ b/alchemy/templates/rwsdk/src/worker.tsx @@ -0,0 +1,63 @@ +import { defineApp, ErrorResponse } from "rwsdk/worker"; +import { route, render, prefix } from "rwsdk/router"; +import { Document } from "@/app/Document"; +import { Home } from "@/app/pages/Home"; +import { setCommonHeaders } from "@/app/headers"; +import { userRoutes } from "@/app/pages/user/routes"; +import { sessions, setupSessionStore } from "./session/store"; +import { Session } from "./session/durableObject"; +import { type User, db, setupDb } from "@/db"; +import { env } from "cloudflare:workers"; +export { SessionDurableObject } from "./session/durableObject"; + +export type AppContext = { + session: Session | null; + user: User | null; +}; + +export default defineApp([ + setCommonHeaders(), + async ({ ctx, request, headers }) => { + await setupDb(env); + setupSessionStore(env); + + try { + ctx.session = await sessions.load(request); + } catch (error) { + if (error instanceof ErrorResponse && error.code === 401) { + await sessions.remove(request, headers); + headers.set("Location", "/user/login"); + + return new Response(null, { + status: 302, + headers, + }); + } + + throw error; + } + + if (ctx.session?.userId) { + ctx.user = await db.user.findUnique({ + where: { + id: ctx.session.userId, + }, + }); + } + }, + render(Document, [ + route("/", () => new Response("Hello, World!")), + route("/protected", [ + ({ ctx }) => { + if (!ctx.user) { + return new Response(null, { + status: 302, + headers: { Location: "/user/login" }, + }); + } + }, + Home, + ]), + prefix("/user", userRoutes), + ]), +]); diff --git a/alchemy/templates/rwsdk/tsconfig.json b/alchemy/templates/rwsdk/tsconfig.json new file mode 100644 index 000000000..7a337d680 --- /dev/null +++ b/alchemy/templates/rwsdk/tsconfig.json @@ -0,0 +1,47 @@ +{ + "compilerOptions": { + /* Visit https://aka.ms/tsconfig.json to read more about this file */ + + /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */ + "target": "es2021", + /* Specify a set of bundled library declaration files that describe the target runtime environment. */ + "lib": ["DOM", "DOM.Iterable", "ESNext", "ES2022"], + /* Specify what JSX code is generated. */ + "jsx": "react-jsx", + + /* Specify what module code is generated. */ + "module": "es2022", + /* Specify how TypeScript looks up a file from a given module specifier. */ + "moduleResolution": "bundler", + /* Specify type package names to be included without being referenced in a source file. */ + "types": [ + "@cloudflare/workers-types", + "types/**/*.ts" + ], + "paths": { + "@/*": ["./src/*"], + "@generated/*": ["./generated/*"] + }, + /* Enable importing .json files */ + "resolveJsonModule": true, + + /* Enable error reporting in type-checked JavaScript files. */ + "checkJs": false, + + /* Disable emitting files from a compilation. */ + "noEmit": true, + + /* Ensure that each file can be safely transpiled without relying on other imports. */ + "isolatedModules": true, + /* Allow 'import x from y' when a module doesn't have a default export. */ + "allowSyntheticDefaultImports": true, + /* Ensure that casing is correct in imports. */ + "forceConsistentCasingInFileNames": true, + + /* Enable all strict type-checking options. */ + "strict": true, + + /* Skip type checking all .d.ts files. */ + "skipLibCheck": true + } +} diff --git a/alchemy/templates/rwsdk/types/env.d.ts b/alchemy/templates/rwsdk/types/env.d.ts new file mode 100644 index 000000000..f371005c3 --- /dev/null +++ b/alchemy/templates/rwsdk/types/env.d.ts @@ -0,0 +1,16 @@ +// This file infers types for the cloudflare:workers environment from your Alchemy Worker. +// @see https://alchemy.run/docs/concepts/bindings.html#type-safe-bindings + +import type { worker } from "../alchemy.run.ts"; + +export type CloudflareEnv = typeof worker.Env; + +declare global { + type Env = CloudflareEnv; +} + +declare module "cloudflare:workers" { + namespace Cloudflare { + export interface Env extends CloudflareEnv {} + } +} diff --git a/alchemy/templates/rwsdk/types/rw.d.ts b/alchemy/templates/rwsdk/types/rw.d.ts new file mode 100644 index 000000000..8e02875d1 --- /dev/null +++ b/alchemy/templates/rwsdk/types/rw.d.ts @@ -0,0 +1,5 @@ +import { AppContext } from "../src/worker"; + +declare module "rwsdk/worker" { + interface DefaultAppContext extends AppContext {} +} diff --git a/alchemy/templates/rwsdk/types/vite.d.ts b/alchemy/templates/rwsdk/types/vite.d.ts new file mode 100644 index 000000000..b5fcf6f0e --- /dev/null +++ b/alchemy/templates/rwsdk/types/vite.d.ts @@ -0,0 +1,4 @@ +declare module "*?url" { + const result: string; + export default result; +} diff --git a/alchemy/templates/rwsdk/vite.config.mts b/alchemy/templates/rwsdk/vite.config.mts new file mode 100644 index 000000000..c1ad0bbcd --- /dev/null +++ b/alchemy/templates/rwsdk/vite.config.mts @@ -0,0 +1,6 @@ +import { defineConfig } from "vite"; +import { redwood } from "rwsdk/vite"; + +export default defineConfig({ + plugins: [redwood()], +}); diff --git a/alchemy/templates/rwsdk/wrangler.jsonc b/alchemy/templates/rwsdk/wrangler.jsonc new file mode 100644 index 000000000..53e941e29 --- /dev/null +++ b/alchemy/templates/rwsdk/wrangler.jsonc @@ -0,0 +1,28 @@ +{ + "name": "my-alchemy-app-website", + "main": "src/worker.tsx", + "compatibility_date": "2025-04-02", + "compatibility_flags": ["nodejs_compat"], + "assets": { "binding": "ASSETS", "directory": "dist/client" }, + "durable_objects": { + "bindings": [ + { "name": "SESSION_DURABLE_OBJECT", "class_name": "SessionDurableObject" } + ] + }, + "d1_databases": [ + { + "binding": "DB", + "database_id": "33c4fe7d-fbfe-4cef-959c-bb54292af712", + "database_name": "my-alchemy-app-db", + "migrations_dir": "migrations", + "preview_database_id": "33c4fe7d-fbfe-4cef-959c-bb54292af712" + } + ], + "migrations": [ + { + "tag": "v1", + "new_sqlite_classes": [], + "new_classes": ["SessionDurableObject"] + } + ] +} diff --git a/alchemy/templates/sveltekit/.gitignore b/alchemy/templates/sveltekit/.gitignore new file mode 100644 index 000000000..a98c00519 --- /dev/null +++ b/alchemy/templates/sveltekit/.gitignore @@ -0,0 +1,26 @@ +node_modules + +# Output +.output +.vercel +.netlify +.wrangler +/.svelte-kit +/build + +# OS +.DS_Store +Thumbs.db + +# Env +.env +.env.* +!.env.example +!.env.test + +# Vite +vite.config.js.timestamp-* +vite.config.ts.timestamp-* + +.alchemy/ +.env \ No newline at end of file diff --git a/alchemy/templates/sveltekit/README.md b/alchemy/templates/sveltekit/README.md new file mode 100644 index 000000000..b5b295070 --- /dev/null +++ b/alchemy/templates/sveltekit/README.md @@ -0,0 +1,38 @@ +# sv + +Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli). + +## Creating a project + +If you're seeing this, you've probably already done this step. Congrats! + +```bash +# create a new project in the current directory +npx sv create + +# create a new project in my-app +npx sv create my-app +``` + +## Developing + +Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server: + +```bash +npm run dev + +# or start the server and open the app in a new browser tab +npm run dev -- --open +``` + +## Building + +To create a production version of your app: + +```bash +npm run build +``` + +You can preview the production build with `npm run preview`. + +> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment. diff --git a/alchemy/templates/sveltekit/_env b/alchemy/templates/sveltekit/_env new file mode 100644 index 000000000..bb4cbbe94 --- /dev/null +++ b/alchemy/templates/sveltekit/_env @@ -0,0 +1 @@ +ALCHEMY_PASSWORD=change-me \ No newline at end of file diff --git a/alchemy/templates/sveltekit/_env.example b/alchemy/templates/sveltekit/_env.example new file mode 100644 index 000000000..bb4cbbe94 --- /dev/null +++ b/alchemy/templates/sveltekit/_env.example @@ -0,0 +1 @@ +ALCHEMY_PASSWORD=change-me \ No newline at end of file diff --git a/alchemy/templates/sveltekit/_gitignore b/alchemy/templates/sveltekit/_gitignore new file mode 100644 index 000000000..a98c00519 --- /dev/null +++ b/alchemy/templates/sveltekit/_gitignore @@ -0,0 +1,26 @@ +node_modules + +# Output +.output +.vercel +.netlify +.wrangler +/.svelte-kit +/build + +# OS +.DS_Store +Thumbs.db + +# Env +.env +.env.* +!.env.example +!.env.test + +# Vite +vite.config.js.timestamp-* +vite.config.ts.timestamp-* + +.alchemy/ +.env \ No newline at end of file diff --git a/alchemy/templates/sveltekit/_npmrc b/alchemy/templates/sveltekit/_npmrc new file mode 100644 index 000000000..b6f27f135 --- /dev/null +++ b/alchemy/templates/sveltekit/_npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/alchemy/templates/sveltekit/_prettierrc b/alchemy/templates/sveltekit/_prettierrc new file mode 100644 index 000000000..b4bfed357 --- /dev/null +++ b/alchemy/templates/sveltekit/_prettierrc @@ -0,0 +1,3 @@ +{ + "plugins": ["prettier-plugin-tailwindcss"] +} diff --git a/alchemy/templates/sveltekit/alchemy.run.ts b/alchemy/templates/sveltekit/alchemy.run.ts new file mode 100644 index 000000000..758380b04 --- /dev/null +++ b/alchemy/templates/sveltekit/alchemy.run.ts @@ -0,0 +1,16 @@ +/// + +import alchemy from "alchemy"; +import { SvelteKit } from "alchemy/cloudflare"; + +const app = await alchemy("my-alchemy-app"); + +export const worker = await SvelteKit("website", { + command: "bun run build", +}); + +console.log({ + url: worker.url, +}); + +await app.finalize(); diff --git a/alchemy/templates/sveltekit/bun.lock b/alchemy/templates/sveltekit/bun.lock new file mode 100644 index 000000000..38445f71c --- /dev/null +++ b/alchemy/templates/sveltekit/bun.lock @@ -0,0 +1,1099 @@ +{ + "lockfileVersion": 1, + "workspaces": { + "": { + "name": "my-alchemy-app", + "devDependencies": { + "@cloudflare/workers-types": "^4.20250620.0", + "@sveltejs/adapter-auto": "^6.0.0", + "@sveltejs/adapter-cloudflare": "^7.0.4", + "@sveltejs/kit": "^2.16.0", + "@sveltejs/vite-plugin-svelte": "^5.1.0", + "@tailwindcss/vite": "^4.0.0", + "alchemy": "^0.37.1", + "prettier-plugin-tailwindcss": "^0.6.11", + "svelte": "^5.0.0", + "svelte-check": "^4.0.0", + "tailwindcss": "^4.0.0", + "typescript": "^5.8.3", + "vite": "^6.2.6", + "vite-plugin-devtools-json": "^0.2.0", + }, + }, + }, + "packages": { + "@ai-sdk/openai": ["@ai-sdk/openai@1.3.22", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8" }, "peerDependencies": { "zod": "^3.0.0" } }, "sha512-QwA+2EkG0QyjVR+7h6FE7iOu2ivNqAVMm9UJZkVxxTk5OIq5fFJDTEI/zICEMuHImTTXR2JjsL6EirJ28Jc4cw=="], + + "@ai-sdk/openai-compatible": ["@ai-sdk/openai-compatible@0.2.14", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8" }, "peerDependencies": { "zod": "^3.0.0" } }, "sha512-icjObfMCHKSIbywijaoLdZ1nSnuRnWgMEMLgwoxPJgxsUHMx0aVORnsLUid4SPtdhHI3X2masrt6iaEQLvOSFw=="], + + "@ai-sdk/provider": ["@ai-sdk/provider@1.1.3", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg=="], + + "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@2.2.8", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "nanoid": "^3.3.8", "secure-json-parse": "^2.7.0" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA=="], + + "@ai-sdk/react": ["@ai-sdk/react@1.2.12", "", { "dependencies": { "@ai-sdk/provider-utils": "2.2.8", "@ai-sdk/ui-utils": "1.2.11", "swr": "^2.2.5", "throttleit": "2.1.0" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "zod": "^3.23.8" }, "optionalPeers": ["zod"] }, "sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g=="], + + "@ai-sdk/ui-utils": ["@ai-sdk/ui-utils@1.2.11", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w=="], + + "@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="], + + "@ark/schema": ["@ark/schema@0.46.0", "", { "dependencies": { "@ark/util": "0.46.0" } }, "sha512-c2UQdKgP2eqqDArfBqQIJppxJHvNNXuQPeuSPlDML4rjw+f1cu0qAlzOG4b8ujgm9ctIDWwhpyw6gjG5ledIVQ=="], + + "@ark/util": ["@ark/util@0.46.0", "", {}, "sha512-JPy/NGWn/lvf1WmGCPw2VGpBg5utZraE84I7wli18EDF3p3zc/e9WolT35tINeZO3l7C77SjqRJeAUoT0CvMRg=="], + + "@aws-crypto/crc32": ["@aws-crypto/crc32@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg=="], + + "@aws-crypto/crc32c": ["@aws-crypto/crc32c@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag=="], + + "@aws-crypto/sha1-browser": ["@aws-crypto/sha1-browser@5.2.0", "", { "dependencies": { "@aws-crypto/supports-web-crypto": "^5.2.0", "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-locate-window": "^3.0.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg=="], + + "@aws-crypto/sha256-browser": ["@aws-crypto/sha256-browser@5.2.0", "", { "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", "@aws-crypto/supports-web-crypto": "^5.2.0", "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-locate-window": "^3.0.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw=="], + + "@aws-crypto/sha256-js": ["@aws-crypto/sha256-js@5.2.0", "", { "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" } }, "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA=="], + + "@aws-crypto/supports-web-crypto": ["@aws-crypto/supports-web-crypto@5.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg=="], + + "@aws-crypto/util": ["@aws-crypto/util@5.2.0", "", { "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ=="], + + "@aws-sdk/client-cognito-identity": ["@aws-sdk/client-cognito-identity@3.830.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/credential-provider-node": "3.830.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", "@aws-sdk/middleware-user-agent": "3.828.0", "@aws-sdk/region-config-resolver": "3.821.0", "@aws-sdk/types": "3.821.0", "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", "@aws-sdk/util-user-agent-node": "3.828.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.5.3", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", "@smithy/middleware-endpoint": "^4.1.11", "@smithy/middleware-retry": "^4.1.12", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.19", "@smithy/util-defaults-mode-node": "^4.0.19", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.5", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-YhhQNVmHykPC6h6Xj60BMG7ELxxlynwNW2wK+8HJRiT62nYhbDyHypY9W2zNshqh/SE+5gLvwt1sXAu7KHGWmQ=="], + + "@aws-sdk/client-dynamodb": ["@aws-sdk/client-dynamodb@3.830.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/credential-provider-node": "3.830.0", "@aws-sdk/middleware-endpoint-discovery": "3.821.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", "@aws-sdk/middleware-user-agent": "3.828.0", "@aws-sdk/region-config-resolver": "3.821.0", "@aws-sdk/types": "3.821.0", "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", "@aws-sdk/util-user-agent-node": "3.828.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.5.3", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", "@smithy/middleware-endpoint": "^4.1.11", "@smithy/middleware-retry": "^4.1.12", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.19", "@smithy/util-defaults-mode-node": "^4.0.19", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.5", "@smithy/util-utf8": "^4.0.0", "@smithy/util-waiter": "^4.0.5", "@types/uuid": "^9.0.1", "tslib": "^2.6.2", "uuid": "^9.0.1" } }, "sha512-jen+bjGipjJMkPKnnYDXPrq/6HYaNr/l4Axcr9RlbROHLmS+kRWqlViCqRtLN1DVFOOfldbD/qVUtiwUCBWI5A=="], + + "@aws-sdk/client-iam": ["@aws-sdk/client-iam@3.830.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/credential-provider-node": "3.830.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", "@aws-sdk/middleware-user-agent": "3.828.0", "@aws-sdk/region-config-resolver": "3.821.0", "@aws-sdk/types": "3.821.0", "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", "@aws-sdk/util-user-agent-node": "3.828.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.5.3", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", "@smithy/middleware-endpoint": "^4.1.11", "@smithy/middleware-retry": "^4.1.12", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.19", "@smithy/util-defaults-mode-node": "^4.0.19", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.5", "@smithy/util-utf8": "^4.0.0", "@smithy/util-waiter": "^4.0.5", "tslib": "^2.6.2" } }, "sha512-ZSWZyqtDXd51B2970tB5XI6+DFtw9c09a0C5v7UP8DY7wusNwqeM28jsFU7HqRAZTM4Hbf7ozC6OOj8uvH/rsQ=="], + + "@aws-sdk/client-lambda": ["@aws-sdk/client-lambda@3.833.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/credential-provider-node": "3.830.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", "@aws-sdk/middleware-user-agent": "3.828.0", "@aws-sdk/region-config-resolver": "3.821.0", "@aws-sdk/types": "3.821.0", "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", "@aws-sdk/util-user-agent-node": "3.828.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.5.3", "@smithy/eventstream-serde-browser": "^4.0.4", "@smithy/eventstream-serde-config-resolver": "^4.1.2", "@smithy/eventstream-serde-node": "^4.0.4", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", "@smithy/middleware-endpoint": "^4.1.11", "@smithy/middleware-retry": "^4.1.12", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.19", "@smithy/util-defaults-mode-node": "^4.0.19", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.5", "@smithy/util-stream": "^4.2.2", "@smithy/util-utf8": "^4.0.0", "@smithy/util-waiter": "^4.0.5", "tslib": "^2.6.2" } }, "sha512-m56p1MB+/t+QFfZZzQoqlVcITn8PRYa7yzvuO2LbR9YsQ97uZyVCu970RAPSkW6/SjyWTUvXtQw3x/1KITcrvw=="], + + "@aws-sdk/client-s3": ["@aws-sdk/client-s3@3.832.0", "", { "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/credential-provider-node": "3.830.0", "@aws-sdk/middleware-bucket-endpoint": "3.830.0", "@aws-sdk/middleware-expect-continue": "3.821.0", "@aws-sdk/middleware-flexible-checksums": "3.826.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-location-constraint": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", "@aws-sdk/middleware-sdk-s3": "3.826.0", "@aws-sdk/middleware-ssec": "3.821.0", "@aws-sdk/middleware-user-agent": "3.828.0", "@aws-sdk/region-config-resolver": "3.821.0", "@aws-sdk/signature-v4-multi-region": "3.826.0", "@aws-sdk/types": "3.821.0", "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", "@aws-sdk/util-user-agent-node": "3.828.0", "@aws-sdk/xml-builder": "3.821.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.5.3", "@smithy/eventstream-serde-browser": "^4.0.4", "@smithy/eventstream-serde-config-resolver": "^4.1.2", "@smithy/eventstream-serde-node": "^4.0.4", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-blob-browser": "^4.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/hash-stream-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/md5-js": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", "@smithy/middleware-endpoint": "^4.1.11", "@smithy/middleware-retry": "^4.1.12", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.19", "@smithy/util-defaults-mode-node": "^4.0.19", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.5", "@smithy/util-stream": "^4.2.2", "@smithy/util-utf8": "^4.0.0", "@smithy/util-waiter": "^4.0.5", "@types/uuid": "^9.0.1", "tslib": "^2.6.2", "uuid": "^9.0.1" } }, "sha512-S+md1zCe71SEuaRDuLHq4mzhYYkVxR1ENa8NwrgInfYoC4xo8/pESoR6i0ZZpcLs0Jw4EyVInWYs4GgDHW70qQ=="], + + "@aws-sdk/client-sagemaker": ["@aws-sdk/client-sagemaker@3.833.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/credential-provider-node": "3.830.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", "@aws-sdk/middleware-user-agent": "3.828.0", "@aws-sdk/region-config-resolver": "3.821.0", "@aws-sdk/types": "3.821.0", "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", "@aws-sdk/util-user-agent-node": "3.828.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.5.3", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", "@smithy/middleware-endpoint": "^4.1.11", "@smithy/middleware-retry": "^4.1.12", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.19", "@smithy/util-defaults-mode-node": "^4.0.19", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.5", "@smithy/util-utf8": "^4.0.0", "@smithy/util-waiter": "^4.0.5", "@types/uuid": "^9.0.1", "tslib": "^2.6.2", "uuid": "^9.0.1" } }, "sha512-mHtUkTtHYd47YZt0L56mCjCyeOysrzeuipU520ID7TlhnyRro0FzY7EZ5VhNDyfjNVU+tyd8BxoRFmFRsR9Vfg=="], + + "@aws-sdk/client-ses": ["@aws-sdk/client-ses@3.830.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/credential-provider-node": "3.830.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", "@aws-sdk/middleware-user-agent": "3.828.0", "@aws-sdk/region-config-resolver": "3.821.0", "@aws-sdk/types": "3.821.0", "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", "@aws-sdk/util-user-agent-node": "3.828.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.5.3", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", "@smithy/middleware-endpoint": "^4.1.11", "@smithy/middleware-retry": "^4.1.12", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.19", "@smithy/util-defaults-mode-node": "^4.0.19", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.5", "@smithy/util-utf8": "^4.0.0", "@smithy/util-waiter": "^4.0.5", "tslib": "^2.6.2" } }, "sha512-Y2XaJkqHJ7qM4cpCw3YS96fMZgT44mP3HLP+9dU0ct29L+iwf3zhigJGQzakieMdJfuTFZe7Vi6s1RbcWv5v5w=="], + + "@aws-sdk/client-sesv2": ["@aws-sdk/client-sesv2@3.830.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/credential-provider-node": "3.830.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", "@aws-sdk/middleware-user-agent": "3.828.0", "@aws-sdk/region-config-resolver": "3.821.0", "@aws-sdk/signature-v4-multi-region": "3.826.0", "@aws-sdk/types": "3.821.0", "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", "@aws-sdk/util-user-agent-node": "3.828.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.5.3", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", "@smithy/middleware-endpoint": "^4.1.11", "@smithy/middleware-retry": "^4.1.12", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.19", "@smithy/util-defaults-mode-node": "^4.0.19", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.5", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-7Il9UByZfo4C2Mkp9UPKEzoVlzYaVY+pAQwmKiutWVYbxiv8/WyWhnJsnXav8t9L4Xfh5n7iSuqvD8+2Dv050Q=="], + + "@aws-sdk/client-sqs": ["@aws-sdk/client-sqs@3.831.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/credential-provider-node": "3.830.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", "@aws-sdk/middleware-sdk-sqs": "3.826.0", "@aws-sdk/middleware-user-agent": "3.828.0", "@aws-sdk/region-config-resolver": "3.821.0", "@aws-sdk/types": "3.821.0", "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", "@aws-sdk/util-user-agent-node": "3.828.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.5.3", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/md5-js": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", "@smithy/middleware-endpoint": "^4.1.11", "@smithy/middleware-retry": "^4.1.12", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.19", "@smithy/util-defaults-mode-node": "^4.0.19", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.5", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-7UioW8vkCar7PzR1MknLqMrLJ/15mdd4tPQvVlaHdDZxstwbTmm1yY6PGZUwIjr7Ju4ZP8XpnTmThKp0N+PJ6g=="], + + "@aws-sdk/client-ssm": ["@aws-sdk/client-ssm@3.830.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/credential-provider-node": "3.830.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", "@aws-sdk/middleware-user-agent": "3.828.0", "@aws-sdk/region-config-resolver": "3.821.0", "@aws-sdk/types": "3.821.0", "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", "@aws-sdk/util-user-agent-node": "3.828.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.5.3", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", "@smithy/middleware-endpoint": "^4.1.11", "@smithy/middleware-retry": "^4.1.12", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.19", "@smithy/util-defaults-mode-node": "^4.0.19", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.5", "@smithy/util-utf8": "^4.0.0", "@smithy/util-waiter": "^4.0.5", "@types/uuid": "^9.0.1", "tslib": "^2.6.2", "uuid": "^9.0.1" } }, "sha512-eeYYfVldV7Od/tMcyWumufspKGwlu53XjhgF8zMC7e7P2D541iQ6LwvHuwScw8xhlqfHsBzMS/yCnsDeM4kNmw=="], + + "@aws-sdk/client-sso": ["@aws-sdk/client-sso@3.830.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", "@aws-sdk/middleware-user-agent": "3.828.0", "@aws-sdk/region-config-resolver": "3.821.0", "@aws-sdk/types": "3.821.0", "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", "@aws-sdk/util-user-agent-node": "3.828.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.5.3", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", "@smithy/middleware-endpoint": "^4.1.11", "@smithy/middleware-retry": "^4.1.12", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.19", "@smithy/util-defaults-mode-node": "^4.0.19", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.5", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-5zCEpfI+zwX2SIa258L+TItNbBoAvQQ6w74qdFM6YJufQ1F9tvwjTX8T+eSTT9nsFIvfYnUaGalWwJVfmJUgVQ=="], + + "@aws-sdk/client-sts": ["@aws-sdk/client-sts@3.830.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/credential-provider-node": "3.830.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", "@aws-sdk/middleware-user-agent": "3.828.0", "@aws-sdk/region-config-resolver": "3.821.0", "@aws-sdk/types": "3.821.0", "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", "@aws-sdk/util-user-agent-node": "3.828.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.5.3", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", "@smithy/middleware-endpoint": "^4.1.11", "@smithy/middleware-retry": "^4.1.12", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.19", "@smithy/util-defaults-mode-node": "^4.0.19", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.5", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-tzVgR1tKK+QTHWzEHvMsGUbAf6n3kNfieTdvMSGhXhkK8TfOQ/k6vwMieISlX2ftMafI1RsPaUEv+9ae+VoGRw=="], + + "@aws-sdk/core": ["@aws-sdk/core@3.826.0", "", { "dependencies": { "@aws-sdk/types": "3.821.0", "@aws-sdk/xml-builder": "3.821.0", "@smithy/core": "^3.5.3", "@smithy/node-config-provider": "^4.1.3", "@smithy/property-provider": "^4.0.4", "@smithy/protocol-http": "^5.1.2", "@smithy/signature-v4": "^5.1.2", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-middleware": "^4.0.4", "@smithy/util-utf8": "^4.0.0", "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" } }, "sha512-BGbQYzWj3ps+dblq33FY5tz/SsgJCcXX0zjQlSC07tYvU1jHTUvsefphyig+fY38xZ4wdKjbTop+KUmXUYrOXw=="], + + "@aws-sdk/credential-provider-cognito-identity": ["@aws-sdk/credential-provider-cognito-identity@3.830.0", "", { "dependencies": { "@aws-sdk/client-cognito-identity": "3.830.0", "@aws-sdk/types": "3.821.0", "@smithy/property-provider": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-YEXmJ1BJ6DzjNnW5OR/5yNPm5d19uifKM6n/1Q1+vooj0OC/zxO9rXo5uQ8Kjs7ZAb0uYSxzy5pTNi5Ilvs8+Q=="], + + "@aws-sdk/credential-provider-env": ["@aws-sdk/credential-provider-env@3.826.0", "", { "dependencies": { "@aws-sdk/core": "3.826.0", "@aws-sdk/types": "3.821.0", "@smithy/property-provider": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-DK3pQY8+iKK3MGDdC3uOZQ2psU01obaKlTYhEwNu4VWzgwQL4Vi3sWj4xSWGEK41vqZxiRLq6fOq7ysRI+qEZA=="], + + "@aws-sdk/credential-provider-http": ["@aws-sdk/credential-provider-http@3.826.0", "", { "dependencies": { "@aws-sdk/core": "3.826.0", "@aws-sdk/types": "3.821.0", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/node-http-handler": "^4.0.6", "@smithy/property-provider": "^4.0.4", "@smithy/protocol-http": "^5.1.2", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/util-stream": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-N+IVZBh+yx/9GbMZTKO/gErBi/FYZQtcFRItoLbY+6WU+0cSWyZYfkoeOxHmQV3iX9k65oljERIWUmL9x6OSQg=="], + + "@aws-sdk/credential-provider-ini": ["@aws-sdk/credential-provider-ini@3.830.0", "", { "dependencies": { "@aws-sdk/core": "3.826.0", "@aws-sdk/credential-provider-env": "3.826.0", "@aws-sdk/credential-provider-http": "3.826.0", "@aws-sdk/credential-provider-process": "3.826.0", "@aws-sdk/credential-provider-sso": "3.830.0", "@aws-sdk/credential-provider-web-identity": "3.830.0", "@aws-sdk/nested-clients": "3.830.0", "@aws-sdk/types": "3.821.0", "@smithy/credential-provider-imds": "^4.0.6", "@smithy/property-provider": "^4.0.4", "@smithy/shared-ini-file-loader": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-zeQenzvh8JRY5nULd8izdjVGoCM1tgsVVsrLSwDkHxZTTW0hW/bmOmXfvdaE0wDdomXW7m2CkQDSmP7XdvNXZg=="], + + "@aws-sdk/credential-provider-node": ["@aws-sdk/credential-provider-node@3.830.0", "", { "dependencies": { "@aws-sdk/credential-provider-env": "3.826.0", "@aws-sdk/credential-provider-http": "3.826.0", "@aws-sdk/credential-provider-ini": "3.830.0", "@aws-sdk/credential-provider-process": "3.826.0", "@aws-sdk/credential-provider-sso": "3.830.0", "@aws-sdk/credential-provider-web-identity": "3.830.0", "@aws-sdk/types": "3.821.0", "@smithy/credential-provider-imds": "^4.0.6", "@smithy/property-provider": "^4.0.4", "@smithy/shared-ini-file-loader": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-X/2LrTgwtK1pkWrvofxQBI8VTi6QVLtSMpsKKPPnJQ0vgqC0e4czSIs3ZxiEsOkCBaQ2usXSiKyh0ccsQ6k2OA=="], + + "@aws-sdk/credential-provider-process": ["@aws-sdk/credential-provider-process@3.826.0", "", { "dependencies": { "@aws-sdk/core": "3.826.0", "@aws-sdk/types": "3.821.0", "@smithy/property-provider": "^4.0.4", "@smithy/shared-ini-file-loader": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-kURrc4amu3NLtw1yZw7EoLNEVhmOMRUTs+chaNcmS+ERm3yK0nKjaJzmKahmwlTQTSl3wJ8jjK7x962VPo+zWw=="], + + "@aws-sdk/credential-provider-sso": ["@aws-sdk/credential-provider-sso@3.830.0", "", { "dependencies": { "@aws-sdk/client-sso": "3.830.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/token-providers": "3.830.0", "@aws-sdk/types": "3.821.0", "@smithy/property-provider": "^4.0.4", "@smithy/shared-ini-file-loader": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-+VdRpZmfekzpySqZikAKx6l5ndnLGluioIgUG4ZznrButgFD/iogzFtGmBDFB3ZLViX1l4pMXru0zFwJEZT21Q=="], + + "@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.830.0", "", { "dependencies": { "@aws-sdk/core": "3.826.0", "@aws-sdk/nested-clients": "3.830.0", "@aws-sdk/types": "3.821.0", "@smithy/property-provider": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-hPYrKsZeeOdLROJ59T6Y8yZ0iwC/60L3qhZXjapBFjbqBtMaQiMTI645K6xVXBioA6vxXq7B4aLOhYqk6Fy/Ww=="], + + "@aws-sdk/credential-providers": ["@aws-sdk/credential-providers@3.834.0", "", { "dependencies": { "@aws-sdk/client-cognito-identity": "3.830.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/credential-provider-cognito-identity": "3.830.0", "@aws-sdk/credential-provider-env": "3.826.0", "@aws-sdk/credential-provider-http": "3.826.0", "@aws-sdk/credential-provider-ini": "3.830.0", "@aws-sdk/credential-provider-node": "3.830.0", "@aws-sdk/credential-provider-process": "3.826.0", "@aws-sdk/credential-provider-sso": "3.830.0", "@aws-sdk/credential-provider-web-identity": "3.830.0", "@aws-sdk/nested-clients": "3.830.0", "@aws-sdk/types": "3.821.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.5.3", "@smithy/credential-provider-imds": "^4.0.6", "@smithy/node-config-provider": "^4.1.3", "@smithy/property-provider": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-ORIWCrLuqJnJg0fuI0rPhwaeuzqnIIJsbSkg1WV2XuiOpWXwLC/CfzhAbelQAv07/sRywZMnKqws0OOWg/ieYg=="], + + "@aws-sdk/endpoint-cache": ["@aws-sdk/endpoint-cache@3.804.0", "", { "dependencies": { "mnemonist": "0.38.3", "tslib": "^2.6.2" } }, "sha512-TQVDkA/lV6ua75ELZaichMzlp6x7tDa1bqdy/+0ZftmODPtKXuOOEcJxmdN7Ui/YRo1gkRz2D9txYy7IlNg1Og=="], + + "@aws-sdk/middleware-bucket-endpoint": ["@aws-sdk/middleware-bucket-endpoint@3.830.0", "", { "dependencies": { "@aws-sdk/types": "3.821.0", "@aws-sdk/util-arn-parser": "3.804.0", "@smithy/node-config-provider": "^4.1.3", "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", "@smithy/util-config-provider": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-ElVeCReZSH5Ds+/pkL5ebneJjuo8f49e9JXV1cYizuH0OAOQfYaBU9+M+7+rn61pTttOFE8W//qKzrXBBJhfMg=="], + + "@aws-sdk/middleware-endpoint-discovery": ["@aws-sdk/middleware-endpoint-discovery@3.821.0", "", { "dependencies": { "@aws-sdk/endpoint-cache": "3.804.0", "@aws-sdk/types": "3.821.0", "@smithy/node-config-provider": "^4.1.3", "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-8EguERzvpzTN2WrPaspK/F9GSkAzBQbecgIaCL49rJWKAso+ewmVVPnrXGzbeGVXTk4G0XuWSjt8wqUzZyt7wQ=="], + + "@aws-sdk/middleware-expect-continue": ["@aws-sdk/middleware-expect-continue@3.821.0", "", { "dependencies": { "@aws-sdk/types": "3.821.0", "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-zAOoSZKe1njOrtynvK6ZORU57YGv5I7KP4+rwOvUN3ZhJbQ7QPf8gKtFUCYAPRMegaXCKF/ADPtDZBAmM+zZ9g=="], + + "@aws-sdk/middleware-flexible-checksums": ["@aws-sdk/middleware-flexible-checksums@3.826.0", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@aws-crypto/crc32c": "5.2.0", "@aws-crypto/util": "5.2.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/types": "3.821.0", "@smithy/is-array-buffer": "^4.0.0", "@smithy/node-config-provider": "^4.1.3", "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", "@smithy/util-middleware": "^4.0.4", "@smithy/util-stream": "^4.2.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-Fz9w8CFYPfSlHEB6feSsi06hdS+s+FB8k5pO4L7IV0tUa78mlhxF/VNlAJaVWYyOkZXl4HPH2K48aapACSQOXw=="], + + "@aws-sdk/middleware-host-header": ["@aws-sdk/middleware-host-header@3.821.0", "", { "dependencies": { "@aws-sdk/types": "3.821.0", "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-xSMR+sopSeWGx5/4pAGhhfMvGBHioVBbqGvDs6pG64xfNwM5vq5s5v6D04e2i+uSTj4qGa71dLUs5I0UzAK3sw=="], + + "@aws-sdk/middleware-location-constraint": ["@aws-sdk/middleware-location-constraint@3.821.0", "", { "dependencies": { "@aws-sdk/types": "3.821.0", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-sKrm80k0t3R0on8aA/WhWFoMaAl4yvdk+riotmMElLUpcMcRXAd1+600uFVrxJqZdbrKQ0mjX0PjT68DlkYXLg=="], + + "@aws-sdk/middleware-logger": ["@aws-sdk/middleware-logger@3.821.0", "", { "dependencies": { "@aws-sdk/types": "3.821.0", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-0cvI0ipf2tGx7fXYEEN5fBeZDz2RnHyb9xftSgUsEq7NBxjV0yTZfLJw6Za5rjE6snC80dRN8+bTNR1tuG89zA=="], + + "@aws-sdk/middleware-recursion-detection": ["@aws-sdk/middleware-recursion-detection@3.821.0", "", { "dependencies": { "@aws-sdk/types": "3.821.0", "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-efmaifbhBoqKG3bAoEfDdcM8hn1psF+4qa7ykWuYmfmah59JBeqHLfz5W9m9JoTwoKPkFcVLWZxnyZzAnVBOIg=="], + + "@aws-sdk/middleware-sdk-s3": ["@aws-sdk/middleware-sdk-s3@3.826.0", "", { "dependencies": { "@aws-sdk/core": "3.826.0", "@aws-sdk/types": "3.821.0", "@aws-sdk/util-arn-parser": "3.804.0", "@smithy/core": "^3.5.3", "@smithy/node-config-provider": "^4.1.3", "@smithy/protocol-http": "^5.1.2", "@smithy/signature-v4": "^5.1.2", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/util-config-provider": "^4.0.0", "@smithy/util-middleware": "^4.0.4", "@smithy/util-stream": "^4.2.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-8F0qWaYKfvD/de1AKccXuigM+gb/IZSncCqxdnFWqd+TFzo9qI9Hh+TpUhWOMYSgxsMsYQ8ipmLzlD/lDhjrmA=="], + + "@aws-sdk/middleware-sdk-sqs": ["@aws-sdk/middleware-sdk-sqs@3.826.0", "", { "dependencies": { "@aws-sdk/types": "3.821.0", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/util-hex-encoding": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-NDnA7Fb6AT+AO1tvm9i5WFS+a+nIMtjYY6gYXPUu0GhT2FFkZfRvagOKoXVH+Vddo5iQRbNx4/1Zb1Z7vB42cA=="], + + "@aws-sdk/middleware-ssec": ["@aws-sdk/middleware-ssec@3.821.0", "", { "dependencies": { "@aws-sdk/types": "3.821.0", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-YYi1Hhr2AYiU/24cQc8HIB+SWbQo6FBkMYojVuz/zgrtkFmALxENGF/21OPg7f/QWd+eadZJRxCjmRwh5F2Cxg=="], + + "@aws-sdk/middleware-user-agent": ["@aws-sdk/middleware-user-agent@3.828.0", "", { "dependencies": { "@aws-sdk/core": "3.826.0", "@aws-sdk/types": "3.821.0", "@aws-sdk/util-endpoints": "3.828.0", "@smithy/core": "^3.5.3", "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-nixvI/SETXRdmrVab4D9LvXT3lrXkwAWGWk2GVvQvzlqN1/M/RfClj+o37Sn4FqRkGH9o9g7Fqb1YqZ4mqDAtA=="], + + "@aws-sdk/nested-clients": ["@aws-sdk/nested-clients@3.830.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", "@aws-sdk/middleware-user-agent": "3.828.0", "@aws-sdk/region-config-resolver": "3.821.0", "@aws-sdk/types": "3.821.0", "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", "@aws-sdk/util-user-agent-node": "3.828.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.5.3", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", "@smithy/middleware-endpoint": "^4.1.11", "@smithy/middleware-retry": "^4.1.12", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.19", "@smithy/util-defaults-mode-node": "^4.0.19", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.5", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-5N5YTlBr1vtxf7+t+UaIQ625KEAmm7fY9o1e3MgGOi/paBoI0+axr3ud24qLIy0NSzFlAHEaxUSWxcERNjIoZw=="], + + "@aws-sdk/region-config-resolver": ["@aws-sdk/region-config-resolver@3.821.0", "", { "dependencies": { "@aws-sdk/types": "3.821.0", "@smithy/node-config-provider": "^4.1.3", "@smithy/types": "^4.3.1", "@smithy/util-config-provider": "^4.0.0", "@smithy/util-middleware": "^4.0.4", "tslib": "^2.6.2" } }, "sha512-t8og+lRCIIy5nlId0bScNpCkif8sc0LhmtaKsbm0ZPm3sCa/WhCbSZibjbZ28FNjVCV+p0D9RYZx0VDDbtWyjw=="], + + "@aws-sdk/signature-v4-multi-region": ["@aws-sdk/signature-v4-multi-region@3.826.0", "", { "dependencies": { "@aws-sdk/middleware-sdk-s3": "3.826.0", "@aws-sdk/types": "3.821.0", "@smithy/protocol-http": "^5.1.2", "@smithy/signature-v4": "^5.1.2", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-3fEi/zy6tpMzomYosksGtu7jZqGFcdBXoL7YRsG7OEeQzBbOW9B+fVaQZ4jnsViSjzA/yKydLahMrfPnt+iaxg=="], + + "@aws-sdk/token-providers": ["@aws-sdk/token-providers@3.830.0", "", { "dependencies": { "@aws-sdk/core": "3.826.0", "@aws-sdk/nested-clients": "3.830.0", "@aws-sdk/types": "3.821.0", "@smithy/property-provider": "^4.0.4", "@smithy/shared-ini-file-loader": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-aJ4guFwj92nV9D+EgJPaCFKK0I3y2uMchiDfh69Zqnmwfxxxfxat6F79VA7PS0BdbjRfhLbn+Ghjftnomu2c1g=="], + + "@aws-sdk/types": ["@aws-sdk/types@3.821.0", "", { "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-Znroqdai1a90TlxGaJ+FK1lwC0fHpo97Xjsp5UKGR5JODYm7f9+/fF17ebO1KdoBr/Rm0UIFiF5VmI8ts9F1eA=="], + + "@aws-sdk/util-arn-parser": ["@aws-sdk/util-arn-parser@3.804.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-wmBJqn1DRXnZu3b4EkE6CWnoWMo1ZMvlfkqU5zPz67xx1GMaXlDCchFvKAXMjk4jn/L1O3tKnoFDNsoLV1kgNQ=="], + + "@aws-sdk/util-endpoints": ["@aws-sdk/util-endpoints@3.828.0", "", { "dependencies": { "@aws-sdk/types": "3.821.0", "@smithy/types": "^4.3.1", "@smithy/util-endpoints": "^3.0.6", "tslib": "^2.6.2" } }, "sha512-RvKch111SblqdkPzg3oCIdlGxlQs+k+P7Etory9FmxPHyPDvsP1j1c74PmgYqtzzMWmoXTjd+c9naUHh9xG8xg=="], + + "@aws-sdk/util-locate-window": ["@aws-sdk/util-locate-window@3.804.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-zVoRfpmBVPodYlnMjgVjfGoEZagyRF5IPn3Uo6ZvOZp24chnW/FRstH7ESDHDDRga4z3V+ElUQHKpFDXWyBW5A=="], + + "@aws-sdk/util-user-agent-browser": ["@aws-sdk/util-user-agent-browser@3.821.0", "", { "dependencies": { "@aws-sdk/types": "3.821.0", "@smithy/types": "^4.3.1", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-irWZHyM0Jr1xhC+38OuZ7JB6OXMLPZlj48thElpsO1ZSLRkLZx5+I7VV6k3sp2yZ7BYbKz/G2ojSv4wdm7XTLw=="], + + "@aws-sdk/util-user-agent-node": ["@aws-sdk/util-user-agent-node@3.828.0", "", { "dependencies": { "@aws-sdk/middleware-user-agent": "3.828.0", "@aws-sdk/types": "3.821.0", "@smithy/node-config-provider": "^4.1.3", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "optionalPeers": ["aws-crt"] }, "sha512-LdN6fTBzTlQmc8O8f1wiZN0qF3yBWVGis7NwpWK7FUEzP9bEZRxYfIkV9oV9zpt6iNRze1SedK3JQVB/udxBoA=="], + + "@aws-sdk/xml-builder": ["@aws-sdk/xml-builder@3.821.0", "", { "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-DIIotRnefVL6DiaHtO6/21DhJ4JZnnIwdNbpwiAhdt/AVbttcE4yw925gsjur0OGv5BTYXQXU3YnANBYnZjuQA=="], + + "@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.4.0", "", { "dependencies": { "mime": "^3.0.0" } }, "sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA=="], + + "@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.3.3", "", { "peerDependencies": { "unenv": "2.0.0-rc.17", "workerd": "^1.20250508.0" }, "optionalPeers": ["workerd"] }, "sha512-/M3MEcj3V2WHIRSW1eAQBPRJ6JnGQHc6JKMAPLkDb7pLs3m6X9ES/+K3ceGqxI6TKeF32AWAi7ls0AYzVxCP0A=="], + + "@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20250617.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-toG8JUKVLIks4oOJLe9FeuixE84pDpMZ32ip7mCpE7JaFc5BqGFvevk0YC/db3T71AQlialjRwioH3jS/dzItA=="], + + "@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20250617.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-JTX0exbC9/ZtMmQQA8tDZEZFMXZrxOpTUj2hHnsUkErWYkr5SSZH04RBhPg6dU4VL8bXuB5/eJAh7+P9cZAp7g=="], + + "@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20250617.0", "", { "os": "linux", "cpu": "x64" }, "sha512-8jkSoVRJ+1bOx3tuWlZCGaGCV2ew7/jFMl6V3CPXOoEtERUHsZBQLVkQIGKcmC/LKSj7f/mpyBUeu2EPTo2HEg=="], + + "@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20250617.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-YAzcOyu897z5dQKFzme1oujGWMGEJCR7/Wrrm1nSP6dqutxFPTubRADM8BHn2CV3ij//vaPnAeLmZE3jVwOwig=="], + + "@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20250617.0", "", { "os": "win32", "cpu": "x64" }, "sha512-XWM/6sagDrO0CYDKhXhPjM23qusvIN1ju9ZEml6gOQs8tNOFnq6Cn6X9FAmnyapRFCGUSEC3HZYJAm7zwVKaMA=="], + + "@cloudflare/workers-shared": ["@cloudflare/workers-shared@0.17.5", "", { "dependencies": { "mime": "^3.0.0", "zod": "^3.22.3" } }, "sha512-e2tjozEy3/8JnPcddYFuMjW9As+aX0i7egciPE8b+mufS33QCtdFEzZKCK8utFzby0tx9TkxGFLJ+cmSrJ+tLw=="], + + "@cloudflare/workers-types": ["@cloudflare/workers-types@4.20250620.0", "", {}, "sha512-EVvRB/DJEm6jhdKg+A4Qm4y/ry1cIvylSgSO3/f/Bv161vldDRxaXM2YoQQWFhLOJOw0qtrHsKOD51KYxV1XCw=="], + + "@cspotcode/source-map-support": ["@cspotcode/source-map-support@0.8.1", "", { "dependencies": { "@jridgewell/trace-mapping": "0.3.9" } }, "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw=="], + + "@emnapi/runtime": ["@emnapi/runtime@1.4.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ=="], + + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.25.5", "", { "os": "android", "cpu": "arm" }, "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.5", "", { "os": "android", "cpu": "arm64" }, "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.25.5", "", { "os": "android", "cpu": "x64" }, "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.5", "", { "os": "linux", "cpu": "arm" }, "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.5", "", { "os": "linux", "cpu": "ia32" }, "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.5", "", { "os": "linux", "cpu": "none" }, "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.5", "", { "os": "linux", "cpu": "none" }, "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.5", "", { "os": "linux", "cpu": "none" }, "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.5", "", { "os": "linux", "cpu": "x64" }, "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw=="], + + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.5", "", { "os": "none", "cpu": "arm64" }, "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.5", "", { "os": "none", "cpu": "x64" }, "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ=="], + + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.5", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.5", "", { "os": "win32", "cpu": "x64" }, "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g=="], + + "@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="], + + "@iarna/toml": ["@iarna/toml@2.2.5", "", {}, "sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg=="], + + "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], + + "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], + + "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="], + + "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="], + + "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="], + + "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="], + + "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA=="], + + "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="], + + "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="], + + "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="], + + "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="], + + "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="], + + "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="], + + "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="], + + "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="], + + "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="], + + "@img/sharp-wasm32": ["@img/sharp-wasm32@0.33.5", "", { "dependencies": { "@emnapi/runtime": "^1.2.0" }, "cpu": "none" }, "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg=="], + + "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="], + + "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], + + "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="], + + "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="], + + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/set-array": ["@jridgewell/set-array@1.2.1", "", {}, "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="], + + "@mixmark-io/domino": ["@mixmark-io/domino@2.2.0", "", {}, "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw=="], + + "@octokit/auth-token": ["@octokit/auth-token@5.1.2", "", {}, "sha512-JcQDsBdg49Yky2w2ld20IHAlwr8d/d8N6NiOXbtuoPCqzbsiJgF633mVUw3x4mo0H5ypataQIX7SFu3yy44Mpw=="], + + "@octokit/core": ["@octokit/core@6.1.5", "", { "dependencies": { "@octokit/auth-token": "^5.0.0", "@octokit/graphql": "^8.2.2", "@octokit/request": "^9.2.3", "@octokit/request-error": "^6.1.8", "@octokit/types": "^14.0.0", "before-after-hook": "^3.0.2", "universal-user-agent": "^7.0.0" } }, "sha512-vvmsN0r7rguA+FySiCsbaTTobSftpIDIpPW81trAmsv9TGxg3YCujAxRYp/Uy8xmDgYCzzgulG62H7KYUFmeIg=="], + + "@octokit/endpoint": ["@octokit/endpoint@10.1.4", "", { "dependencies": { "@octokit/types": "^14.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-OlYOlZIsfEVZm5HCSR8aSg02T2lbUWOsCQoPKfTXJwDzcHQBrVBGdGXb89dv2Kw2ToZaRtudp8O3ZIYoaOjKlA=="], + + "@octokit/graphql": ["@octokit/graphql@8.2.2", "", { "dependencies": { "@octokit/request": "^9.2.3", "@octokit/types": "^14.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-Yi8hcoqsrXGdt0yObxbebHXFOiUA+2v3n53epuOg1QUgOB6c4XzvisBNVXJSl8RYA5KrDuSL2yq9Qmqe5N0ryA=="], + + "@octokit/openapi-types": ["@octokit/openapi-types@25.1.0", "", {}, "sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA=="], + + "@octokit/plugin-paginate-rest": ["@octokit/plugin-paginate-rest@11.6.0", "", { "dependencies": { "@octokit/types": "^13.10.0" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-n5KPteiF7pWKgBIBJSk8qzoZWcUkza2O6A0za97pMGVrGfPdltxrfmfF5GucHYvHGZD8BdaZmmHGz5cX/3gdpw=="], + + "@octokit/plugin-request-log": ["@octokit/plugin-request-log@5.3.1", "", { "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-n/lNeCtq+9ofhC15xzmJCNKP2BWTv8Ih2TTy+jatNCCq/gQP/V7rK3fjIfuz0pDWDALO/o/4QY4hyOF6TQQFUw=="], + + "@octokit/plugin-rest-endpoint-methods": ["@octokit/plugin-rest-endpoint-methods@13.5.0", "", { "dependencies": { "@octokit/types": "^13.10.0" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-9Pas60Iv9ejO3WlAX3maE1+38c5nqbJXV5GrncEfkndIpZrJ/WPMRd2xYDcPPEt5yzpxcjw9fWNoPhsSGzqKqw=="], + + "@octokit/request": ["@octokit/request@9.2.4", "", { "dependencies": { "@octokit/endpoint": "^10.1.4", "@octokit/request-error": "^6.1.8", "@octokit/types": "^14.0.0", "fast-content-type-parse": "^2.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-q8ybdytBmxa6KogWlNa818r0k1wlqzNC+yNkcQDECHvQo8Vmstrg18JwqJHdJdUiHD2sjlwBgSm9kHkOKe2iyA=="], + + "@octokit/request-error": ["@octokit/request-error@6.1.8", "", { "dependencies": { "@octokit/types": "^14.0.0" } }, "sha512-WEi/R0Jmq+IJKydWlKDmryPcmdYSVjL3ekaiEL1L9eo1sUnqMJ+grqmC9cjk7CA7+b2/T397tO5d8YLOH3qYpQ=="], + + "@octokit/rest": ["@octokit/rest@21.1.1", "", { "dependencies": { "@octokit/core": "^6.1.4", "@octokit/plugin-paginate-rest": "^11.4.2", "@octokit/plugin-request-log": "^5.3.1", "@octokit/plugin-rest-endpoint-methods": "^13.3.0" } }, "sha512-sTQV7va0IUVZcntzy1q3QqPm/r8rWtDCqpRAmb8eXXnKkjoQEtFe3Nt5GTVsHft+R6jJoHeSiVLcgcvhtue/rg=="], + + "@octokit/types": ["@octokit/types@14.1.0", "", { "dependencies": { "@octokit/openapi-types": "^25.1.0" } }, "sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g=="], + + "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], + + "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], + + "@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="], + + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.44.0", "", { "os": "android", "cpu": "arm" }, "sha512-xEiEE5oDW6tK4jXCAyliuntGR+amEMO7HLtdSshVuhFnKTYoeYMyXQK7pLouAJJj5KHdwdn87bfHAR2nSdNAUA=="], + + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.44.0", "", { "os": "android", "cpu": "arm64" }, "sha512-uNSk/TgvMbskcHxXYHzqwiyBlJ/lGcv8DaUfcnNwict8ba9GTTNxfn3/FAoFZYgkaXXAdrAA+SLyKplyi349Jw=="], + + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.44.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-VGF3wy0Eq1gcEIkSCr8Ke03CWT+Pm2yveKLaDvq51pPpZza3JX/ClxXOCmTYYq3us5MvEuNRTaeyFThCKRQhOA=="], + + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.44.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-fBkyrDhwquRvrTxSGH/qqt3/T0w5Rg0L7ZIDypvBPc1/gzjJle6acCpZ36blwuwcKD/u6oCE/sRWlUAcxLWQbQ=="], + + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.44.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-u5AZzdQJYJXByB8giQ+r4VyfZP+walV+xHWdaFx/1VxsOn6eWJhK2Vl2eElvDJFKQBo/hcYIBg/jaKS8ZmKeNQ=="], + + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.44.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-qC0kS48c/s3EtdArkimctY7h3nHicQeEUdjJzYVJYR3ct3kWSafmn6jkNCA8InbUdge6PVx6keqjk5lVGJf99g=="], + + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.44.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x+e/Z9H0RAWckn4V2OZZl6EmV0L2diuX3QB0uM1r6BvhUIv6xBPL5mrAX2E3e8N8rEHVPwFfz/ETUbV4oW9+lQ=="], + + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.44.0", "", { "os": "linux", "cpu": "arm" }, "sha512-1exwiBFf4PU/8HvI8s80icyCcnAIB86MCBdst51fwFmH5dyeoWVPVgmQPcKrMtBQ0W5pAs7jBCWuRXgEpRzSCg=="], + + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.44.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-ZTR2mxBHb4tK4wGf9b8SYg0Y6KQPjGpR4UWwTFdnmjB4qRtoATZ5dWn3KsDwGa5Z2ZBOE7K52L36J9LueKBdOQ=="], + + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.44.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-GFWfAhVhWGd4r6UxmnKRTBwP1qmModHtd5gkraeW2G490BpFOZkFtem8yuX2NyafIP/mGpRJgTJ2PwohQkUY/Q=="], + + "@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.44.0", "", { "os": "linux", "cpu": "none" }, "sha512-xw+FTGcov/ejdusVOqKgMGW3c4+AgqrfvzWEVXcNP6zq2ue+lsYUgJ+5Rtn/OTJf7e2CbgTFvzLW2j0YAtj0Gg=="], + + "@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.44.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-bKGibTr9IdF0zr21kMvkZT4K6NV+jjRnBoVMt2uNMG0BYWm3qOVmYnXKzx7UhwrviKnmK46IKMByMgvpdQlyJQ=="], + + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.44.0", "", { "os": "linux", "cpu": "none" }, "sha512-vV3cL48U5kDaKZtXrti12YRa7TyxgKAIDoYdqSIOMOFBXqFj2XbChHAtXquEn2+n78ciFgr4KIqEbydEGPxXgA=="], + + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.44.0", "", { "os": "linux", "cpu": "none" }, "sha512-TDKO8KlHJuvTEdfw5YYFBjhFts2TR0VpZsnLLSYmB7AaohJhM8ctDSdDnUGq77hUh4m/djRafw+9zQpkOanE2Q=="], + + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.44.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-8541GEyktXaw4lvnGp9m84KENcxInhAt6vPWJ9RodsB/iGjHoMB2Pp5MVBCiKIRxrxzJhGCxmNzdu+oDQ7kwRA=="], + + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.44.0", "", { "os": "linux", "cpu": "x64" }, "sha512-iUVJc3c0o8l9Sa/qlDL2Z9UP92UZZW1+EmQ4xfjTc1akr0iUFZNfxrXJ/R1T90h/ILm9iXEY6+iPrmYB3pXKjw=="], + + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.44.0", "", { "os": "linux", "cpu": "x64" }, "sha512-PQUobbhLTQT5yz/SPg116VJBgz+XOtXt8D1ck+sfJJhuEsMj2jSej5yTdp8CvWBSceu+WW+ibVL6dm0ptG5fcA=="], + + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.44.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-M0CpcHf8TWn+4oTxJfh7LQuTuaYeXGbk0eageVjQCKzYLsajWS/lFC94qlRqOlyC2KvRT90ZrfXULYmukeIy7w=="], + + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.44.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-3XJ0NQtMAXTWFW8FqZKcw3gOQwBtVWP/u8TpHP3CRPXD7Pd6s8lLdH3sHWh8vqKCyyiI8xW5ltJScQmBU9j7WA=="], + + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.44.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Q2Mgwt+D8hd5FIPUuPDsvPR7Bguza6yTkJxspDGkZj7tBRn2y4KSWYuIXpftFSjBra76TbKerCV7rgFPQrn+wQ=="], + + "@smithy/abort-controller": ["@smithy/abort-controller@4.0.4", "", { "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-gJnEjZMvigPDQWHrW3oPrFhQtkrgqBkyjj3pCIdF3A5M6vsZODG93KNlfJprv6bp4245bdT32fsHK4kkH3KYDA=="], + + "@smithy/chunked-blob-reader": ["@smithy/chunked-blob-reader@5.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-+sKqDBQqb036hh4NPaUiEkYFkTUGYzRsn3EuFhyfQfMy6oGHEUJDurLP9Ufb5dasr/XiAmPNMr6wa9afjQB+Gw=="], + + "@smithy/chunked-blob-reader-native": ["@smithy/chunked-blob-reader-native@4.0.0", "", { "dependencies": { "@smithy/util-base64": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-R9wM2yPmfEMsUmlMlIgSzOyICs0x9uu7UTHoccMyt7BWw8shcGM8HqB355+BZCPBcySvbTYMs62EgEQkNxz2ig=="], + + "@smithy/config-resolver": ["@smithy/config-resolver@4.1.4", "", { "dependencies": { "@smithy/node-config-provider": "^4.1.3", "@smithy/types": "^4.3.1", "@smithy/util-config-provider": "^4.0.0", "@smithy/util-middleware": "^4.0.4", "tslib": "^2.6.2" } }, "sha512-prmU+rDddxHOH0oNcwemL+SwnzcG65sBF2yXRO7aeXIn/xTlq2pX7JLVbkBnVLowHLg4/OL4+jBmv9hVrVGS+w=="], + + "@smithy/core": ["@smithy/core@3.5.3", "", { "dependencies": { "@smithy/middleware-serde": "^4.0.8", "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-middleware": "^4.0.4", "@smithy/util-stream": "^4.2.2", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-xa5byV9fEguZNofCclv6v9ra0FYh5FATQW/da7FQUVTic94DfrN/NvmKZjrMyzbpqfot9ZjBaO8U1UeTbmSLuA=="], + + "@smithy/credential-provider-imds": ["@smithy/credential-provider-imds@4.0.6", "", { "dependencies": { "@smithy/node-config-provider": "^4.1.3", "@smithy/property-provider": "^4.0.4", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "tslib": "^2.6.2" } }, "sha512-hKMWcANhUiNbCJouYkZ9V3+/Qf9pteR1dnwgdyzR09R4ODEYx8BbUysHwRSyex4rZ9zapddZhLFTnT4ZijR4pw=="], + + "@smithy/eventstream-codec": ["@smithy/eventstream-codec@4.0.4", "", { "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.3.1", "@smithy/util-hex-encoding": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-7XoWfZqWb/QoR/rAU4VSi0mWnO2vu9/ltS6JZ5ZSZv0eovLVfDfu0/AX4ub33RsJTOth3TiFWSHS5YdztvFnig=="], + + "@smithy/eventstream-serde-browser": ["@smithy/eventstream-serde-browser@4.0.4", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-3fb/9SYaYqbpy/z/H3yIi0bYKyAa89y6xPmIqwr2vQiUT2St+avRt8UKwsWt9fEdEasc5d/V+QjrviRaX1JRFA=="], + + "@smithy/eventstream-serde-config-resolver": ["@smithy/eventstream-serde-config-resolver@4.1.2", "", { "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-JGtambizrWP50xHgbzZI04IWU7LdI0nh/wGbqH3sJesYToMi2j/DcoElqyOcqEIG/D4tNyxgRuaqBXWE3zOFhQ=="], + + "@smithy/eventstream-serde-node": ["@smithy/eventstream-serde-node@4.0.4", "", { "dependencies": { "@smithy/eventstream-serde-universal": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-RD6UwNZ5zISpOWPuhVgRz60GkSIp0dy1fuZmj4RYmqLVRtejFqQ16WmfYDdoSoAjlp1LX+FnZo+/hkdmyyGZ1w=="], + + "@smithy/eventstream-serde-universal": ["@smithy/eventstream-serde-universal@4.0.4", "", { "dependencies": { "@smithy/eventstream-codec": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-UeJpOmLGhq1SLox79QWw/0n2PFX+oPRE1ZyRMxPIaFEfCqWaqpB7BU9C8kpPOGEhLF7AwEqfFbtwNxGy4ReENA=="], + + "@smithy/fetch-http-handler": ["@smithy/fetch-http-handler@5.0.4", "", { "dependencies": { "@smithy/protocol-http": "^5.1.2", "@smithy/querystring-builder": "^4.0.4", "@smithy/types": "^4.3.1", "@smithy/util-base64": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-AMtBR5pHppYMVD7z7G+OlHHAcgAN7v0kVKEpHuTO4Gb199Gowh0taYi9oDStFeUhetkeP55JLSVlTW1n9rFtUw=="], + + "@smithy/hash-blob-browser": ["@smithy/hash-blob-browser@4.0.4", "", { "dependencies": { "@smithy/chunked-blob-reader": "^5.0.0", "@smithy/chunked-blob-reader-native": "^4.0.0", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-WszRiACJiQV3QG6XMV44i5YWlkrlsM5Yxgz4jvsksuu7LDXA6wAtypfPajtNTadzpJy3KyJPoWehYpmZGKUFIQ=="], + + "@smithy/hash-node": ["@smithy/hash-node@4.0.4", "", { "dependencies": { "@smithy/types": "^4.3.1", "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-qnbTPUhCVnCgBp4z4BUJUhOEkVwxiEi1cyFM+Zj6o+aY8OFGxUQleKWq8ltgp3dujuhXojIvJWdoqpm6dVO3lQ=="], + + "@smithy/hash-stream-node": ["@smithy/hash-stream-node@4.0.4", "", { "dependencies": { "@smithy/types": "^4.3.1", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-wHo0d8GXyVmpmMh/qOR0R7Y46/G1y6OR8U+bSTB4ppEzRxd1xVAQ9xOE9hOc0bSjhz0ujCPAbfNLkLrpa6cevg=="], + + "@smithy/invalid-dependency": ["@smithy/invalid-dependency@4.0.4", "", { "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-bNYMi7WKTJHu0gn26wg8OscncTt1t2b8KcsZxvOv56XA6cyXtOAAAaNP7+m45xfppXfOatXF3Sb1MNsLUgVLTw=="], + + "@smithy/is-array-buffer": ["@smithy/is-array-buffer@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-saYhF8ZZNoJDTvJBEWgeBccCg+yvp1CX+ed12yORU3NilJScfc6gfch2oVb4QgxZrGUx3/ZJlb+c/dJbyupxlw=="], + + "@smithy/md5-js": ["@smithy/md5-js@4.0.4", "", { "dependencies": { "@smithy/types": "^4.3.1", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-uGLBVqcOwrLvGh/v/jw423yWHq/ofUGK1W31M2TNspLQbUV1Va0F5kTxtirkoHawODAZcjXTSGi7JwbnPcDPJg=="], + + "@smithy/middleware-content-length": ["@smithy/middleware-content-length@4.0.4", "", { "dependencies": { "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-F7gDyfI2BB1Kc+4M6rpuOLne5LOcEknH1n6UQB69qv+HucXBR1rkzXBnQTB2q46sFy1PM/zuSJOB532yc8bg3w=="], + + "@smithy/middleware-endpoint": ["@smithy/middleware-endpoint@4.1.11", "", { "dependencies": { "@smithy/core": "^3.5.3", "@smithy/middleware-serde": "^4.0.8", "@smithy/node-config-provider": "^4.1.3", "@smithy/shared-ini-file-loader": "^4.0.4", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-middleware": "^4.0.4", "tslib": "^2.6.2" } }, "sha512-zDogwtRLzKl58lVS8wPcARevFZNBOOqnmzWWxVe9XiaXU2CADFjvJ9XfNibgkOWs08sxLuSr81NrpY4mgp9OwQ=="], + + "@smithy/middleware-retry": ["@smithy/middleware-retry@4.1.12", "", { "dependencies": { "@smithy/node-config-provider": "^4.1.3", "@smithy/protocol-http": "^5.1.2", "@smithy/service-error-classification": "^4.0.5", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.5", "tslib": "^2.6.2", "uuid": "^9.0.1" } }, "sha512-wvIH70c4e91NtRxdaLZF+mbLZ/HcC6yg7ySKUiufL6ESp6zJUSnJucZ309AvG9nqCFHSRB5I6T3Ez1Q9wCh0Ww=="], + + "@smithy/middleware-serde": ["@smithy/middleware-serde@4.0.8", "", { "dependencies": { "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-iSSl7HJoJaGyMIoNn2B7czghOVwJ9nD7TMvLhMWeSB5vt0TnEYyRRqPJu/TqW76WScaNvYYB8nRoiBHR9S1Ddw=="], + + "@smithy/middleware-stack": ["@smithy/middleware-stack@4.0.4", "", { "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-kagK5ggDrBUCCzI93ft6DjteNSfY8Ulr83UtySog/h09lTIOAJ/xUSObutanlPT0nhoHAkpmW9V5K8oPyLh+QA=="], + + "@smithy/node-config-provider": ["@smithy/node-config-provider@4.1.3", "", { "dependencies": { "@smithy/property-provider": "^4.0.4", "@smithy/shared-ini-file-loader": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-HGHQr2s59qaU1lrVH6MbLlmOBxadtzTsoO4c+bF5asdgVik3I8o7JIOzoeqWc5MjVa+vD36/LWE0iXKpNqooRw=="], + + "@smithy/node-http-handler": ["@smithy/node-http-handler@4.0.6", "", { "dependencies": { "@smithy/abort-controller": "^4.0.4", "@smithy/protocol-http": "^5.1.2", "@smithy/querystring-builder": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-NqbmSz7AW2rvw4kXhKGrYTiJVDHnMsFnX4i+/FzcZAfbOBauPYs2ekuECkSbtqaxETLLTu9Rl/ex6+I2BKErPA=="], + + "@smithy/property-provider": ["@smithy/property-provider@4.0.4", "", { "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-qHJ2sSgu4FqF4U/5UUp4DhXNmdTrgmoAai6oQiM+c5RZ/sbDwJ12qxB1M6FnP+Tn/ggkPZf9ccn4jqKSINaquw=="], + + "@smithy/protocol-http": ["@smithy/protocol-http@5.1.2", "", { "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-rOG5cNLBXovxIrICSBm95dLqzfvxjEmuZx4KK3hWwPFHGdW3lxY0fZNXfv2zebfRO7sJZ5pKJYHScsqopeIWtQ=="], + + "@smithy/querystring-builder": ["@smithy/querystring-builder@4.0.4", "", { "dependencies": { "@smithy/types": "^4.3.1", "@smithy/util-uri-escape": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-SwREZcDnEYoh9tLNgMbpop+UTGq44Hl9tdj3rf+yeLcfH7+J8OXEBaMc2kDxtyRHu8BhSg9ADEx0gFHvpJgU8w=="], + + "@smithy/querystring-parser": ["@smithy/querystring-parser@4.0.4", "", { "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-6yZf53i/qB8gRHH/l2ZwUG5xgkPgQF15/KxH0DdXMDHjesA9MeZje/853ifkSY0x4m5S+dfDZ+c4x439PF0M2w=="], + + "@smithy/service-error-classification": ["@smithy/service-error-classification@4.0.5", "", { "dependencies": { "@smithy/types": "^4.3.1" } }, "sha512-LvcfhrnCBvCmTee81pRlh1F39yTS/+kYleVeLCwNtkY8wtGg8V/ca9rbZZvYIl8OjlMtL6KIjaiL/lgVqHD2nA=="], + + "@smithy/shared-ini-file-loader": ["@smithy/shared-ini-file-loader@4.0.4", "", { "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-63X0260LoFBjrHifPDs+nM9tV0VMkOTl4JRMYNuKh/f5PauSjowTfvF3LogfkWdcPoxsA9UjqEOgjeYIbhb7Nw=="], + + "@smithy/signature-v4": ["@smithy/signature-v4@5.1.2", "", { "dependencies": { "@smithy/is-array-buffer": "^4.0.0", "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", "@smithy/util-hex-encoding": "^4.0.0", "@smithy/util-middleware": "^4.0.4", "@smithy/util-uri-escape": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-d3+U/VpX7a60seHziWnVZOHuEgJlclufjkS6zhXvxcJgkJq4UWdH5eOBLzHRMx6gXjsdT9h6lfpmLzbrdupHgQ=="], + + "@smithy/smithy-client": ["@smithy/smithy-client@4.4.3", "", { "dependencies": { "@smithy/core": "^3.5.3", "@smithy/middleware-endpoint": "^4.1.11", "@smithy/middleware-stack": "^4.0.4", "@smithy/protocol-http": "^5.1.2", "@smithy/types": "^4.3.1", "@smithy/util-stream": "^4.2.2", "tslib": "^2.6.2" } }, "sha512-xxzNYgA0HD6ETCe5QJubsxP0hQH3QK3kbpJz3QrosBCuIWyEXLR/CO5hFb2OeawEKUxMNhz3a1nuJNN2np2RMA=="], + + "@smithy/types": ["@smithy/types@4.3.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-UqKOQBL2x6+HWl3P+3QqFD4ncKq0I8Nuz9QItGv5WuKuMHuuwlhvqcZCoXGfc+P1QmfJE7VieykoYYmrOoFJxA=="], + + "@smithy/url-parser": ["@smithy/url-parser@4.0.4", "", { "dependencies": { "@smithy/querystring-parser": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-eMkc144MuN7B0TDA4U2fKs+BqczVbk3W+qIvcoCY6D1JY3hnAdCuhCZODC+GAeaxj0p6Jroz4+XMUn3PCxQQeQ=="], + + "@smithy/util-base64": ["@smithy/util-base64@4.0.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-CvHfCmO2mchox9kjrtzoHkWHxjHZzaFojLc8quxXY7WAAMAg43nuxwv95tATVgQFNDwd4M9S1qFzj40Ul41Kmg=="], + + "@smithy/util-body-length-browser": ["@smithy/util-body-length-browser@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-sNi3DL0/k64/LO3A256M+m3CDdG6V7WKWHdAiBBMUN8S3hK3aMPhwnPik2A/a2ONN+9doY9UxaLfgqsIRg69QA=="], + + "@smithy/util-body-length-node": ["@smithy/util-body-length-node@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-q0iDP3VsZzqJyje8xJWEJCNIu3lktUGVoSy1KB0UWym2CL1siV3artm+u1DFYTLejpsrdGyCSWBdGNjJzfDPjg=="], + + "@smithy/util-buffer-from": ["@smithy/util-buffer-from@4.0.0", "", { "dependencies": { "@smithy/is-array-buffer": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-9TOQ7781sZvddgO8nxueKi3+yGvkY35kotA0Y6BWRajAv8jjmigQ1sBwz0UX47pQMYXJPahSKEKYFgt+rXdcug=="], + + "@smithy/util-config-provider": ["@smithy/util-config-provider@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-L1RBVzLyfE8OXH+1hsJ8p+acNUSirQnWQ6/EgpchV88G6zGBTDPdXiiExei6Z1wR2RxYvxY/XLw6AMNCCt8H3w=="], + + "@smithy/util-defaults-mode-browser": ["@smithy/util-defaults-mode-browser@4.0.19", "", { "dependencies": { "@smithy/property-provider": "^4.0.4", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "sha512-mvLMh87xSmQrV5XqnUYEPoiFFeEGYeAKIDDKdhE2ahqitm8OHM3aSvhqL6rrK6wm1brIk90JhxDf5lf2hbrLbQ=="], + + "@smithy/util-defaults-mode-node": ["@smithy/util-defaults-mode-node@4.0.19", "", { "dependencies": { "@smithy/config-resolver": "^4.1.4", "@smithy/credential-provider-imds": "^4.0.6", "@smithy/node-config-provider": "^4.1.3", "@smithy/property-provider": "^4.0.4", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-8tYnx+LUfj6m+zkUUIrIQJxPM1xVxfRBvoGHua7R/i6qAxOMjqR6CpEpDwKoIs1o0+hOjGvkKE23CafKL0vJ9w=="], + + "@smithy/util-endpoints": ["@smithy/util-endpoints@3.0.6", "", { "dependencies": { "@smithy/node-config-provider": "^4.1.3", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-YARl3tFL3WgPuLzljRUnrS2ngLiUtkwhQtj8PAL13XZSyUiNLQxwG3fBBq3QXFqGFUXepIN73pINp3y8c2nBmA=="], + + "@smithy/util-hex-encoding": ["@smithy/util-hex-encoding@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-Yk5mLhHtfIgW2W2WQZWSg5kuMZCVbvhFmC7rV4IO2QqnZdbEFPmQnCcGMAX2z/8Qj3B9hYYNjZOhWym+RwhePw=="], + + "@smithy/util-middleware": ["@smithy/util-middleware@4.0.4", "", { "dependencies": { "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-9MLKmkBmf4PRb0ONJikCbCwORACcil6gUWojwARCClT7RmLzF04hUR4WdRprIXal7XVyrddadYNfp2eF3nrvtQ=="], + + "@smithy/util-retry": ["@smithy/util-retry@4.0.5", "", { "dependencies": { "@smithy/service-error-classification": "^4.0.5", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-V7MSjVDTlEt/plmOFBn1762Dyu5uqMrV2Pl2X0dYk4XvWfdWJNe9Bs5Bzb56wkCuiWjSfClVMGcsuKrGj7S/yg=="], + + "@smithy/util-stream": ["@smithy/util-stream@4.2.2", "", { "dependencies": { "@smithy/fetch-http-handler": "^5.0.4", "@smithy/node-http-handler": "^4.0.6", "@smithy/types": "^4.3.1", "@smithy/util-base64": "^4.0.0", "@smithy/util-buffer-from": "^4.0.0", "@smithy/util-hex-encoding": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-aI+GLi7MJoVxg24/3J1ipwLoYzgkB4kUfogZfnslcYlynj3xsQ0e7vk4TnTro9hhsS5PvX1mwmkRqqHQjwcU7w=="], + + "@smithy/util-uri-escape": ["@smithy/util-uri-escape@4.0.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-77yfbCbQMtgtTylO9itEAdpPXSog3ZxMe09AEhm0dU0NLTalV70ghDZFR+Nfi1C60jnJoh/Re4090/DuZh2Omg=="], + + "@smithy/util-utf8": ["@smithy/util-utf8@4.0.0", "", { "dependencies": { "@smithy/util-buffer-from": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-b+zebfKCfRdgNJDknHCob3O7FpeYQN6ZG6YLExMcasDHsCXlsXCEuiPZeLnJLpwa5dvPetGlnGCiMHuLwGvFow=="], + + "@smithy/util-waiter": ["@smithy/util-waiter@4.0.5", "", { "dependencies": { "@smithy/abort-controller": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-4QvC49HTteI1gfemu0I1syWovJgPvGn7CVUoN9ZFkdvr/cCFkrEL7qNCdx/2eICqDWEGnnr68oMdSIPCLAriSQ=="], + + "@sveltejs/acorn-typescript": ["@sveltejs/acorn-typescript@1.0.5", "", { "peerDependencies": { "acorn": "^8.9.0" } }, "sha512-IwQk4yfwLdibDlrXVE04jTZYlLnwsTT2PIOQQGNLWfjavGifnk1JD1LcZjZaBTRcxZu2FfPfNLOE04DSu9lqtQ=="], + + "@sveltejs/adapter-auto": ["@sveltejs/adapter-auto@6.0.1", "", { "peerDependencies": { "@sveltejs/kit": "^2.0.0" } }, "sha512-mcWud3pYGPWM2Pphdj8G9Qiq24nZ8L4LB7coCUckUEy5Y7wOWGJ/enaZ4AtJTcSm5dNK1rIkBRoqt+ae4zlxcQ=="], + + "@sveltejs/adapter-cloudflare": ["@sveltejs/adapter-cloudflare@7.0.4", "", { "dependencies": { "@cloudflare/workers-types": "^4.20250507.0", "worktop": "0.8.0-next.18" }, "peerDependencies": { "@sveltejs/kit": "^2.0.0", "wrangler": "^4.0.0" } }, "sha512-pYJDICmhatM9lofkjLR+nhAJ4prEPjmwmx+J7hBuMSfrrNEVk+THfAahuWNizcxae4mO1MQKjIRMLpVnKyNE5g=="], + + "@sveltejs/kit": ["@sveltejs/kit@2.22.0", "", { "dependencies": { "@sveltejs/acorn-typescript": "^1.0.5", "@types/cookie": "^0.6.0", "acorn": "^8.14.1", "cookie": "^0.6.0", "devalue": "^5.1.0", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.0", "sirv": "^3.0.0", "vitefu": "^1.0.6" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0" }, "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-DJm0UxVgzXq+1MUfiJK4Ridk7oIQsIets6JwHiEl97sI6nXScfXe+BeqNhzB7jQIVBb3BM51U4hNk8qQxRXBAA=="], + + "@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@5.1.0", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^4.0.1", "debug": "^4.4.1", "deepmerge": "^4.3.1", "kleur": "^4.1.5", "magic-string": "^0.30.17", "vitefu": "^1.0.6" }, "peerDependencies": { "svelte": "^5.0.0", "vite": "^6.0.0" } }, "sha512-wojIS/7GYnJDYIg1higWj2ROA6sSRWvcR1PO/bqEyFr/5UZah26c8Cz4u0NaqjPeVltzsVpt2Tm8d2io0V+4Tw=="], + + "@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@4.0.1", "", { "dependencies": { "debug": "^4.3.7" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^5.0.0", "svelte": "^5.0.0", "vite": "^6.0.0" } }, "sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw=="], + + "@swc/core": ["@swc/core@1.12.5", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.23" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.12.5", "@swc/core-darwin-x64": "1.12.5", "@swc/core-linux-arm-gnueabihf": "1.12.5", "@swc/core-linux-arm64-gnu": "1.12.5", "@swc/core-linux-arm64-musl": "1.12.5", "@swc/core-linux-x64-gnu": "1.12.5", "@swc/core-linux-x64-musl": "1.12.5", "@swc/core-win32-arm64-msvc": "1.12.5", "@swc/core-win32-ia32-msvc": "1.12.5", "@swc/core-win32-x64-msvc": "1.12.5" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-KxA0PHHIuUBmQ/Oi+xFpVzILj2Oo37sTtftCbyowQlyx5YOknEOw1kLpas5hMcpznXgFyAWbpK71xQps4INPgA=="], + + "@swc/core-darwin-arm64": ["@swc/core-darwin-arm64@1.12.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-3WF+naP/qkt5flrTfJr+p07b522JcixKvIivM7FgvllA6LjJxf+pheoILrTS8IwrNAK/XtHfKWYcGY+3eaA4mA=="], + + "@swc/core-darwin-x64": ["@swc/core-darwin-x64@1.12.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-GCcD3dft8YN7unTBcW02Fx41jXp2MNQHCjx5ceWSEYOGvn7vBSUp7k7LkfTxGN5Ftxb9a1mxhPq8r4rD2u/aPw=="], + + "@swc/core-linux-arm-gnueabihf": ["@swc/core-linux-arm-gnueabihf@1.12.5", "", { "os": "linux", "cpu": "arm" }, "sha512-jWlzP/Y4+wbE/EJM+WGIDQsklLFV3g5LmbYTBgrY4+5nb517P31mkBzf5y2knfNWPrL7HzNu0578j3Zi2E6Iig=="], + + "@swc/core-linux-arm64-gnu": ["@swc/core-linux-arm64-gnu@1.12.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-GkzgIUz+2r6J6Tn3hb7/4ByaWHRrRZt4vuN9BLAd+y65m2Bt0vlEpPtWhrB/TVe4hEkFR+W5PDETLEbUT4i0tQ=="], + + "@swc/core-linux-arm64-musl": ["@swc/core-linux-arm64-musl@1.12.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-g0AJ7QmZPj3Uw+C5pDa48LAUG7JBgQmB0mN5cW+s2mjaFKT0mTSxYALtx/MDZwJExDPo0yJV8kSbFO1tvFPyhg=="], + + "@swc/core-linux-x64-gnu": ["@swc/core-linux-x64-gnu@1.12.5", "", { "os": "linux", "cpu": "x64" }, "sha512-PeYoSziNy+iNiBHPtAsO84bzBne/mbCsG5ijYkAhS1GVsDgohClorUvRXXhcUZoX2gr8TfSI9WLHo30K+DKiHg=="], + + "@swc/core-linux-x64-musl": ["@swc/core-linux-x64-musl@1.12.5", "", { "os": "linux", "cpu": "x64" }, "sha512-EJrfCCIyuV5LLmYgKtIMwtgsnjVesdFe0IgQzEKs9OfB6cL6g7WO9conn8BkGX8jphVa7jChKxShDGkreWWDzA=="], + + "@swc/core-win32-arm64-msvc": ["@swc/core-win32-arm64-msvc@1.12.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-FnwT7fxkJJMgsfiDoZKEVGyCzrPFbzpflFAAoTCUCu3MaHw6mW55o/MAAfofvJ1iIcEpec4o93OilsmKtpyO5Q=="], + + "@swc/core-win32-ia32-msvc": ["@swc/core-win32-ia32-msvc@1.12.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-jW6l4KFt9mIXSpGseE6BQOEFmbIeXeShDuWgldEJXKeXf/uPs8wrqv80XBIUwVpK0ZbmJwPQ0waGVj8UM3th2Q=="], + + "@swc/core-win32-x64-msvc": ["@swc/core-win32-x64-msvc@1.12.5", "", { "os": "win32", "cpu": "x64" }, "sha512-AZszwuEjlz1tSNLQRm3T5OZJ5eebxjJlDQnnzXJmg0B7DJMRoaAe1HTLOmejxjFK6yWr7fh+pSeCw2PgQLxgqA=="], + + "@swc/counter": ["@swc/counter@0.1.3", "", {}, "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="], + + "@swc/types": ["@swc/types@0.1.23", "", { "dependencies": { "@swc/counter": "^0.1.3" } }, "sha512-u1iIVZV9Q0jxY+yM2vw/hZGDNudsN85bBpTqzAQ9rzkxW9D+e3aEM4Han+ow518gSewkXgjmEK0BD79ZcNVgPw=="], + + "@tailwindcss/node": ["@tailwindcss/node@4.1.10", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", "lightningcss": "1.30.1", "magic-string": "^0.30.17", "source-map-js": "^1.2.1", "tailwindcss": "4.1.10" } }, "sha512-2ACf1znY5fpRBwRhMgj9ZXvb2XZW8qs+oTfotJ2C5xR0/WNL7UHZ7zXl6s+rUqedL1mNi+0O+WQr5awGowS3PQ=="], + + "@tailwindcss/oxide": ["@tailwindcss/oxide@4.1.10", "", { "dependencies": { "detect-libc": "^2.0.4", "tar": "^7.4.3" }, "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.1.10", "@tailwindcss/oxide-darwin-arm64": "4.1.10", "@tailwindcss/oxide-darwin-x64": "4.1.10", "@tailwindcss/oxide-freebsd-x64": "4.1.10", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.10", "@tailwindcss/oxide-linux-arm64-gnu": "4.1.10", "@tailwindcss/oxide-linux-arm64-musl": "4.1.10", "@tailwindcss/oxide-linux-x64-gnu": "4.1.10", "@tailwindcss/oxide-linux-x64-musl": "4.1.10", "@tailwindcss/oxide-wasm32-wasi": "4.1.10", "@tailwindcss/oxide-win32-arm64-msvc": "4.1.10", "@tailwindcss/oxide-win32-x64-msvc": "4.1.10" } }, "sha512-v0C43s7Pjw+B9w21htrQwuFObSkio2aV/qPx/mhrRldbqxbWJK6KizM+q7BF1/1CmuLqZqX3CeYF7s7P9fbA8Q=="], + + "@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.1.10", "", { "os": "android", "cpu": "arm64" }, "sha512-VGLazCoRQ7rtsCzThaI1UyDu/XRYVyH4/EWiaSX6tFglE+xZB5cvtC5Omt0OQ+FfiIVP98su16jDVHDEIuH4iQ=="], + + "@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.1.10", "", { "os": "darwin", "cpu": "arm64" }, "sha512-ZIFqvR1irX2yNjWJzKCqTCcHZbgkSkSkZKbRM3BPzhDL/18idA8uWCoopYA2CSDdSGFlDAxYdU2yBHwAwx8euQ=="], + + "@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.1.10", "", { "os": "darwin", "cpu": "x64" }, "sha512-eCA4zbIhWUFDXoamNztmS0MjXHSEJYlvATzWnRiTqJkcUteSjO94PoRHJy1Xbwp9bptjeIxxBHh+zBWFhttbrQ=="], + + "@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.1.10", "", { "os": "freebsd", "cpu": "x64" }, "sha512-8/392Xu12R0cc93DpiJvNpJ4wYVSiciUlkiOHOSOQNH3adq9Gi/dtySK7dVQjXIOzlpSHjeCL89RUUI8/GTI6g=="], + + "@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.1.10", "", { "os": "linux", "cpu": "arm" }, "sha512-t9rhmLT6EqeuPT+MXhWhlRYIMSfh5LZ6kBrC4FS6/+M1yXwfCtp24UumgCWOAJVyjQwG+lYva6wWZxrfvB+NhQ=="], + + "@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.1.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-3oWrlNlxLRxXejQ8zImzrVLuZ/9Z2SeKoLhtCu0hpo38hTO2iL86eFOu4sVR8cZc6n3z7eRXXqtHJECa6mFOvA=="], + + "@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.1.10", "", { "os": "linux", "cpu": "arm64" }, "sha512-saScU0cmWvg/Ez4gUmQWr9pvY9Kssxt+Xenfx1LG7LmqjcrvBnw4r9VjkFcqmbBb7GCBwYNcZi9X3/oMda9sqQ=="], + + "@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.1.10", "", { "os": "linux", "cpu": "x64" }, "sha512-/G3ao/ybV9YEEgAXeEg28dyH6gs1QG8tvdN9c2MNZdUXYBaIY/Gx0N6RlJzfLy/7Nkdok4kaxKPHKJUlAaoTdA=="], + + "@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.1.10", "", { "os": "linux", "cpu": "x64" }, "sha512-LNr7X8fTiKGRtQGOerSayc2pWJp/9ptRYAa4G+U+cjw9kJZvkopav1AQc5HHD+U364f71tZv6XamaHKgrIoVzA=="], + + "@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.1.10", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@emnapi/wasi-threads": "^1.0.2", "@napi-rs/wasm-runtime": "^0.2.10", "@tybys/wasm-util": "^0.9.0", "tslib": "^2.8.0" }, "cpu": "none" }, "sha512-d6ekQpopFQJAcIK2i7ZzWOYGZ+A6NzzvQ3ozBvWFdeyqfOZdYHU66g5yr+/HC4ipP1ZgWsqa80+ISNILk+ae/Q=="], + + "@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.1.10", "", { "os": "win32", "cpu": "arm64" }, "sha512-i1Iwg9gRbwNVOCYmnigWCCgow8nDWSFmeTUU5nbNx3rqbe4p0kRbEqLwLJbYZKmSSp23g4N6rCDmm7OuPBXhDA=="], + + "@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.1.10", "", { "os": "win32", "cpu": "x64" }, "sha512-sGiJTjcBSfGq2DVRtaSljq5ZgZS2SDHSIfhOylkBvHVjwOsodBhnb3HdmiKkVuUGKD0I7G63abMOVaskj1KpOA=="], + + "@tailwindcss/vite": ["@tailwindcss/vite@4.1.10", "", { "dependencies": { "@tailwindcss/node": "4.1.10", "@tailwindcss/oxide": "4.1.10", "tailwindcss": "4.1.10" }, "peerDependencies": { "vite": "^5.2.0 || ^6" } }, "sha512-QWnD5HDY2IADv+vYR82lOhqOlS1jSCUUAmfem52cXAhRTKxpDh3ARX8TTXJTCCO7Rv7cD2Nlekabv02bwP3a2A=="], + + "@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="], + + "@types/diff-match-patch": ["@types/diff-match-patch@1.0.36", "", {}, "sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg=="], + + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + + "@types/node": ["@types/node@18.19.112", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-i+Vukt9POdS/MBI7YrrkkI5fMfwFtOjphSmt4WXYLfwqsfr6z/HdCx7LqT9M7JktGob8WNgj8nFB4TbGNE4Cog=="], + + "@types/node-fetch": ["@types/node-fetch@2.6.12", "", { "dependencies": { "@types/node": "*", "form-data": "^4.0.0" } }, "sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA=="], + + "@types/uuid": ["@types/uuid@9.0.8", "", {}, "sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA=="], + + "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="], + + "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], + + "acorn-walk": ["acorn-walk@8.3.2", "", {}, "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A=="], + + "agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="], + + "ai": ["ai@4.3.16", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8", "@ai-sdk/react": "1.2.12", "@ai-sdk/ui-utils": "1.2.11", "@opentelemetry/api": "1.9.0", "jsondiffpatch": "0.6.0" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "zod": "^3.23.8" }, "optionalPeers": ["react"] }, "sha512-KUDwlThJ5tr2Vw0A1ZkbDKNME3wzWhuVfAOwIvFUzl1TPVDFAXDFTXio3p+jaKneB+dKNCvFFlolYmmgHttG1g=="], + + "alchemy": ["alchemy@0.37.1", "", { "dependencies": { "@aws-sdk/credential-providers": "^3.0.0", "@cloudflare/unenv-preset": "^2.3.1", "@cloudflare/workers-shared": "^0.17.5", "@iarna/toml": "^2.2.5", "@smithy/node-config-provider": "^4.0.0", "@swc/core": "^1.11.24", "aws4fetch": "^1.0.20", "diff": "^8.0.2", "esbuild": "^0.25.1", "fast-json-patch": "^3.1.1", "glob": "^10.0.0", "jszip": "^3.0.0", "kleur": "^4.1.5", "libsodium-wrappers": "^0.7.15", "turndown": "^7.0.0", "unenv": "2.0.0-rc.15", "yaml": "^2.0.0" }, "peerDependencies": { "@ai-sdk/openai": "^1.1.9", "@ai-sdk/openai-compatible": "^0.2.2", "@aws-sdk/client-dynamodb": "^3.0.0", "@aws-sdk/client-iam": "^3.0.0", "@aws-sdk/client-lambda": "^3.0.0", "@aws-sdk/client-s3": "^3.0.0", "@aws-sdk/client-sagemaker": "^3.0.0", "@aws-sdk/client-ses": "^3.0.0", "@aws-sdk/client-sesv2": "^3.0.0", "@aws-sdk/client-sqs": "^3.0.0", "@aws-sdk/client-ssm": "^3.0.0", "@aws-sdk/client-sts": "^3.0.0", "@octokit/rest": "^21.1.1", "ai": "^4.0.0", "arktype": "^2.0.0", "cloudflare": "^4.0.0", "dofs": "^0.0.1", "hono": "^4.0.0", "prettier": "^3.0.0", "stripe": "^17.0.0", "zod": "^3.0.0" }, "bin": { "alchemy": "bin/alchemy.mjs" } }, "sha512-IvTvunSEbJZD6h+WDHQ73z6TEXuF9UuLhsVQ3wmNAZ/hZvTz+FQS8QM4P58p9YaKmAhfSnLjaH829XVdKSQIsA=="], + + "ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], + + "ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], + + "aria-query": ["aria-query@5.3.2", "", {}, "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw=="], + + "arktype": ["arktype@2.1.20", "", { "dependencies": { "@ark/schema": "0.46.0", "@ark/util": "0.46.0" } }, "sha512-IZCEEXaJ8g+Ijd59WtSYwtjnqXiwM8sWQ5EjGamcto7+HVN9eK0C4p0zDlCuAwWhpqr6fIBkxPuYDl4/Mcj/+Q=="], + + "as-table": ["as-table@1.0.55", "", { "dependencies": { "printable-characters": "^1.0.42" } }, "sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ=="], + + "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], + + "aws4fetch": ["aws4fetch@1.0.20", "", {}, "sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g=="], + + "axobject-query": ["axobject-query@4.1.0", "", {}, "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ=="], + + "balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + + "before-after-hook": ["before-after-hook@3.0.2", "", {}, "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A=="], + + "blake3-wasm": ["blake3-wasm@2.1.5", "", {}, "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g=="], + + "bowser": ["bowser@2.11.0", "", {}, "sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA=="], + + "brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + + "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + + "chalk": ["chalk@5.4.1", "", {}, "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w=="], + + "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], + + "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], + + "cloudflare": ["cloudflare@4.4.1", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }, "sha512-wrtQ9WMflnfRcmdQZf/XfVVkeucgwzzYeqFDfgbNdADTaexsPwrtt3etzUvPGvVUeEk9kOPfNkl8MSzObxrIsg=="], + + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + + "color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="], + + "color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="], + + "color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="], + + "color-string": ["color-string@1.9.1", "", { "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" } }, "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg=="], + + "combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="], + + "cookie": ["cookie@0.6.0", "", {}, "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw=="], + + "core-util-is": ["core-util-is@1.0.3", "", {}, "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="], + + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], + + "data-uri-to-buffer": ["data-uri-to-buffer@2.0.2", "", {}, "sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA=="], + + "debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], + + "deepmerge": ["deepmerge@4.3.1", "", {}, "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A=="], + + "defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="], + + "delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="], + + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], + + "detect-libc": ["detect-libc@2.0.4", "", {}, "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA=="], + + "devalue": ["devalue@5.1.1", "", {}, "sha512-maua5KUiapvEwiEAe+XnlZ3Rh0GD+qI1J/nb9vrJc3muPXvcF/8gXYTWF76+5DAqHyDUtOIImEuo0YKE9mshVw=="], + + "diff": ["diff@8.0.2", "", {}, "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg=="], + + "diff-match-patch": ["diff-match-patch@1.0.5", "", {}, "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw=="], + + "dofs": ["dofs@0.0.1", "", { "peerDependencies": { "hono": "^4.7.10" } }, "sha512-vNfYmLREQNQRE0R+ZiUuoW4VkxLZm/M6PHxAlTE5YDwrDIYdSTA0rfZ3Rgp7g2Tkvv5VI9xTt7pUtdMD46eM1Q=="], + + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + + "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], + + "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + + "enhanced-resolve": ["enhanced-resolve@5.18.1", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" } }, "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], + + "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], + + "esbuild": ["esbuild@0.25.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.5", "@esbuild/android-arm": "0.25.5", "@esbuild/android-arm64": "0.25.5", "@esbuild/android-x64": "0.25.5", "@esbuild/darwin-arm64": "0.25.5", "@esbuild/darwin-x64": "0.25.5", "@esbuild/freebsd-arm64": "0.25.5", "@esbuild/freebsd-x64": "0.25.5", "@esbuild/linux-arm": "0.25.5", "@esbuild/linux-arm64": "0.25.5", "@esbuild/linux-ia32": "0.25.5", "@esbuild/linux-loong64": "0.25.5", "@esbuild/linux-mips64el": "0.25.5", "@esbuild/linux-ppc64": "0.25.5", "@esbuild/linux-riscv64": "0.25.5", "@esbuild/linux-s390x": "0.25.5", "@esbuild/linux-x64": "0.25.5", "@esbuild/netbsd-arm64": "0.25.5", "@esbuild/netbsd-x64": "0.25.5", "@esbuild/openbsd-arm64": "0.25.5", "@esbuild/openbsd-x64": "0.25.5", "@esbuild/sunos-x64": "0.25.5", "@esbuild/win32-arm64": "0.25.5", "@esbuild/win32-ia32": "0.25.5", "@esbuild/win32-x64": "0.25.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ=="], + + "esm-env": ["esm-env@1.2.2", "", {}, "sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA=="], + + "esrap": ["esrap@1.4.9", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15" } }, "sha512-3OMlcd0a03UGuZpPeUC1HxR3nA23l+HEyCiZw3b3FumJIN9KphoGzDJKMXI1S72jVS1dsenDyQC0kJlO1U9E1g=="], + + "event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="], + + "exit-hook": ["exit-hook@2.2.1", "", {}, "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw=="], + + "exsolve": ["exsolve@1.0.7", "", {}, "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw=="], + + "fast-content-type-parse": ["fast-content-type-parse@2.0.1", "", {}, "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q=="], + + "fast-json-patch": ["fast-json-patch@3.1.1", "", {}, "sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ=="], + + "fast-xml-parser": ["fast-xml-parser@4.4.1", "", { "dependencies": { "strnum": "^1.0.5" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw=="], + + "fdir": ["fdir@6.4.6", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w=="], + + "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], + + "form-data": ["form-data@4.0.3", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA=="], + + "form-data-encoder": ["form-data-encoder@1.7.2", "", {}, "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A=="], + + "formdata-node": ["formdata-node@4.4.1", "", { "dependencies": { "node-domexception": "1.0.0", "web-streams-polyfill": "4.0.0-beta.3" } }, "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ=="], + + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], + + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], + + "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + + "get-source": ["get-source@2.0.12", "", { "dependencies": { "data-uri-to-buffer": "^2.0.0", "source-map": "^0.6.1" } }, "sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w=="], + + "glob": ["glob@10.4.5", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg=="], + + "glob-to-regexp": ["glob-to-regexp@0.4.1", "", {}, "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw=="], + + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], + + "has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="], + + "has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="], + + "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + + "hono": ["hono@4.8.2", "", {}, "sha512-hM+1RIn9PK1I6SiTNS6/y7O1mvg88awYLFEuEtoiMtRyT3SD2iu9pSFgbBXT3b1Ua4IwzvSTLvwO0SEhDxCi4w=="], + + "humanize-ms": ["humanize-ms@1.2.1", "", { "dependencies": { "ms": "^2.0.0" } }, "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ=="], + + "immediate": ["immediate@3.0.6", "", {}, "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="], + + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], + + "is-arrayish": ["is-arrayish@0.3.2", "", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="], + + "is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="], + + "is-reference": ["is-reference@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.6" } }, "sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw=="], + + "isarray": ["isarray@1.0.0", "", {}, "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="], + + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], + + "jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="], + + "jiti": ["jiti@2.4.2", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A=="], + + "json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="], + + "jsondiffpatch": ["jsondiffpatch@0.6.0", "", { "dependencies": { "@types/diff-match-patch": "^1.0.36", "chalk": "^5.3.0", "diff-match-patch": "^1.0.5" }, "bin": { "jsondiffpatch": "bin/jsondiffpatch.js" } }, "sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ=="], + + "jszip": ["jszip@3.10.1", "", { "dependencies": { "lie": "~3.3.0", "pako": "~1.0.2", "readable-stream": "~2.3.6", "setimmediate": "^1.0.5" } }, "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g=="], + + "kleur": ["kleur@4.1.5", "", {}, "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ=="], + + "libsodium": ["libsodium@0.7.15", "", {}, "sha512-sZwRknt/tUpE2AwzHq3jEyUU5uvIZHtSssktXq7owd++3CSgn8RGrv6UZJJBpP7+iBghBqe7Z06/2M31rI2NKw=="], + + "libsodium-wrappers": ["libsodium-wrappers@0.7.15", "", { "dependencies": { "libsodium": "^0.7.15" } }, "sha512-E4anqJQwcfiC6+Yrl01C1m8p99wEhLmJSs0VQqST66SbQXXBoaJY0pF4BNjRYa/sOQAxx6lXAaAFIlx+15tXJQ=="], + + "lie": ["lie@3.3.0", "", { "dependencies": { "immediate": "~3.0.5" } }, "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ=="], + + "lightningcss": ["lightningcss@1.30.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.30.1", "lightningcss-darwin-x64": "1.30.1", "lightningcss-freebsd-x64": "1.30.1", "lightningcss-linux-arm-gnueabihf": "1.30.1", "lightningcss-linux-arm64-gnu": "1.30.1", "lightningcss-linux-arm64-musl": "1.30.1", "lightningcss-linux-x64-gnu": "1.30.1", "lightningcss-linux-x64-musl": "1.30.1", "lightningcss-win32-arm64-msvc": "1.30.1", "lightningcss-win32-x64-msvc": "1.30.1" } }, "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg=="], + + "lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.30.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ=="], + + "lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.30.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA=="], + + "lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.30.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig=="], + + "lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.30.1", "", { "os": "linux", "cpu": "arm" }, "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q=="], + + "lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw=="], + + "lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.30.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ=="], + + "lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw=="], + + "lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.30.1", "", { "os": "linux", "cpu": "x64" }, "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ=="], + + "lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.30.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA=="], + + "lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.30.1", "", { "os": "win32", "cpu": "x64" }, "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg=="], + + "locate-character": ["locate-character@3.0.0", "", {}, "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA=="], + + "lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + + "magic-string": ["magic-string@0.30.17", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" } }, "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA=="], + + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], + + "mime": ["mime@3.0.0", "", { "bin": { "mime": "cli.js" } }, "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A=="], + + "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + + "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + + "miniflare": ["miniflare@4.20250617.3", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "^5.28.5", "workerd": "1.20250617.0", "ws": "8.18.0", "youch": "3.3.4", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-j+LZycT11UdlVeNdaqD0XdNnYnqAL+wXmboz+tNPFgTq6zhD489Ujj3BfSDyEHDCA9UFBLbkc5ByGWBh+pYZ5Q=="], + + "minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], + + "minipass": ["minipass@7.1.2", "", {}, "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw=="], + + "minizlib": ["minizlib@3.0.2", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA=="], + + "mkdirp": ["mkdirp@3.0.1", "", { "bin": { "mkdirp": "dist/cjs/src/bin.js" } }, "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg=="], + + "mnemonist": ["mnemonist@0.38.3", "", { "dependencies": { "obliterator": "^1.6.1" } }, "sha512-2K9QYubXx/NAjv4VLq1d1Ly8pWNC5L3BrixtdkyTegXWJIqY+zLNDhhX/A+ZwWt70tB1S8H4BE8FLYEFyNoOBw=="], + + "mri": ["mri@1.2.0", "", {}, "sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA=="], + + "mrmime": ["mrmime@2.0.1", "", {}, "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "mustache": ["mustache@4.2.0", "", { "bin": { "mustache": "bin/mustache" } }, "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ=="], + + "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + + "node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="], + + "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], + + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], + + "obliterator": ["obliterator@1.6.1", "", {}, "sha512-9WXswnqINnnhOG/5SLimUlzuU1hFJUc8zkwyD59Sd+dPOMf05PmnYG/d6Q7HZ+KmgkZJa1PxRso6QdM3sTNHig=="], + + "ohash": ["ohash@2.0.11", "", {}, "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ=="], + + "package-json-from-dist": ["package-json-from-dist@1.0.1", "", {}, "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw=="], + + "pako": ["pako@1.0.11", "", {}, "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="], + + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], + + "path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], + + "path-to-regexp": ["path-to-regexp@6.3.0", "", {}, "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ=="], + + "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="], + + "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], + + "prettier": ["prettier@3.5.3", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw=="], + + "prettier-plugin-tailwindcss": ["prettier-plugin-tailwindcss@0.6.13", "", { "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", "@zackad/prettier-plugin-twig": "*", "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", "prettier-plugin-import-sort": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", "prettier-plugin-multiline-arrays": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", "prettier-plugin-style-order": "*", "prettier-plugin-svelte": "*" }, "optionalPeers": ["@ianvs/prettier-plugin-sort-imports", "@prettier/plugin-pug", "@shopify/prettier-plugin-liquid", "@trivago/prettier-plugin-sort-imports", "@zackad/prettier-plugin-twig", "prettier-plugin-astro", "prettier-plugin-css-order", "prettier-plugin-import-sort", "prettier-plugin-jsdoc", "prettier-plugin-marko", "prettier-plugin-multiline-arrays", "prettier-plugin-organize-attributes", "prettier-plugin-organize-imports", "prettier-plugin-sort-imports", "prettier-plugin-style-order", "prettier-plugin-svelte"] }, "sha512-uQ0asli1+ic8xrrSmIOaElDu0FacR4x69GynTh2oZjFY10JUt6EEumTQl5tB4fMeD6I1naKd+4rXQQ7esT2i1g=="], + + "printable-characters": ["printable-characters@1.0.42", "", {}, "sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ=="], + + "process-nextick-args": ["process-nextick-args@2.0.1", "", {}, "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="], + + "qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="], + + "react": ["react@19.1.0", "", {}, "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg=="], + + "readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], + + "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], + + "regexparam": ["regexparam@3.0.0", "", {}, "sha512-RSYAtP31mvYLkAHrOlh25pCNQ5hWnT106VukGaaFfuJrZFkGRX5GhUAdPqpSDXxOhA2c4akmRuplv1mRqnBn6Q=="], + + "rollup": ["rollup@4.44.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.44.0", "@rollup/rollup-android-arm64": "4.44.0", "@rollup/rollup-darwin-arm64": "4.44.0", "@rollup/rollup-darwin-x64": "4.44.0", "@rollup/rollup-freebsd-arm64": "4.44.0", "@rollup/rollup-freebsd-x64": "4.44.0", "@rollup/rollup-linux-arm-gnueabihf": "4.44.0", "@rollup/rollup-linux-arm-musleabihf": "4.44.0", "@rollup/rollup-linux-arm64-gnu": "4.44.0", "@rollup/rollup-linux-arm64-musl": "4.44.0", "@rollup/rollup-linux-loongarch64-gnu": "4.44.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.44.0", "@rollup/rollup-linux-riscv64-gnu": "4.44.0", "@rollup/rollup-linux-riscv64-musl": "4.44.0", "@rollup/rollup-linux-s390x-gnu": "4.44.0", "@rollup/rollup-linux-x64-gnu": "4.44.0", "@rollup/rollup-linux-x64-musl": "4.44.0", "@rollup/rollup-win32-arm64-msvc": "4.44.0", "@rollup/rollup-win32-ia32-msvc": "4.44.0", "@rollup/rollup-win32-x64-msvc": "4.44.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-qHcdEzLCiktQIfwBq420pn2dP+30uzqYxv9ETm91wdt2R9AFcWfjNAmje4NWlnCIQ5RMTzVf0ZyisOKqHR6RwA=="], + + "sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="], + + "safe-buffer": ["safe-buffer@5.1.2", "", {}, "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="], + + "secure-json-parse": ["secure-json-parse@2.7.0", "", {}, "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw=="], + + "semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="], + + "set-cookie-parser": ["set-cookie-parser@2.7.1", "", {}, "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ=="], + + "setimmediate": ["setimmediate@1.0.5", "", {}, "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="], + + "sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], + + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], + + "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + + "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], + + "side-channel-list": ["side-channel-list@1.0.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" } }, "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA=="], + + "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], + + "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + + "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + + "simple-swizzle": ["simple-swizzle@0.2.2", "", { "dependencies": { "is-arrayish": "^0.3.1" } }, "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg=="], + + "sirv": ["sirv@3.0.1", "", { "dependencies": { "@polka/url": "^1.0.0-next.24", "mrmime": "^2.0.0", "totalist": "^3.0.0" } }, "sha512-FoqMu0NCGBLCcAkS1qA+XJIQTR6/JHfQXl+uGteNCQ76T91DMUjPa9xfmeqMY3z80nLSg9yQmNjK0Px6RWsH/A=="], + + "source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], + + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + + "stacktracey": ["stacktracey@2.1.8", "", { "dependencies": { "as-table": "^1.0.36", "get-source": "^2.0.12" } }, "sha512-Kpij9riA+UNg7TnphqjH7/CzctQ/owJGNbFkfEeve4Z4uxT5+JapVLFXcsurIfN34gnTWZNJ/f7NMG0E8JDzTw=="], + + "stoppable": ["stoppable@1.1.0", "", {}, "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw=="], + + "string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], + + "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "string_decoder": ["string_decoder@1.1.1", "", { "dependencies": { "safe-buffer": "~5.1.0" } }, "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="], + + "strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], + + "strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "stripe": ["stripe@17.7.0", "", { "dependencies": { "@types/node": ">=8.1.0", "qs": "^6.11.0" } }, "sha512-aT2BU9KkizY9SATf14WhhYVv2uOapBWX0OFWF4xvcj1mPaNotlSc2CsxpS4DS46ZueSppmCF5BX1sNYBtwBvfw=="], + + "strnum": ["strnum@1.1.2", "", {}, "sha512-vrN+B7DBIoTTZjnPNewwhx6cBA/H+IS7rfW68n7XxC1y7uoiGQBxaKzqucGUgavX15dJgiGztLJ8vxuEzwqBdA=="], + + "svelte": ["svelte@5.34.7", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", "esrap": "^1.4.8", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-5PEg+QQKce4t1qiOtVUhUS3AQRTtxJyGBTpxLcNWnr0Ve8q4r06bMo0Gv8uhtCPWlztZHoi3Ye7elLhu+PCTMg=="], + + "svelte-check": ["svelte-check@4.2.2", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-1+31EOYZ7NKN0YDMKusav2hhEoA51GD9Ws6o//0SphMT0ve9mBTsTUEX7OmDMadUP3KjNHsSKtJrqdSaD8CrGQ=="], + + "swr": ["swr@2.3.3", "", { "dependencies": { "dequal": "^2.0.3", "use-sync-external-store": "^1.4.0" }, "peerDependencies": { "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-dshNvs3ExOqtZ6kJBaAsabhPdHyeY4P2cKwRCniDVifBMoG/SVI7tfLWqPXriVspf2Rg4tPzXJTnwaihIeFw2A=="], + + "tailwindcss": ["tailwindcss@4.1.10", "", {}, "sha512-P3nr6WkvKV/ONsTzj6Gb57sWPMX29EPNPopo7+FcpkQaNsrNpZ1pv8QmrYI2RqEKD7mlGqLnGovlcYnBK0IqUA=="], + + "tapable": ["tapable@2.2.2", "", {}, "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg=="], + + "tar": ["tar@7.4.3", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.0.1", "mkdirp": "^3.0.1", "yallist": "^5.0.0" } }, "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw=="], + + "throttleit": ["throttleit@2.1.0", "", {}, "sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw=="], + + "tinyglobby": ["tinyglobby@0.2.14", "", { "dependencies": { "fdir": "^6.4.4", "picomatch": "^4.0.2" } }, "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ=="], + + "totalist": ["totalist@3.0.1", "", {}, "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ=="], + + "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "turndown": ["turndown@7.2.0", "", { "dependencies": { "@mixmark-io/domino": "^2.2.0" } }, "sha512-eCZGBN4nNNqM9Owkv9HAtWRYfLA4h909E/WGAWWBpmB275ehNhZyk87/Tpvjbp0jjNl9XwCsbe6bm6CqFsgD+A=="], + + "typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], + + "ufo": ["ufo@1.6.1", "", {}, "sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA=="], + + "undici": ["undici@5.29.0", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg=="], + + "undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + + "unenv": ["unenv@2.0.0-rc.15", "", { "dependencies": { "defu": "^6.1.4", "exsolve": "^1.0.4", "ohash": "^2.0.11", "pathe": "^2.0.3", "ufo": "^1.5.4" } }, "sha512-J/rEIZU8w6FOfLNz/hNKsnY+fFHWnu9MH4yRbSZF3xbbGHovcetXPs7sD+9p8L6CeNC//I9bhRYAOsBt2u7/OA=="], + + "universal-user-agent": ["universal-user-agent@7.0.3", "", {}, "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A=="], + + "use-sync-external-store": ["use-sync-external-store@1.5.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A=="], + + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], + + "uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="], + + "vite": ["vite@6.3.5", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", "picomatch": "^4.0.2", "postcss": "^8.5.3", "rollup": "^4.34.9", "tinyglobby": "^0.2.13" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ=="], + + "vite-plugin-devtools-json": ["vite-plugin-devtools-json@0.2.0", "", { "dependencies": { "uuid": "^11.1.0" }, "peerDependencies": { "vite": "^2.7.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" } }, "sha512-K7PoaWOEJECZ1n3VbhJXsUAX2PsO0xY7KFMM/Leh7tUev0M5zi+lz+vnVVdCK17IOK9Jp9rdzHXc08cnQirGbg=="], + + "vitefu": ["vitefu@1.0.7", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-eRWXLBbJjW3X5z5P5IHcSm2yYbYRPb2kQuc+oqsbAl99WB5kVsPbiiox+cymo8twTzifA6itvhr2CmjnaZZp0Q=="], + + "web-streams-polyfill": ["web-streams-polyfill@4.0.0-beta.3", "", {}, "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug=="], + + "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], + + "whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], + + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], + + "workerd": ["workerd@1.20250617.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20250617.0", "@cloudflare/workerd-darwin-arm64": "1.20250617.0", "@cloudflare/workerd-linux-64": "1.20250617.0", "@cloudflare/workerd-linux-arm64": "1.20250617.0", "@cloudflare/workerd-windows-64": "1.20250617.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-Uv6p0PYUHp/W/aWfUPLkZVAoAjapisM27JJlwcX9wCPTfCfnuegGOxFMvvlYpmNaX4YCwEdLCwuNn3xkpSkuZw=="], + + "worktop": ["worktop@0.8.0-next.18", "", { "dependencies": { "mrmime": "^2.0.0", "regexparam": "^3.0.0" } }, "sha512-+TvsA6VAVoMC3XDKR5MoC/qlLqDixEfOBysDEKnPIPou/NvoPWCAuXHXMsswwlvmEuvX56lQjvELLyLuzTKvRw=="], + + "wrangler": ["wrangler@4.20.5", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.3.3", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "miniflare": "4.20250617.3", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.17", "workerd": "1.20250617.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20250617.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-tmiyt2vBHszhdzJEDbCpFLU2RiV7/QzvGMV07Zaz4ptqiU2h/lTojyNJAugPpSFNiOuY+k0g3ENNTDQqoUkMFA=="], + + "wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], + + "wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + + "ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="], + + "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], + + "yaml": ["yaml@2.8.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ=="], + + "youch": ["youch@3.3.4", "", { "dependencies": { "cookie": "^0.7.1", "mustache": "^4.2.0", "stacktracey": "^2.1.8" } }, "sha512-UeVBXie8cA35DS6+nBkls68xaBBXCye0CNznrhszZjTbRVnJKQuNsyLKBTTL4ln1o1rh2PKtv35twV7irj5SEg=="], + + "zimmerframe": ["zimmerframe@1.1.2", "", {}, "sha512-rAbqEGa8ovJy4pyBxZM70hg4pE6gDgaQ0Sl9M3enG3I0d6H4XSAM3GeNGLKnsBpuijUow064sf7ww1nutC5/3w=="], + + "zod": ["zod@3.25.67", "", {}, "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw=="], + + "zod-to-json-schema": ["zod-to-json-schema@3.24.5", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g=="], + + "@aws-crypto/sha1-browser/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], + + "@aws-crypto/sha256-browser/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], + + "@aws-crypto/util/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], + + "@aws-sdk/client-dynamodb/uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="], + + "@aws-sdk/client-s3/uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="], + + "@aws-sdk/client-sagemaker/uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="], + + "@aws-sdk/client-ssm/uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="], + + "@cspotcode/source-map-support/@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.9", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", "@jridgewell/sourcemap-codec": "^1.4.10" } }, "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ=="], + + "@octokit/plugin-paginate-rest/@octokit/types": ["@octokit/types@13.10.0", "", { "dependencies": { "@octokit/openapi-types": "^24.2.0" } }, "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA=="], + + "@octokit/plugin-rest-endpoint-methods/@octokit/types": ["@octokit/types@13.10.0", "", { "dependencies": { "@octokit/openapi-types": "^24.2.0" } }, "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA=="], + + "@smithy/middleware-retry/uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.4.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.2", "tslib": "^2.4.0" }, "bundled": true }, "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.4.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ=="], + + "@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA=="], + + "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.11", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.9.0" }, "bundled": true }, "sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA=="], + + "@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.9.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw=="], + + "@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "@types/node-fetch/@types/node": ["@types/node@24.0.3", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-R4I/kzCYAdRLzfiCabn9hxWfbuHS573x+r0dJMkkzThEa7pbrcDWK+9zu3e7aBOouf+rQAciqPFMnxwr0aWgKg=="], + + "miniflare/acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="], + + "miniflare/zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="], + + "string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "strip-ansi-cjs/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "stripe/@types/node": ["@types/node@24.0.3", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-R4I/kzCYAdRLzfiCabn9hxWfbuHS573x+r0dJMkkzThEa7pbrcDWK+9zu3e7aBOouf+rQAciqPFMnxwr0aWgKg=="], + + "wrangler/esbuild": ["esbuild@0.25.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.4", "@esbuild/android-arm": "0.25.4", "@esbuild/android-arm64": "0.25.4", "@esbuild/android-x64": "0.25.4", "@esbuild/darwin-arm64": "0.25.4", "@esbuild/darwin-x64": "0.25.4", "@esbuild/freebsd-arm64": "0.25.4", "@esbuild/freebsd-x64": "0.25.4", "@esbuild/linux-arm": "0.25.4", "@esbuild/linux-arm64": "0.25.4", "@esbuild/linux-ia32": "0.25.4", "@esbuild/linux-loong64": "0.25.4", "@esbuild/linux-mips64el": "0.25.4", "@esbuild/linux-ppc64": "0.25.4", "@esbuild/linux-riscv64": "0.25.4", "@esbuild/linux-s390x": "0.25.4", "@esbuild/linux-x64": "0.25.4", "@esbuild/netbsd-arm64": "0.25.4", "@esbuild/netbsd-x64": "0.25.4", "@esbuild/openbsd-arm64": "0.25.4", "@esbuild/openbsd-x64": "0.25.4", "@esbuild/sunos-x64": "0.25.4", "@esbuild/win32-arm64": "0.25.4", "@esbuild/win32-ia32": "0.25.4", "@esbuild/win32-x64": "0.25.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q=="], + + "wrangler/unenv": ["unenv@2.0.0-rc.17", "", { "dependencies": { "defu": "^6.1.4", "exsolve": "^1.0.4", "ohash": "^2.0.11", "pathe": "^2.0.3", "ufo": "^1.6.1" } }, "sha512-B06u0wXkEd+o5gOCMl/ZHl5cfpYbDZKAT+HWTL+Hws6jWu7dCiqBBXXXzMFcFVJb8D4ytAnYmxJA83uwOQRSsg=="], + + "wrap-ansi-cjs/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], + + "wrap-ansi-cjs/string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + + "wrap-ansi-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + + "youch/cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], + + "@aws-crypto/sha1-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], + + "@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], + + "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], + + "@octokit/plugin-paginate-rest/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="], + + "@octokit/plugin-rest-endpoint-methods/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="], + + "@types/node-fetch/@types/node/undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="], + + "string-width-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "stripe/@types/node/undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="], + + "wrangler/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.4", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q=="], + + "wrangler/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.4", "", { "os": "android", "cpu": "arm" }, "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ=="], + + "wrangler/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.4", "", { "os": "android", "cpu": "arm64" }, "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A=="], + + "wrangler/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.25.4", "", { "os": "android", "cpu": "x64" }, "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ=="], + + "wrangler/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g=="], + + "wrangler/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A=="], + + "wrangler/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.4", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ=="], + + "wrangler/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ=="], + + "wrangler/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.4", "", { "os": "linux", "cpu": "arm" }, "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ=="], + + "wrangler/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ=="], + + "wrangler/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.4", "", { "os": "linux", "cpu": "ia32" }, "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ=="], + + "wrangler/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.4", "", { "os": "linux", "cpu": "none" }, "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA=="], + + "wrangler/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.4", "", { "os": "linux", "cpu": "none" }, "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg=="], + + "wrangler/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag=="], + + "wrangler/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.4", "", { "os": "linux", "cpu": "none" }, "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA=="], + + "wrangler/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g=="], + + "wrangler/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.4", "", { "os": "linux", "cpu": "x64" }, "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA=="], + + "wrangler/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.4", "", { "os": "none", "cpu": "arm64" }, "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ=="], + + "wrangler/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.4", "", { "os": "none", "cpu": "x64" }, "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw=="], + + "wrangler/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.4", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A=="], + + "wrangler/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.4", "", { "os": "openbsd", "cpu": "x64" }, "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw=="], + + "wrangler/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.4", "", { "os": "sunos", "cpu": "x64" }, "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q=="], + + "wrangler/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ=="], + + "wrangler/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg=="], + + "wrangler/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.4", "", { "os": "win32", "cpu": "x64" }, "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ=="], + + "wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + + "wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], + + "@aws-crypto/sha1-browser/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], + + "@aws-crypto/sha256-browser/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], + + "@aws-crypto/util/@smithy/util-utf8/@smithy/util-buffer-from/@smithy/is-array-buffer": ["@smithy/is-array-buffer@2.2.0", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA=="], + } +} diff --git a/alchemy/templates/sveltekit/package.json b/alchemy/templates/sveltekit/package.json new file mode 100644 index 000000000..8b04dbb62 --- /dev/null +++ b/alchemy/templates/sveltekit/package.json @@ -0,0 +1,33 @@ +{ + "type": "module", + "name": "@alchemy.run/sveltekit-template", + "private": true, + "version": "0.0.1", + "scripts": { + "build": "vite build", + "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", + "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", + "deploy": "bun --env-file=./.env ./alchemy.run.ts", + "destroy": "bun --env-file=./.env ./alchemy.run.ts --destroy", + "dev": "vite dev", + "prepare": "svelte-kit sync || echo ''", + "preview": "vite preview" + }, + "devDependencies": { + "alchemy": "workspace:*", + "@cloudflare/workers-types": "^4.20250620.0", + "miniflare": "^4.20250617.3", + "@sveltejs/adapter-auto": "^6.0.0", + "@sveltejs/adapter-cloudflare": "^7.0.4", + "@sveltejs/kit": "^2.16.0", + "@sveltejs/vite-plugin-svelte": "^5.1.0", + "@tailwindcss/vite": "^4.0.0", + "prettier-plugin-tailwindcss": "^0.6.11", + "svelte": "^5.0.0", + "svelte-check": "^4.0.0", + "tailwindcss": "^4.0.0", + "typescript": "^5.8.3", + "vite": "^6.2.6", + "vite-plugin-devtools-json": "^0.2.0" + } +} diff --git a/alchemy/templates/sveltekit/src/app.css b/alchemy/templates/sveltekit/src/app.css new file mode 100644 index 000000000..f1d8c73cd --- /dev/null +++ b/alchemy/templates/sveltekit/src/app.css @@ -0,0 +1 @@ +@import "tailwindcss"; diff --git a/alchemy/templates/sveltekit/src/app.d.ts b/alchemy/templates/sveltekit/src/app.d.ts new file mode 100644 index 000000000..da08e6da5 --- /dev/null +++ b/alchemy/templates/sveltekit/src/app.d.ts @@ -0,0 +1,13 @@ +// See https://svelte.dev/docs/kit/types#app.d.ts +// for information about these interfaces +declare global { + namespace App { + // interface Error {} + // interface Locals {} + // interface PageData {} + // interface PageState {} + // interface Platform {} + } +} + +export {}; diff --git a/alchemy/templates/sveltekit/src/app.html b/alchemy/templates/sveltekit/src/app.html new file mode 100644 index 000000000..77a5ff52c --- /dev/null +++ b/alchemy/templates/sveltekit/src/app.html @@ -0,0 +1,12 @@ + + + + + + + %sveltekit.head% + + +
%sveltekit.body%
+ + diff --git a/alchemy/templates/sveltekit/src/routes/+layout.svelte b/alchemy/templates/sveltekit/src/routes/+layout.svelte new file mode 100644 index 000000000..3153e95d8 --- /dev/null +++ b/alchemy/templates/sveltekit/src/routes/+layout.svelte @@ -0,0 +1,7 @@ + + +{@render children()} diff --git a/alchemy/templates/sveltekit/src/routes/+page.svelte b/alchemy/templates/sveltekit/src/routes/+page.svelte new file mode 100644 index 000000000..cc88df0ea --- /dev/null +++ b/alchemy/templates/sveltekit/src/routes/+page.svelte @@ -0,0 +1,2 @@ +

Welcome to SvelteKit

+

Visit svelte.dev/docs/kit to read the documentation

diff --git a/alchemy/templates/sveltekit/static/favicon.png b/alchemy/templates/sveltekit/static/favicon.png new file mode 100644 index 000000000..825b9e65a Binary files /dev/null and b/alchemy/templates/sveltekit/static/favicon.png differ diff --git a/alchemy/templates/sveltekit/svelte.config.js b/alchemy/templates/sveltekit/svelte.config.js new file mode 100644 index 000000000..f6dab0f67 --- /dev/null +++ b/alchemy/templates/sveltekit/svelte.config.js @@ -0,0 +1,12 @@ +import adapter from '@sveltejs/adapter-cloudflare'; +import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; + +/** @type {import('@sveltejs/kit').Config} */ +const config = { + preprocess: vitePreprocess(), + kit: { + adapter: adapter() + } +}; + +export default config; diff --git a/alchemy/templates/sveltekit/tsconfig.json b/alchemy/templates/sveltekit/tsconfig.json new file mode 100644 index 000000000..d470f39b4 --- /dev/null +++ b/alchemy/templates/sveltekit/tsconfig.json @@ -0,0 +1,27 @@ +{ + "extends": "./.svelte-kit/tsconfig.json", + "compilerOptions": { + "allowJs": true, + "checkJs": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "sourceMap": true, + "strict": true, + "moduleResolution": "bundler", + "types": [ + "@cloudflare/workers-types", + "./types/env.d.ts" + ] + }, + "include": [ + "alchemy.run.ts", + "types/**/*.ts" + ] + // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias + // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files + // + // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes + // from the referenced tsconfig.json - TypeScript does not merge them in +} diff --git a/alchemy/templates/sveltekit/types/env.d.ts b/alchemy/templates/sveltekit/types/env.d.ts new file mode 100644 index 000000000..f371005c3 --- /dev/null +++ b/alchemy/templates/sveltekit/types/env.d.ts @@ -0,0 +1,16 @@ +// This file infers types for the cloudflare:workers environment from your Alchemy Worker. +// @see https://alchemy.run/docs/concepts/bindings.html#type-safe-bindings + +import type { worker } from "../alchemy.run.ts"; + +export type CloudflareEnv = typeof worker.Env; + +declare global { + type Env = CloudflareEnv; +} + +declare module "cloudflare:workers" { + namespace Cloudflare { + export interface Env extends CloudflareEnv {} + } +} diff --git a/alchemy/templates/sveltekit/vite.config.ts b/alchemy/templates/sveltekit/vite.config.ts new file mode 100644 index 000000000..fce9e8704 --- /dev/null +++ b/alchemy/templates/sveltekit/vite.config.ts @@ -0,0 +1,6 @@ +import { sveltekit } from '@sveltejs/kit/vite'; +import { defineConfig } from 'vite'; + +export default defineConfig({ + plugins: [sveltekit()], +}); diff --git a/alchemy/templates/sveltekit/wrangler.jsonc b/alchemy/templates/sveltekit/wrangler.jsonc new file mode 100644 index 000000000..74736e18e --- /dev/null +++ b/alchemy/templates/sveltekit/wrangler.jsonc @@ -0,0 +1,7 @@ +{ + "name": "website", + "main": ".svelte-kit/cloudflare/_worker.js", + "compatibility_date": "2025-04-20", + "compatibility_flags": ["nodejs_compat"], + "assets": { "binding": "ASSETS", "directory": ".svelte-kit/cloudflare" } +} diff --git a/alchemy/templates/tanstack-start/.gitignore b/alchemy/templates/tanstack-start/.gitignore new file mode 100644 index 000000000..55f2a4601 --- /dev/null +++ b/alchemy/templates/tanstack-start/.gitignore @@ -0,0 +1,22 @@ +node_modules +package-lock.json +yarn.lock + +.DS_Store +.cache +.env +.vercel +.output +.nitro +/build/ +/api/ +/server/build +/public/build# Sentry Config File +.env.sentry-build-plugin +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ +.tanstack +.alchemy/ +.env \ No newline at end of file diff --git a/alchemy/templates/tanstack-start/.vscode/settings.json b/alchemy/templates/tanstack-start/.vscode/settings.json new file mode 100644 index 000000000..00b5278e5 --- /dev/null +++ b/alchemy/templates/tanstack-start/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "files.watcherExclude": { + "**/routeTree.gen.ts": true + }, + "search.exclude": { + "**/routeTree.gen.ts": true + }, + "files.readonlyInclude": { + "**/routeTree.gen.ts": true + } +} diff --git a/alchemy/templates/tanstack-start/README.md b/alchemy/templates/tanstack-start/README.md new file mode 100644 index 000000000..90cba4aac --- /dev/null +++ b/alchemy/templates/tanstack-start/README.md @@ -0,0 +1,72 @@ +# Welcome to TanStack.com! + +This site is built with TanStack Router! + +- [TanStack Router Docs](https://tanstack.com/router) + +It's deployed automagically with Netlify! + +- [Netlify](https://netlify.com/) + +## Development + +From your terminal: + +```sh +pnpm install +pnpm dev +``` + +This starts your app in development mode, rebuilding assets on file changes. + +## Editing and previewing the docs of TanStack projects locally + +The documentations for all TanStack projects except for `React Charts` are hosted on [https://tanstack.com](https://tanstack.com), powered by this TanStack Router app. +In production, the markdown doc pages are fetched from the GitHub repos of the projects, but in development they are read from the local file system. + +Follow these steps if you want to edit the doc pages of a project (in these steps we'll assume it's [`TanStack/form`](https://github.com/tanstack/form)) and preview them locally : + +1. Create a new directory called `tanstack`. + +```sh +mkdir tanstack +``` + +2. Enter the directory and clone this repo and the repo of the project there. + +```sh +cd tanstack +git clone git@github.com:TanStack/tanstack.com.git +git clone git@github.com:TanStack/form.git +``` + +> [!NOTE] +> Your `tanstack` directory should look like this: +> +> ``` +> tanstack/ +> | +> +-- form/ +> | +> +-- tanstack.com/ +> ``` + +> [!WARNING] +> Make sure the name of the directory in your local file system matches the name of the project's repo. For example, `tanstack/form` must be cloned into `form` (this is the default) instead of `some-other-name`, because that way, the doc pages won't be found. + +3. Enter the `tanstack/tanstack.com` directory, install the dependencies and run the app in dev mode: + +```sh +cd tanstack.com +pnpm i +# The app will run on https://localhost:3000 by default +pnpm dev +``` + +4. Now you can visit http://localhost:3000/form/latest/docs/overview in the browser and see the changes you make in `tanstack/form/docs`. + +> [!NOTE] +> The updated pages need to be manually reloaded in the browser. + +> [!WARNING] +> You will need to update the `docs/config.json` file (in the project's repo) if you add a new doc page! diff --git a/alchemy/templates/tanstack-start/_env b/alchemy/templates/tanstack-start/_env new file mode 100644 index 000000000..bb4cbbe94 --- /dev/null +++ b/alchemy/templates/tanstack-start/_env @@ -0,0 +1 @@ +ALCHEMY_PASSWORD=change-me \ No newline at end of file diff --git a/alchemy/templates/tanstack-start/_env.example b/alchemy/templates/tanstack-start/_env.example new file mode 100644 index 000000000..bb4cbbe94 --- /dev/null +++ b/alchemy/templates/tanstack-start/_env.example @@ -0,0 +1 @@ +ALCHEMY_PASSWORD=change-me \ No newline at end of file diff --git a/alchemy/templates/tanstack-start/_gitignore b/alchemy/templates/tanstack-start/_gitignore new file mode 100644 index 000000000..55f2a4601 --- /dev/null +++ b/alchemy/templates/tanstack-start/_gitignore @@ -0,0 +1,22 @@ +node_modules +package-lock.json +yarn.lock + +.DS_Store +.cache +.env +.vercel +.output +.nitro +/build/ +/api/ +/server/build +/public/build# Sentry Config File +.env.sentry-build-plugin +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ +.tanstack +.alchemy/ +.env \ No newline at end of file diff --git a/alchemy/templates/tanstack-start/_prettierignore b/alchemy/templates/tanstack-start/_prettierignore new file mode 100644 index 000000000..2be5eaa6e --- /dev/null +++ b/alchemy/templates/tanstack-start/_prettierignore @@ -0,0 +1,4 @@ +**/build +**/public +pnpm-lock.yaml +routeTree.gen.ts \ No newline at end of file diff --git a/alchemy/templates/tanstack-start/alchemy.run.ts b/alchemy/templates/tanstack-start/alchemy.run.ts new file mode 100644 index 000000000..68fec9b4d --- /dev/null +++ b/alchemy/templates/tanstack-start/alchemy.run.ts @@ -0,0 +1,16 @@ +/// + +import alchemy from "alchemy"; +import { TanStackStart } from "alchemy/cloudflare"; + +const app = await alchemy("my-alchemy-app"); + +export const worker = await TanStackStart("website", { + command: "bun run build", +}); + +console.log({ + url: worker.url, +}); + +await app.finalize(); diff --git a/alchemy/templates/tanstack-start/package.json b/alchemy/templates/tanstack-start/package.json new file mode 100644 index 000000000..298462da0 --- /dev/null +++ b/alchemy/templates/tanstack-start/package.json @@ -0,0 +1,37 @@ +{ + "type": "module", + "name": "@alchemy.run/tanstack-start-template", + "private": true, + "sideEffects": false, + "scripts": { + "build": "vite build", + "deploy": "bun --env-file=./.env ./alchemy.run.ts", + "destroy": "bun --env-file=./.env ./alchemy.run.ts --destroy", + "dev": "vite dev", + "start": "node .output/server/index.mjs" + }, + "dependencies": { + "@tanstack/react-router": "^1.121.27", + "@tanstack/react-router-devtools": "^1.121.27", + "@tanstack/react-start": "^1.121.32", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "tailwind-merge": "3", + "zod": "^3.24.2" + }, + "devDependencies": { + "alchemy": "workspace:*", + "@cloudflare/workers-types": "^4.20250620.0", + "@tailwindcss/vite": "^4.1.10", + "@types/node": "^22.5.4", + "@types/react": "^19.0.8", + "@types/react-dom": "^19.0.3", + "autoprefixer": "^10.4.20", + "miniflare": "^4.20250617.3", + "postcss": "^8.5.6", + "tailwindcss": "4", + "typescript": "^5.8.3", + "vite": "^6.3.5", + "vite-tsconfig-paths": "^5.1.4" + } +} diff --git a/alchemy/templates/tanstack-start/public/android-chrome-192x192.png b/alchemy/templates/tanstack-start/public/android-chrome-192x192.png new file mode 100644 index 000000000..09c8324f8 Binary files /dev/null and b/alchemy/templates/tanstack-start/public/android-chrome-192x192.png differ diff --git a/alchemy/templates/tanstack-start/public/android-chrome-512x512.png b/alchemy/templates/tanstack-start/public/android-chrome-512x512.png new file mode 100644 index 000000000..11d626ea3 Binary files /dev/null and b/alchemy/templates/tanstack-start/public/android-chrome-512x512.png differ diff --git a/alchemy/templates/tanstack-start/public/apple-touch-icon.png b/alchemy/templates/tanstack-start/public/apple-touch-icon.png new file mode 100644 index 000000000..5a9423cc0 Binary files /dev/null and b/alchemy/templates/tanstack-start/public/apple-touch-icon.png differ diff --git a/alchemy/templates/tanstack-start/public/favicon-16x16.png b/alchemy/templates/tanstack-start/public/favicon-16x16.png new file mode 100644 index 000000000..e3389b004 Binary files /dev/null and b/alchemy/templates/tanstack-start/public/favicon-16x16.png differ diff --git a/alchemy/templates/tanstack-start/public/favicon-32x32.png b/alchemy/templates/tanstack-start/public/favicon-32x32.png new file mode 100644 index 000000000..900c77d44 Binary files /dev/null and b/alchemy/templates/tanstack-start/public/favicon-32x32.png differ diff --git a/alchemy/templates/tanstack-start/public/favicon.ico b/alchemy/templates/tanstack-start/public/favicon.ico new file mode 100644 index 000000000..1a1751676 Binary files /dev/null and b/alchemy/templates/tanstack-start/public/favicon.ico differ diff --git a/alchemy/templates/tanstack-start/public/favicon.png b/alchemy/templates/tanstack-start/public/favicon.png new file mode 100644 index 000000000..1e77bc060 Binary files /dev/null and b/alchemy/templates/tanstack-start/public/favicon.png differ diff --git a/alchemy/templates/tanstack-start/public/site.webmanifest b/alchemy/templates/tanstack-start/public/site.webmanifest new file mode 100644 index 000000000..fa99de77d --- /dev/null +++ b/alchemy/templates/tanstack-start/public/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "", + "short_name": "", + "icons": [ + { + "src": "/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/alchemy/templates/tanstack-start/src/components/DefaultCatchBoundary.tsx b/alchemy/templates/tanstack-start/src/components/DefaultCatchBoundary.tsx new file mode 100644 index 000000000..f750e7bd2 --- /dev/null +++ b/alchemy/templates/tanstack-start/src/components/DefaultCatchBoundary.tsx @@ -0,0 +1,53 @@ +import { + ErrorComponent, + Link, + rootRouteId, + useMatch, + useRouter, +} from '@tanstack/react-router' +import type { ErrorComponentProps } from '@tanstack/react-router' + +export function DefaultCatchBoundary({ error }: ErrorComponentProps) { + const router = useRouter() + const isRoot = useMatch({ + strict: false, + select: (state) => state.id === rootRouteId, + }) + + console.error('DefaultCatchBoundary Error:', error) + + return ( +
+ +
+ + {isRoot ? ( + + Home + + ) : ( + { + e.preventDefault() + window.history.back() + }} + > + Go Back + + )} +
+
+ ) +} diff --git a/alchemy/templates/tanstack-start/src/components/NotFound.tsx b/alchemy/templates/tanstack-start/src/components/NotFound.tsx new file mode 100644 index 000000000..7b54fa568 --- /dev/null +++ b/alchemy/templates/tanstack-start/src/components/NotFound.tsx @@ -0,0 +1,25 @@ +import { Link } from '@tanstack/react-router' + +export function NotFound({ children }: { children?: any }) { + return ( +
+
+ {children ||

The page you are looking for does not exist.

} +
+

+ + + Start Over + +

+
+ ) +} diff --git a/alchemy/templates/tanstack-start/src/components/PostError.tsx b/alchemy/templates/tanstack-start/src/components/PostError.tsx new file mode 100644 index 000000000..3573f4696 --- /dev/null +++ b/alchemy/templates/tanstack-start/src/components/PostError.tsx @@ -0,0 +1,5 @@ +import { ErrorComponent, ErrorComponentProps } from '@tanstack/react-router' + +export function PostErrorComponent({ error }: ErrorComponentProps) { + return +} diff --git a/alchemy/templates/tanstack-start/src/components/UserError.tsx b/alchemy/templates/tanstack-start/src/components/UserError.tsx new file mode 100644 index 000000000..ebea2f621 --- /dev/null +++ b/alchemy/templates/tanstack-start/src/components/UserError.tsx @@ -0,0 +1,5 @@ +import { ErrorComponent, ErrorComponentProps } from '@tanstack/react-router' + +export function UserErrorComponent({ error }: ErrorComponentProps) { + return +} diff --git a/alchemy/templates/tanstack-start/src/router.tsx b/alchemy/templates/tanstack-start/src/router.tsx new file mode 100644 index 000000000..c76eb0210 --- /dev/null +++ b/alchemy/templates/tanstack-start/src/router.tsx @@ -0,0 +1,22 @@ +import { createRouter as createTanStackRouter } from '@tanstack/react-router' +import { routeTree } from './routeTree.gen' +import { DefaultCatchBoundary } from './components/DefaultCatchBoundary' +import { NotFound } from './components/NotFound' + +export function createRouter() { + const router = createTanStackRouter({ + routeTree, + defaultPreload: 'intent', + defaultErrorComponent: DefaultCatchBoundary, + defaultNotFoundComponent: () => , + scrollRestoration: true, + }) + + return router +} + +declare module '@tanstack/react-router' { + interface Register { + router: ReturnType + } +} diff --git a/alchemy/templates/tanstack-start/src/routes/__root.tsx b/alchemy/templates/tanstack-start/src/routes/__root.tsx new file mode 100644 index 000000000..8e095f4cc --- /dev/null +++ b/alchemy/templates/tanstack-start/src/routes/__root.tsx @@ -0,0 +1,146 @@ +/// +import { + HeadContent, + Link, + Outlet, + Scripts, + createRootRoute, +} from '@tanstack/react-router' +import { TanStackRouterDevtools } from '@tanstack/react-router-devtools' +import * as React from 'react' +import { DefaultCatchBoundary } from '~/components/DefaultCatchBoundary' +import { NotFound } from '~/components/NotFound' +import appCss from '~/styles/app.css?url' +import { seo } from '~/utils/seo' + +export const Route = createRootRoute({ + head: () => ({ + meta: [ + { + charSet: 'utf-8', + }, + { + name: 'viewport', + content: 'width=device-width, initial-scale=1', + }, + ...seo({ + title: + 'TanStack Start | Type-Safe, Client-First, Full-Stack React Framework', + description: `TanStack Start is a type-safe, client-first, full-stack React framework. `, + }), + ], + links: [ + { rel: 'stylesheet', href: appCss }, + { + rel: 'apple-touch-icon', + sizes: '180x180', + href: '/apple-touch-icon.png', + }, + { + rel: 'icon', + type: 'image/png', + sizes: '32x32', + href: '/favicon-32x32.png', + }, + { + rel: 'icon', + type: 'image/png', + sizes: '16x16', + href: '/favicon-16x16.png', + }, + { rel: 'manifest', href: '/site.webmanifest', color: '#fffff' }, + { rel: 'icon', href: '/favicon.ico' }, + ], + scripts: [ + { + src: '/customScript.js', + type: 'text/javascript', + }, + ], + }), + errorComponent: (props) => { + return ( + + + + ) + }, + notFoundComponent: () => , + component: RootComponent, +}) + +function RootComponent() { + return ( + + + + ) +} + +function RootDocument({ children }: { children: React.ReactNode }) { + return ( + + + + + +
+ + Home + {' '} + + Posts + {' '} + + Users + {' '} + + Pathless Layout + {' '} + + Deferred + {' '} + + This Route Does Not Exist + +
+
+ {children} + + + + + ) +} diff --git a/alchemy/templates/tanstack-start/src/routes/_pathlessLayout.tsx b/alchemy/templates/tanstack-start/src/routes/_pathlessLayout.tsx new file mode 100644 index 000000000..c3b12442b --- /dev/null +++ b/alchemy/templates/tanstack-start/src/routes/_pathlessLayout.tsx @@ -0,0 +1,16 @@ +import { Outlet, createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/_pathlessLayout')({ + component: LayoutComponent, +}) + +function LayoutComponent() { + return ( +
+
I'm a layout
+
+ +
+
+ ) +} diff --git a/alchemy/templates/tanstack-start/src/routes/_pathlessLayout/_nested-layout.tsx b/alchemy/templates/tanstack-start/src/routes/_pathlessLayout/_nested-layout.tsx new file mode 100644 index 000000000..9a48b73a4 --- /dev/null +++ b/alchemy/templates/tanstack-start/src/routes/_pathlessLayout/_nested-layout.tsx @@ -0,0 +1,34 @@ +import { Link, Outlet, createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/_pathlessLayout/_nested-layout')({ + component: LayoutComponent, +}) + +function LayoutComponent() { + return ( +
+
I'm a nested layout
+
+ + Go to route A + + + Go to route B + +
+
+ +
+
+ ) +} diff --git a/alchemy/templates/tanstack-start/src/routes/_pathlessLayout/_nested-layout/route-a.tsx b/alchemy/templates/tanstack-start/src/routes/_pathlessLayout/_nested-layout/route-a.tsx new file mode 100644 index 000000000..0213f1516 --- /dev/null +++ b/alchemy/templates/tanstack-start/src/routes/_pathlessLayout/_nested-layout/route-a.tsx @@ -0,0 +1,10 @@ +import { createFileRoute } from '@tanstack/react-router' +export const Route = createFileRoute('/_pathlessLayout/_nested-layout/route-a')( + { + component: LayoutAComponent, + }, +) + +function LayoutAComponent() { + return
I'm A!
+} diff --git a/alchemy/templates/tanstack-start/src/routes/_pathlessLayout/_nested-layout/route-b.tsx b/alchemy/templates/tanstack-start/src/routes/_pathlessLayout/_nested-layout/route-b.tsx new file mode 100644 index 000000000..3d909523f --- /dev/null +++ b/alchemy/templates/tanstack-start/src/routes/_pathlessLayout/_nested-layout/route-b.tsx @@ -0,0 +1,10 @@ +import { createFileRoute } from '@tanstack/react-router' +export const Route = createFileRoute('/_pathlessLayout/_nested-layout/route-b')( + { + component: LayoutBComponent, + }, +) + +function LayoutBComponent() { + return
I'm B!
+} diff --git a/alchemy/templates/tanstack-start/src/routes/api/users.$userId.ts b/alchemy/templates/tanstack-start/src/routes/api/users.$userId.ts new file mode 100644 index 000000000..c5c253993 --- /dev/null +++ b/alchemy/templates/tanstack-start/src/routes/api/users.$userId.ts @@ -0,0 +1,28 @@ +import { createServerFileRoute } from '@tanstack/react-start/server' +import { json } from '@tanstack/react-start' +import type { User } from '~/utils/users' + +export const ServerRoute = createServerFileRoute('/api/users/$userId').methods({ + GET: async ({ params, request }) => { + console.info(`Fetching users by id=${params.userId}... @`, request.url) + try { + const res = await fetch( + 'https://jsonplaceholder.typicode.com/users/' + params.userId, + ) + if (!res.ok) { + throw new Error('Failed to fetch user') + } + + const user = (await res.json()) as User + + return json({ + id: user.id, + name: user.name, + email: user.email, + }) + } catch (e) { + console.error(e) + return json({ error: 'User not found' }, { status: 404 }) + } + }, +}) diff --git a/alchemy/templates/tanstack-start/src/routes/api/users.ts b/alchemy/templates/tanstack-start/src/routes/api/users.ts new file mode 100644 index 000000000..b5a4b7569 --- /dev/null +++ b/alchemy/templates/tanstack-start/src/routes/api/users.ts @@ -0,0 +1,59 @@ +import { createServerFileRoute } from '@tanstack/react-start/server' +import { createMiddleware, json } from '@tanstack/react-start' +import type { User } from '~/utils/users' + +const userLoggerMiddleware = createMiddleware({ type: 'request' }).server( + async ({ next, request }) => { + console.info('In: /users') + const result = await next() + result.response.headers.set('x-users', 'true') + console.info('Out: /users') + return result + }, +) + +const testParentMiddleware = createMiddleware({ type: 'request' }).server( + async ({ next, request }) => { + console.info('In: testParentMiddleware') + const result = await next() + result.response.headers.set('x-test-parent', 'true') + console.info('Out: testParentMiddleware') + return result + }, +) + +const testMiddleware = createMiddleware({ type: 'request' }) + .middleware([testParentMiddleware]) + .server(async ({ next, request }) => { + console.info('In: testMiddleware') + const result = await next() + result.response.headers.set('x-test', 'true') + + // if (Math.random() > 0.5) { + // throw new Response(null, { + // status: 302, + // headers: { Location: 'https://www.google.com' }, + // }) + // } + + console.info('Out: testMiddleware') + return result + }) + +export const ServerRoute = createServerFileRoute('/api/users') + .middleware([testMiddleware, userLoggerMiddleware, testParentMiddleware]) + .methods({ + GET: async ({ request }) => { + console.info('Fetching users... @', request.url) + const res = await fetch('https://jsonplaceholder.typicode.com/users') + if (!res.ok) { + throw new Error('Failed to fetch users') + } + + const data = (await res.json()) as Array + + const list = data.slice(0, 10) + + return json(list.map((u) => ({ id: u.id, name: u.name, email: u.email }))) + }, + }) diff --git a/alchemy/templates/tanstack-start/src/routes/customScript[.]js.ts b/alchemy/templates/tanstack-start/src/routes/customScript[.]js.ts new file mode 100644 index 000000000..92cc40dad --- /dev/null +++ b/alchemy/templates/tanstack-start/src/routes/customScript[.]js.ts @@ -0,0 +1,10 @@ +import { createServerFileRoute } from '@tanstack/react-start/server' +export const ServerRoute = createServerFileRoute('/customScript.js').methods({ + GET: async ({ request }) => { + return new Response('console.log("Hello from customScript.js!")', { + headers: { + 'Content-Type': 'application/javascript', + }, + }) + }, +}) diff --git a/alchemy/templates/tanstack-start/src/routes/deferred.tsx b/alchemy/templates/tanstack-start/src/routes/deferred.tsx new file mode 100644 index 000000000..f3e09d1d4 --- /dev/null +++ b/alchemy/templates/tanstack-start/src/routes/deferred.tsx @@ -0,0 +1,62 @@ +import { Await, createFileRoute } from '@tanstack/react-router' +import { createServerFn } from '@tanstack/react-start' +import { Suspense, useState } from 'react' + +const personServerFn = createServerFn({ method: 'GET' }) + .validator((d: string) => d) + .handler(({ data: name }) => { + return { name, randomNumber: Math.floor(Math.random() * 100) } + }) + +const slowServerFn = createServerFn({ method: 'GET' }) + .validator((d: string) => d) + .handler(async ({ data: name }) => { + await new Promise((r) => setTimeout(r, 1000)) + return { name, randomNumber: Math.floor(Math.random() * 100) } + }) + +export const Route = createFileRoute('/deferred')({ + loader: async () => { + return { + deferredStuff: new Promise((r) => + setTimeout(() => r('Hello deferred!'), 2000), + ), + deferredPerson: slowServerFn({ data: 'Tanner Linsley' }), + person: await personServerFn({ data: 'John Doe' }), + } + }, + component: Deferred, +}) + +function Deferred() { + const [count, setCount] = useState(0) + const { deferredStuff, deferredPerson, person } = Route.useLoaderData() + + return ( +
+
+ {person.name} - {person.randomNumber} +
+ Loading person...
}> + ( +
+ {data.name} - {data.randomNumber} +
+ )} + /> + + Loading stuff...}> +

{data}

} + /> +
+
Count: {count}
+
+ +
+ + ) +} diff --git a/alchemy/templates/tanstack-start/src/routes/index.tsx b/alchemy/templates/tanstack-start/src/routes/index.tsx new file mode 100644 index 000000000..37c8d237b --- /dev/null +++ b/alchemy/templates/tanstack-start/src/routes/index.tsx @@ -0,0 +1,12 @@ +import { createFileRoute } from '@tanstack/react-router' +export const Route = createFileRoute('/')({ + component: Home, +}) + +function Home() { + return ( +
+

Welcome Home!!!

+
+ ) +} diff --git a/alchemy/templates/tanstack-start/src/routes/posts.$postId.tsx b/alchemy/templates/tanstack-start/src/routes/posts.$postId.tsx new file mode 100644 index 000000000..f509f9a4b --- /dev/null +++ b/alchemy/templates/tanstack-start/src/routes/posts.$postId.tsx @@ -0,0 +1,34 @@ +import { Link, createFileRoute } from '@tanstack/react-router' +import { fetchPost } from '../utils/posts' +import { NotFound } from '~/components/NotFound' +import { PostErrorComponent } from '~/components/PostError' + +export const Route = createFileRoute('/posts/$postId')({ + loader: ({ params: { postId } }) => fetchPost({ data: postId }), + errorComponent: PostErrorComponent, + component: PostComponent, + notFoundComponent: () => { + return Post not found + }, +}) + +function PostComponent() { + const post = Route.useLoaderData() + + return ( +
+

{post.title}

+
{post.body}
+ + Deep View + +
+ ) +} diff --git a/alchemy/templates/tanstack-start/src/routes/posts.index.tsx b/alchemy/templates/tanstack-start/src/routes/posts.index.tsx new file mode 100644 index 000000000..c65927456 --- /dev/null +++ b/alchemy/templates/tanstack-start/src/routes/posts.index.tsx @@ -0,0 +1,8 @@ +import { createFileRoute } from '@tanstack/react-router' +export const Route = createFileRoute('/posts/')({ + component: PostsIndexComponent, +}) + +function PostsIndexComponent() { + return
Select a post.
+} diff --git a/alchemy/templates/tanstack-start/src/routes/posts.tsx b/alchemy/templates/tanstack-start/src/routes/posts.tsx new file mode 100644 index 000000000..ae4903245 --- /dev/null +++ b/alchemy/templates/tanstack-start/src/routes/posts.tsx @@ -0,0 +1,38 @@ +import { Link, Outlet, createFileRoute } from '@tanstack/react-router' +import { fetchPosts } from '../utils/posts' + +export const Route = createFileRoute('/posts')({ + loader: async () => fetchPosts(), + component: PostsComponent, +}) + +function PostsComponent() { + const posts = Route.useLoaderData() + + return ( +
+
    + {[...posts, { id: 'i-do-not-exist', title: 'Non-existent Post' }].map( + (post) => { + return ( +
  • + +
    {post.title.substring(0, 20)}
    + +
  • + ) + }, + )} +
+
+ +
+ ) +} diff --git a/alchemy/templates/tanstack-start/src/routes/posts_.$postId.deep.tsx b/alchemy/templates/tanstack-start/src/routes/posts_.$postId.deep.tsx new file mode 100644 index 000000000..29e6c39b5 --- /dev/null +++ b/alchemy/templates/tanstack-start/src/routes/posts_.$postId.deep.tsx @@ -0,0 +1,29 @@ +import { Link, createFileRoute } from '@tanstack/react-router' +import { fetchPost } from '../utils/posts' +import { PostErrorComponent } from '~/components/PostError' + +export const Route = createFileRoute('/posts_/$postId/deep')({ + loader: async ({ params: { postId } }) => + fetchPost({ + data: postId, + }), + errorComponent: PostErrorComponent, + component: PostDeepComponent, +}) + +function PostDeepComponent() { + const post = Route.useLoaderData() + + return ( +
+ + ← All Posts + +

{post.title}

+
{post.body}
+
+ ) +} diff --git a/alchemy/templates/tanstack-start/src/routes/redirect.tsx b/alchemy/templates/tanstack-start/src/routes/redirect.tsx new file mode 100644 index 000000000..fa220b509 --- /dev/null +++ b/alchemy/templates/tanstack-start/src/routes/redirect.tsx @@ -0,0 +1,9 @@ +import { redirect, createFileRoute } from '@tanstack/react-router' + +export const Route = createFileRoute('/redirect')({ + beforeLoad: async () => { + throw redirect({ + to: '/posts', + }) + }, +}) diff --git a/alchemy/templates/tanstack-start/src/routes/users.$userId.tsx b/alchemy/templates/tanstack-start/src/routes/users.$userId.tsx new file mode 100644 index 000000000..e9bf081fa --- /dev/null +++ b/alchemy/templates/tanstack-start/src/routes/users.$userId.tsx @@ -0,0 +1,44 @@ +import { createFileRoute } from '@tanstack/react-router' +import { NotFound } from 'src/components/NotFound' +import { UserErrorComponent } from 'src/components/UserError' + +export const Route = createFileRoute('/users/$userId')({ + loader: async ({ params: { userId } }) => { + try { + const res = await fetch('/api/users/' + userId) + if (!res.ok) { + throw new Error('Unexpected status code') + } + + const data = await res.json() + + return data + } catch { + throw new Error('Failed to fetch user') + } + }, + errorComponent: UserErrorComponent, + component: UserComponent, + notFoundComponent: () => { + return User not found + }, +}) + +function UserComponent() { + const user = Route.useLoaderData() + + return ( +
+

{user.name}

+
{user.email}
+ +
+ ) +} diff --git a/alchemy/templates/tanstack-start/src/routes/users.index.tsx b/alchemy/templates/tanstack-start/src/routes/users.index.tsx new file mode 100644 index 000000000..410d32548 --- /dev/null +++ b/alchemy/templates/tanstack-start/src/routes/users.index.tsx @@ -0,0 +1,18 @@ +import { createFileRoute } from '@tanstack/react-router' +export const Route = createFileRoute('/users/')({ + component: UsersIndexComponent, +}) + +function UsersIndexComponent() { + return ( +
+ Select a user or{' '} + + view as JSON + +
+ ) +} diff --git a/alchemy/templates/tanstack-start/src/routes/users.tsx b/alchemy/templates/tanstack-start/src/routes/users.tsx new file mode 100644 index 000000000..8017e21ef --- /dev/null +++ b/alchemy/templates/tanstack-start/src/routes/users.tsx @@ -0,0 +1,49 @@ +import { Link, Outlet, createFileRoute } from '@tanstack/react-router' +import type { User } from '../utils/users' + +export const Route = createFileRoute('/users')({ + loader: async () => { + const res = await fetch('/api/users') + + if (!res.ok) { + throw new Error('Unexpected status code') + } + + const data = (await res.json()) as Array + + return data + }, + component: UsersComponent, +}) + +function UsersComponent() { + const users = Route.useLoaderData() + + return ( +
+
    + {[ + ...users, + { id: 'i-do-not-exist', name: 'Non-existent User', email: '' }, + ].map((user) => { + return ( +
  • + +
    {user.name}
    + +
  • + ) + })} +
+
+ +
+ ) +} diff --git a/alchemy/templates/tanstack-start/src/styles/app.css b/alchemy/templates/tanstack-start/src/styles/app.css new file mode 100644 index 000000000..eef8f35f4 --- /dev/null +++ b/alchemy/templates/tanstack-start/src/styles/app.css @@ -0,0 +1,12 @@ +@import "tailwindcss"; + +:root { + --border: var(--color-zinc-200); + --popover: var(--color-white); + --popover-foreground: var(--color-zinc-950); +} + +@theme { + --font-sans: var(--font-sans, Inter), ui-sans-serif, system-ui, sans-serif, + "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; +} diff --git a/alchemy/templates/tanstack-start/src/utils/loggingMiddleware.tsx b/alchemy/templates/tanstack-start/src/utils/loggingMiddleware.tsx new file mode 100644 index 000000000..3ea9a0643 --- /dev/null +++ b/alchemy/templates/tanstack-start/src/utils/loggingMiddleware.tsx @@ -0,0 +1,41 @@ +import { createMiddleware } from '@tanstack/react-start' + +const preLogMiddleware = createMiddleware({ type: 'function' }) + .client(async (ctx) => { + const clientTime = new Date() + + return ctx.next({ + context: { + clientTime, + }, + sendContext: { + clientTime, + }, + }) + }) + .server(async (ctx) => { + const serverTime = new Date() + + return ctx.next({ + sendContext: { + serverTime, + durationToServer: + serverTime.getTime() - ctx.context.clientTime.getTime(), + }, + }) + }) + +export const logMiddleware = createMiddleware({ type: 'function' }) + .middleware([preLogMiddleware]) + .client(async (ctx) => { + const res = await ctx.next() + + const now = new Date() + console.log('Client Req/Res:', { + duration: now.getTime() - res.context.clientTime.getTime(), + durationToServer: res.context.durationToServer, + durationFromServer: now.getTime() - res.context.serverTime.getTime(), + }) + + return res + }) diff --git a/alchemy/templates/tanstack-start/src/utils/posts.tsx b/alchemy/templates/tanstack-start/src/utils/posts.tsx new file mode 100644 index 000000000..52877be68 --- /dev/null +++ b/alchemy/templates/tanstack-start/src/utils/posts.tsx @@ -0,0 +1,40 @@ +import { notFound } from '@tanstack/react-router' +import { createServerFn } from '@tanstack/react-start' + +export type PostType = { + id: string + title: string + body: string +} + +export const fetchPost = createServerFn() + .validator((d: string) => d) + .handler(async ({ data }) => { + console.info(`Fetching post with id ${data}...`) + const res = await fetch( + `https://jsonplaceholder.typicode.com/posts/${data}`, + ) + if (!res.ok) { + if (res.status === 404) { + throw notFound() + } + + throw new Error('Failed to fetch post') + } + + const post = (await res.json()) as PostType + + return post + }) + +export const fetchPosts = createServerFn().handler(async () => { + console.info('Fetching posts...') + const res = await fetch('https://jsonplaceholder.typicode.com/posts') + if (!res.ok) { + throw new Error('Failed to fetch posts') + } + + const posts = (await res.json()) as Array + + return posts.slice(0, 10) +}) diff --git a/alchemy/templates/tanstack-start/src/utils/seo.ts b/alchemy/templates/tanstack-start/src/utils/seo.ts new file mode 100644 index 000000000..d18ad84b7 --- /dev/null +++ b/alchemy/templates/tanstack-start/src/utils/seo.ts @@ -0,0 +1,33 @@ +export const seo = ({ + title, + description, + keywords, + image, +}: { + title: string + description?: string + image?: string + keywords?: string +}) => { + const tags = [ + { title }, + { name: 'description', content: description }, + { name: 'keywords', content: keywords }, + { name: 'twitter:title', content: title }, + { name: 'twitter:description', content: description }, + { name: 'twitter:creator', content: '@tannerlinsley' }, + { name: 'twitter:site', content: '@tannerlinsley' }, + { name: 'og:type', content: 'website' }, + { name: 'og:title', content: title }, + { name: 'og:description', content: description }, + ...(image + ? [ + { name: 'twitter:image', content: image }, + { name: 'twitter:card', content: 'summary_large_image' }, + { name: 'og:image', content: image }, + ] + : []), + ] + + return tags +} diff --git a/alchemy/templates/tanstack-start/src/utils/users.tsx b/alchemy/templates/tanstack-start/src/utils/users.tsx new file mode 100644 index 000000000..7ba645b38 --- /dev/null +++ b/alchemy/templates/tanstack-start/src/utils/users.tsx @@ -0,0 +1,5 @@ +export type User = { + id: number + name: string + email: string +} diff --git a/alchemy/templates/tanstack-start/tsconfig.json b/alchemy/templates/tanstack-start/tsconfig.json new file mode 100644 index 000000000..a4913c139 --- /dev/null +++ b/alchemy/templates/tanstack-start/tsconfig.json @@ -0,0 +1,31 @@ +{ + "include": [ + "alchemy.run.ts", + "types/**/*.ts", + "**/*.ts", + "**/*.tsx" + ], + "compilerOptions": { + "strict": true, + "esModuleInterop": true, + "jsx": "react-jsx", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["DOM", "DOM.Iterable", "ES2022"], + "isolatedModules": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "target": "ES2022", + "allowJs": true, + "forceConsistentCasingInFileNames": true, + "baseUrl": ".", + "paths": { + "~/*": ["./src/*"] + }, + "noEmit": true, + "types": [ + "@cloudflare/workers-types", + "./types/env.d.ts" + ] + } +} diff --git a/alchemy/templates/tanstack-start/types/env.d.ts b/alchemy/templates/tanstack-start/types/env.d.ts new file mode 100644 index 000000000..f371005c3 --- /dev/null +++ b/alchemy/templates/tanstack-start/types/env.d.ts @@ -0,0 +1,16 @@ +// This file infers types for the cloudflare:workers environment from your Alchemy Worker. +// @see https://alchemy.run/docs/concepts/bindings.html#type-safe-bindings + +import type { worker } from "../alchemy.run.ts"; + +export type CloudflareEnv = typeof worker.Env; + +declare global { + type Env = CloudflareEnv; +} + +declare module "cloudflare:workers" { + namespace Cloudflare { + export interface Env extends CloudflareEnv {} + } +} diff --git a/alchemy/templates/tanstack-start/vite.config.ts b/alchemy/templates/tanstack-start/vite.config.ts new file mode 100644 index 000000000..58d30c12c --- /dev/null +++ b/alchemy/templates/tanstack-start/vite.config.ts @@ -0,0 +1,33 @@ +import tailwindcss from "@tailwindcss/vite"; +import { tanstackStart } from "@tanstack/react-start/plugin/vite"; +import { cloudflareWorkersDevEnvironmentShim } from "alchemy/cloudflare"; +import { defineConfig, PluginOption } from "vite"; +import tsConfigPaths from "vite-tsconfig-paths"; + +export default defineConfig({ + server: { + port: 3000, + }, + build: { + target: "esnext", + rollupOptions: { + external: ["node:async_hooks", "cloudflare:workers"], + }, + }, + plugins: [ + tailwindcss() as PluginOption, + cloudflareWorkersDevEnvironmentShim(), + tsConfigPaths({ + projects: ["./tsconfig.json"], + }), + tanstackStart({ + target: "cloudflare-module", + tsr: { + routeTreeFileHeader: [ + "/** biome-ignore-all lint/suspicious/noExplicitAny: code generated by @tanstack/react-start */", + ], + quoteStyle: "double", + }, + }), + ], +}); diff --git a/alchemy/templates/tanstack-start/wrangler.jsonc b/alchemy/templates/tanstack-start/wrangler.jsonc new file mode 100644 index 000000000..f98de4cd9 --- /dev/null +++ b/alchemy/templates/tanstack-start/wrangler.jsonc @@ -0,0 +1,7 @@ +{ + "name": "website", + "main": ".output/server/index.mjs", + "compatibility_date": "2025-04-20", + "compatibility_flags": ["nodejs_compat"], + "assets": { "binding": "ASSETS", "directory": ".output/public" } +} diff --git a/alchemy/templates/typescript/.gitignore b/alchemy/templates/typescript/.gitignore new file mode 100644 index 000000000..dbba1c9ee --- /dev/null +++ b/alchemy/templates/typescript/.gitignore @@ -0,0 +1,37 @@ +# dependencies (bun install) +node_modules + +# output +out +dist +*.tgz + +# code coverage +coverage +*.lcov + +# logs +logs +_.log +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# caches +.eslintcache +.cache +*.tsbuildinfo + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store + +.alchemy/ +.env \ No newline at end of file diff --git a/alchemy/templates/typescript/README.md b/alchemy/templates/typescript/README.md new file mode 100644 index 000000000..abe3e4de4 --- /dev/null +++ b/alchemy/templates/typescript/README.md @@ -0,0 +1,15 @@ +# my-alchemy-app + +To install dependencies: + +```bash +bun install +``` + +To run: + +```bash +bun run index.ts +``` + +This project was created using `bun init` in bun v1.2.16. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime. diff --git a/alchemy/templates/typescript/_gitignore b/alchemy/templates/typescript/_gitignore new file mode 100644 index 000000000..dbba1c9ee --- /dev/null +++ b/alchemy/templates/typescript/_gitignore @@ -0,0 +1,37 @@ +# dependencies (bun install) +node_modules + +# output +out +dist +*.tgz + +# code coverage +coverage +*.lcov + +# logs +logs +_.log +report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local +.env.local + +# caches +.eslintcache +.cache +*.tsbuildinfo + +# IntelliJ based IDEs +.idea + +# Finder (MacOS) folder config +.DS_Store + +.alchemy/ +.env \ No newline at end of file diff --git a/alchemy/templates/typescript/alchemy.run.ts b/alchemy/templates/typescript/alchemy.run.ts new file mode 100644 index 000000000..c89f84d72 --- /dev/null +++ b/alchemy/templates/typescript/alchemy.run.ts @@ -0,0 +1,15 @@ +/// + +import alchemy from "alchemy"; +import { Worker } from "alchemy/cloudflare"; + +const app = await alchemy("my-alchemy-app"); + +export const worker = await Worker("worker", { + name: "my-alchemy-app", + entrypoint: "src/worker.ts", +}); + +console.log(worker.url); + +await app.finalize(); diff --git a/alchemy/templates/typescript/index.ts b/alchemy/templates/typescript/index.ts new file mode 100644 index 000000000..f67b2c645 --- /dev/null +++ b/alchemy/templates/typescript/index.ts @@ -0,0 +1 @@ +console.log("Hello via Bun!"); \ No newline at end of file diff --git a/alchemy/templates/typescript/package.json b/alchemy/templates/typescript/package.json new file mode 100644 index 000000000..9c44afe05 --- /dev/null +++ b/alchemy/templates/typescript/package.json @@ -0,0 +1,18 @@ +{ + "name": "@alchemy.run/typescript-template", + "version": "0.0.0", + "description": "Alchemy Typescript Project", + "type": "module", + "scripts": { + "build": "tsc -b", + "deploy": "tsx ./alchemy.run.ts", + "destroy": "tsx ./alchemy.run.ts --destroy" + }, + "devDependencies": { + "alchemy": "workspace:*", + "@cloudflare/workers-types": "^4.20250620.0", + "miniflare": "^4.20250617.3", + "@types/node": "^24.0.3", + "typescript": "^5.8.3" + } +} diff --git a/alchemy/templates/typescript/src/worker.js b/alchemy/templates/typescript/src/worker.js new file mode 100644 index 000000000..560cc4329 --- /dev/null +++ b/alchemy/templates/typescript/src/worker.js @@ -0,0 +1,5 @@ +export default { + async fetch(request, env, ctx) { + return new Response("Hello World from my-alchemy-app!"); + }, +}; diff --git a/alchemy/templates/typescript/src/worker.ts b/alchemy/templates/typescript/src/worker.ts new file mode 100644 index 000000000..560fd65a4 --- /dev/null +++ b/alchemy/templates/typescript/src/worker.ts @@ -0,0 +1,7 @@ +import type { worker } from "../alchemy.run.ts"; + +export default { + async fetch(request: Request, env: typeof worker.Env, ctx: ExecutionContext): Promise { + return new Response("Hello World from my-alchemy-app!"); + }, +}; diff --git a/alchemy/templates/typescript/tsconfig.json b/alchemy/templates/typescript/tsconfig.json new file mode 100644 index 000000000..46fa34335 --- /dev/null +++ b/alchemy/templates/typescript/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "allowImportingTsExtensions": true, + "rewriteRelativeImportExtensions": true, + "types": [ + "@cloudflare/workers-types", + "@types/node" + ] + }, + "include": [ + "src/**/*", + "types/**/*", + "alchemy.run.ts" + ] +} \ No newline at end of file diff --git a/alchemy/templates/typescript/types/env.d.ts b/alchemy/templates/typescript/types/env.d.ts new file mode 100644 index 000000000..f371005c3 --- /dev/null +++ b/alchemy/templates/typescript/types/env.d.ts @@ -0,0 +1,16 @@ +// This file infers types for the cloudflare:workers environment from your Alchemy Worker. +// @see https://alchemy.run/docs/concepts/bindings.html#type-safe-bindings + +import type { worker } from "../alchemy.run.ts"; + +export type CloudflareEnv = typeof worker.Env; + +declare global { + type Env = CloudflareEnv; +} + +declare module "cloudflare:workers" { + namespace Cloudflare { + export interface Env extends CloudflareEnv {} + } +} diff --git a/alchemy/templates/vite/.gitignore b/alchemy/templates/vite/.gitignore new file mode 100644 index 000000000..ec4a974db --- /dev/null +++ b/alchemy/templates/vite/.gitignore @@ -0,0 +1,27 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +.alchemy/ +.env \ No newline at end of file diff --git a/alchemy/templates/vite/README.md b/alchemy/templates/vite/README.md new file mode 100644 index 000000000..da9844432 --- /dev/null +++ b/alchemy/templates/vite/README.md @@ -0,0 +1,54 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: + +```js +export default tseslint.config({ + extends: [ + // Remove ...tseslint.configs.recommended and replace with this + ...tseslint.configs.recommendedTypeChecked, + // Alternatively, use this for stricter rules + ...tseslint.configs.strictTypeChecked, + // Optionally, add this for stylistic rules + ...tseslint.configs.stylisticTypeChecked, + ], + languageOptions: { + // other options... + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + }, +}) +``` + +You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: + +```js +// eslint.config.js +import reactX from 'eslint-plugin-react-x' +import reactDom from 'eslint-plugin-react-dom' + +export default tseslint.config({ + plugins: { + // Add the react-x and react-dom plugins + 'react-x': reactX, + 'react-dom': reactDom, + }, + rules: { + // other rules... + // Enable its recommended typescript rules + ...reactX.configs['recommended-typescript'].rules, + ...reactDom.configs.recommended.rules, + }, +}) +``` diff --git a/alchemy/templates/vite/_env.example b/alchemy/templates/vite/_env.example new file mode 100644 index 000000000..bb4cbbe94 --- /dev/null +++ b/alchemy/templates/vite/_env.example @@ -0,0 +1 @@ +ALCHEMY_PASSWORD=change-me \ No newline at end of file diff --git a/alchemy/templates/vite/_gitignore b/alchemy/templates/vite/_gitignore new file mode 100644 index 000000000..ec4a974db --- /dev/null +++ b/alchemy/templates/vite/_gitignore @@ -0,0 +1,27 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +.alchemy/ +.env \ No newline at end of file diff --git a/alchemy/templates/vite/alchemy.run.ts b/alchemy/templates/vite/alchemy.run.ts new file mode 100644 index 000000000..125f169b8 --- /dev/null +++ b/alchemy/templates/vite/alchemy.run.ts @@ -0,0 +1,17 @@ +/// + +import alchemy from "alchemy"; +import { Vite } from "alchemy/cloudflare"; + +const app = await alchemy("my-alchemy-app"); + +export const worker = await Vite("website", { + main: "worker/index.ts", + command: "bun run build", +}); + +console.log({ + url: worker.url, +}); + +await app.finalize(); diff --git a/alchemy/templates/vite/eslint.config.js b/alchemy/templates/vite/eslint.config.js new file mode 100644 index 000000000..092408a9f --- /dev/null +++ b/alchemy/templates/vite/eslint.config.js @@ -0,0 +1,28 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' + +export default tseslint.config( + { ignores: ['dist'] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ['**/*.{ts,tsx}'], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, + }, +) diff --git a/alchemy/templates/vite/index.html b/alchemy/templates/vite/index.html new file mode 100644 index 000000000..e4b78eae1 --- /dev/null +++ b/alchemy/templates/vite/index.html @@ -0,0 +1,13 @@ + + + + + + + Vite + React + TS + + +
+ + + diff --git a/alchemy/templates/vite/package.json b/alchemy/templates/vite/package.json new file mode 100644 index 000000000..4e3181229 --- /dev/null +++ b/alchemy/templates/vite/package.json @@ -0,0 +1,35 @@ +{ + "type": "module", + "name": "@alchemy.run/vite-template", + "private": true, + "version": "0.0.0", + "scripts": { + "build": "vite build", + "deploy": "bun --env-file=./.env ./alchemy.run.ts", + "destroy": "bun --env-file=./.env ./alchemy.run.ts --destroy", + "dev": "vite", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "react": "^19.1.0", + "react-dom": "^19.1.0" + }, + "devDependencies": { + "alchemy": "workspace:*", + "@cloudflare/vite-plugin": "^1.7.4", + "@cloudflare/workers-types": "^4.20250620.0", + "miniflare": "^4.20250617.3", + "@eslint/js": "^9.25.0", + "@types/react": "^19.1.2", + "@types/react-dom": "^19.1.2", + "@vitejs/plugin-react": "^4.4.1", + "eslint": "^9.25.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.19", + "globals": "^16.0.0", + "typescript": "^5.8.3", + "typescript-eslint": "^8.30.1", + "vite": "^6.3.5" + } +} diff --git a/alchemy/templates/vite/public/vite.svg b/alchemy/templates/vite/public/vite.svg new file mode 100644 index 000000000..e7b8dfb1b --- /dev/null +++ b/alchemy/templates/vite/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/alchemy/templates/vite/src/App.css b/alchemy/templates/vite/src/App.css new file mode 100644 index 000000000..b9d355df2 --- /dev/null +++ b/alchemy/templates/vite/src/App.css @@ -0,0 +1,42 @@ +#root { + max-width: 1280px; + margin: 0 auto; + padding: 2rem; + text-align: center; +} + +.logo { + height: 6em; + padding: 1.5em; + will-change: filter; + transition: filter 300ms; +} +.logo:hover { + filter: drop-shadow(0 0 2em #646cffaa); +} +.logo.react:hover { + filter: drop-shadow(0 0 2em #61dafbaa); +} + +@keyframes logo-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +} + +@media (prefers-reduced-motion: no-preference) { + a:nth-of-type(2) .logo { + animation: logo-spin infinite 20s linear; + } +} + +.card { + padding: 2em; +} + +.read-the-docs { + color: #888; +} diff --git a/alchemy/templates/vite/src/App.tsx b/alchemy/templates/vite/src/App.tsx new file mode 100644 index 000000000..3d7ded3ff --- /dev/null +++ b/alchemy/templates/vite/src/App.tsx @@ -0,0 +1,35 @@ +import { useState } from 'react' +import reactLogo from './assets/react.svg' +import viteLogo from '/vite.svg' +import './App.css' + +function App() { + const [count, setCount] = useState(0) + + return ( + <> + +

Vite + React

+
+ +

+ Edit src/App.tsx and save to test HMR +

+
+

+ Click on the Vite and React logos to learn more +

+ + ) +} + +export default App diff --git a/alchemy/templates/vite/src/assets/react.svg b/alchemy/templates/vite/src/assets/react.svg new file mode 100644 index 000000000..6c87de9bb --- /dev/null +++ b/alchemy/templates/vite/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/alchemy/templates/vite/src/index.css b/alchemy/templates/vite/src/index.css new file mode 100644 index 000000000..08a3ac9e1 --- /dev/null +++ b/alchemy/templates/vite/src/index.css @@ -0,0 +1,68 @@ +:root { + font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + font-weight: 400; + + color-scheme: light dark; + color: rgba(255, 255, 255, 0.87); + background-color: #242424; + + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +a { + font-weight: 500; + color: #646cff; + text-decoration: inherit; +} +a:hover { + color: #535bf2; +} + +body { + margin: 0; + display: flex; + place-items: center; + min-width: 320px; + min-height: 100vh; +} + +h1 { + font-size: 3.2em; + line-height: 1.1; +} + +button { + border-radius: 8px; + border: 1px solid transparent; + padding: 0.6em 1.2em; + font-size: 1em; + font-weight: 500; + font-family: inherit; + background-color: #1a1a1a; + cursor: pointer; + transition: border-color 0.25s; +} +button:hover { + border-color: #646cff; +} +button:focus, +button:focus-visible { + outline: 4px auto -webkit-focus-ring-color; +} + +@media (prefers-color-scheme: light) { + :root { + color: #213547; + background-color: #ffffff; + } + a:hover { + color: #747bff; + } + button { + background-color: #f9f9f9; + } +} diff --git a/alchemy/templates/vite/src/main.tsx b/alchemy/templates/vite/src/main.tsx new file mode 100644 index 000000000..bef5202a3 --- /dev/null +++ b/alchemy/templates/vite/src/main.tsx @@ -0,0 +1,10 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import './index.css' +import App from './App.tsx' + +createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/alchemy/templates/vite/src/vite-env.d.ts b/alchemy/templates/vite/src/vite-env.d.ts new file mode 100644 index 000000000..11f02fe2a --- /dev/null +++ b/alchemy/templates/vite/src/vite-env.d.ts @@ -0,0 +1 @@ +/// diff --git a/alchemy/templates/vite/tsconfig.json b/alchemy/templates/vite/tsconfig.json new file mode 100644 index 000000000..56556f81b --- /dev/null +++ b/alchemy/templates/vite/tsconfig.json @@ -0,0 +1,34 @@ +{ + "exclude": [ + "test" + ], + "include": [ + "types/**/*.ts", + "src/**/*.ts", + "alchemy.run.ts" + ], + "compilerOptions": { + "target": "es2021", + "lib": [ + "es2021" + ], + "jsx": "react-jsx", + "module": "es2022", + "moduleResolution": "Bundler", + "resolveJsonModule": true, + "allowJs": true, + "checkJs": false, + "noEmit": true, + "isolatedModules": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "allowImportingTsExtensions": true, + "rewriteRelativeImportExtensions": true, + "strict": true, + "skipLibCheck": true, + "types": [ + "@cloudflare/workers-types", + "./types/env.d.ts" + ] + } +} \ No newline at end of file diff --git a/alchemy/templates/vite/types/env.d.ts b/alchemy/templates/vite/types/env.d.ts new file mode 100644 index 000000000..f371005c3 --- /dev/null +++ b/alchemy/templates/vite/types/env.d.ts @@ -0,0 +1,16 @@ +// This file infers types for the cloudflare:workers environment from your Alchemy Worker. +// @see https://alchemy.run/docs/concepts/bindings.html#type-safe-bindings + +import type { worker } from "../alchemy.run.ts"; + +export type CloudflareEnv = typeof worker.Env; + +declare global { + type Env = CloudflareEnv; +} + +declare module "cloudflare:workers" { + namespace Cloudflare { + export interface Env extends CloudflareEnv {} + } +} diff --git a/alchemy/templates/vite/vite.config.ts b/alchemy/templates/vite/vite.config.ts new file mode 100644 index 000000000..f51f942b9 --- /dev/null +++ b/alchemy/templates/vite/vite.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +import { cloudflare } from "@cloudflare/vite-plugin"; + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react(), cloudflare()], +}); diff --git a/alchemy/templates/vite/worker/index.ts b/alchemy/templates/vite/worker/index.ts new file mode 100644 index 000000000..67cc72243 --- /dev/null +++ b/alchemy/templates/vite/worker/index.ts @@ -0,0 +1,12 @@ +export default { + fetch(request) { + const url = new URL(request.url); + + if (url.pathname.startsWith("/api/")) { + return Response.json({ + name: "Cloudflare", + }); + } + return new Response(null, { status: 404 }); + }, +} satisfies ExportedHandler; diff --git a/alchemy/templates/vite/wrangler.jsonc b/alchemy/templates/vite/wrangler.jsonc new file mode 100644 index 000000000..3660282fc --- /dev/null +++ b/alchemy/templates/vite/wrangler.jsonc @@ -0,0 +1,6 @@ +{ + "name": "website", + "main": "worker/index.ts", + "compatibility_date": "2025-04-20", + "assets": { "binding": "ASSETS", "directory": "dist/client" } +} diff --git a/alchemy/test/cloudflare/ai.test.ts b/alchemy/test/cloudflare/ai.test.ts index 7010ef031..a6d82cd91 100644 --- a/alchemy/test/cloudflare/ai.test.ts +++ b/alchemy/test/cloudflare/ai.test.ts @@ -22,6 +22,7 @@ describe("AI Resource Binding", () => { // Create a worker with an AI binding worker = await Worker(workerName, { name: workerName, + adopt: true, script: ` export default { async fetch(request, env) { diff --git a/alchemy/test/cloudflare/browser-rendering.test.ts b/alchemy/test/cloudflare/browser-rendering.test.ts index e99f0ba4c..1164de90b 100644 --- a/alchemy/test/cloudflare/browser-rendering.test.ts +++ b/alchemy/test/cloudflare/browser-rendering.test.ts @@ -34,6 +34,7 @@ describe("Browser Rendering Resource", () => { // Create a worker with browser rendering binding worker = await Worker(workerName, { name: workerName, + adopt: true, entrypoint: path.join(import.meta.dirname, "browser-handler.ts"), format: "esm", compatibilityFlags: ["nodejs_compat"], // Required for puppeteer diff --git a/alchemy/test/cloudflare/bucket.test.ts b/alchemy/test/cloudflare/bucket.test.ts index 47110448c..6dc27de6c 100644 --- a/alchemy/test/cloudflare/bucket.test.ts +++ b/alchemy/test/cloudflare/bucket.test.ts @@ -202,6 +202,7 @@ describe("R2 Bucket Resource", async () => { // Create a worker with the R2 bucket binding worker = await Worker(workerName, { name: workerName, + adopt: true, script: ` export default { async fetch(request, env, ctx) { diff --git a/alchemy/test/cloudflare/certificate-pack.test.ts b/alchemy/test/cloudflare/certificate-pack.test.ts new file mode 100644 index 000000000..190a88084 --- /dev/null +++ b/alchemy/test/cloudflare/certificate-pack.test.ts @@ -0,0 +1,500 @@ +import { describe, expect } from "vitest"; +import { alchemy } from "../../src/alchemy.ts"; +import { createCloudflareApi } from "../../src/cloudflare/api.ts"; +import { CertificatePack } from "../../src/cloudflare/certificate-pack.ts"; +import { Zone } from "../../src/cloudflare/zone.ts"; +import { destroy } from "../../src/destroy.ts"; +import { BRANCH_PREFIX } from "../util.ts"; + +import "../../src/test/vitest.ts"; + +const api = await createCloudflareApi(); + +const test = alchemy.test(import.meta, { + prefix: BRANCH_PREFIX, +}); + +describe("CertificatePack Resource", () => { + // Use BRANCH_PREFIX for deterministic, non-colliding resource names + const testDomain = `${BRANCH_PREFIX}-cert-test.dev`; + + test("create, update cloudflare branding, and delete certificate pack", async (scope) => { + let zone: Zone | undefined; + let certificatePack: CertificatePack | undefined; + + try { + // First create a test zone for the certificate pack + zone = await Zone(testDomain, { + name: testDomain, + type: "full", + jumpStart: false, + }); + + expect(zone.id).toBeTruthy(); + expect(zone.name).toEqual(testDomain); + + // Create a certificate pack with Let's Encrypt + certificatePack = await CertificatePack(`${BRANCH_PREFIX}-cert`, { + zone: zone, + certificateAuthority: "lets_encrypt", + hosts: [testDomain, `www.${testDomain}`], + validationMethod: "txt", + validityDays: 90, + cloudflareBranding: false, + }); + + expect(certificatePack.id).toBeTruthy(); + expect(certificatePack.certificateAuthority).toEqual("lets_encrypt"); + expect(certificatePack.hosts).toEqual([testDomain, `www.${testDomain}`]); + expect(certificatePack.validationMethod).toEqual("txt"); + expect(certificatePack.validityDays).toEqual(90); + expect(certificatePack.type).toEqual("advanced"); + expect(certificatePack.cloudflareBranding).toEqual(false); + expect(certificatePack.zoneId).toEqual(zone.id); + + // Verify certificate pack was created by querying the API directly + const getResponse = await api.get( + `/zones/${zone.id}/ssl/certificate_packs/${certificatePack.id}`, + ); + expect(getResponse.status).toEqual(200); + + const responseData: any = await getResponse.json(); + expect(responseData.result.id).toEqual(certificatePack.id); + expect(responseData.result.certificate_authority).toEqual("lets_encrypt"); + expect(responseData.result.validation_method).toEqual("txt"); + expect(responseData.result.validity_days).toEqual(90); + expect(responseData.result.cloudflare_branding).toEqual(false); + + // Update the certificate pack to enable Cloudflare branding + // This is the only property that can be updated + certificatePack = await CertificatePack(`${BRANCH_PREFIX}-cert`, { + zone: zone, + certificateAuthority: "lets_encrypt", + hosts: [testDomain, `www.${testDomain}`], + validationMethod: "txt", + validityDays: 90, + cloudflareBranding: true, // Only change this property + }); + + expect(certificatePack.cloudflareBranding).toEqual(true); + expect(certificatePack.certificateAuthority).toEqual("lets_encrypt"); + expect(certificatePack.hosts).toEqual([testDomain, `www.${testDomain}`]); + + // Verify the update was applied in the API + const updatedResponse = await api.get( + `/zones/${zone.id}/ssl/certificate_packs/${certificatePack.id}`, + ); + const updatedData: any = await updatedResponse.json(); + expect(updatedData.result.cloudflare_branding).toEqual(true); + } finally { + // Always clean up, even if test assertions fail + await destroy(scope); + + // Verify certificate pack was deleted + if (certificatePack && zone) { + await assertCertificatePackDoesNotExist( + api, + zone.id, + certificatePack.id, + ); + } + + // Verify zone was deleted + if (zone) { + const getDeletedResponse = await api.get(`/zones/${zone.id}`); + const text = await getDeletedResponse.text(); + expect(text).toContain("Invalid zone identifier"); + expect(getDeletedResponse.status).toEqual(400); + } + } + }); + + test("prevent updating immutable properties", async (scope) => { + let zone: Zone | undefined; + let certificatePack: CertificatePack | undefined; + + try { + // Create a test zone + zone = await Zone(`${testDomain}-immutable`, { + name: `${testDomain}-immutable`, + type: "full", + jumpStart: false, + }); + + // Create initial certificate pack + certificatePack = await CertificatePack( + `${BRANCH_PREFIX}-immutable-cert`, + { + zone: zone, + certificateAuthority: "lets_encrypt", + hosts: [`${testDomain}-immutable`], + validationMethod: "txt", + validityDays: 90, + }, + ); + + expect(certificatePack.id).toBeTruthy(); + + // Try to update certificate authority (should fail) + await expect( + CertificatePack(`${BRANCH_PREFIX}-immutable-cert`, { + zone: zone, + certificateAuthority: "google", // Changed from lets_encrypt + hosts: [`${testDomain}-immutable`], + validationMethod: "txt", + validityDays: 90, + }), + ).rejects.toThrow( + "Cannot change certificateAuthority from 'lets_encrypt' to 'google'", + ); + + // Try to update hosts (should fail) + await expect( + CertificatePack(`${BRANCH_PREFIX}-immutable-cert`, { + zone: zone, + certificateAuthority: "lets_encrypt", + hosts: [`${testDomain}-immutable`, `www.${testDomain}-immutable`], // Added host + validationMethod: "txt", + validityDays: 90, + }), + ).rejects.toThrow("Cannot change hosts from"); + + // Try to update validation method (should fail) + await expect( + CertificatePack(`${BRANCH_PREFIX}-immutable-cert`, { + zone: zone, + certificateAuthority: "lets_encrypt", + hosts: [`${testDomain}-immutable`], + validationMethod: "http", // Changed from txt + validityDays: 90, + }), + ).rejects.toThrow("Cannot change validationMethod from 'txt' to 'http'"); + + // Try to update validity days (should fail) + await expect( + CertificatePack(`${BRANCH_PREFIX}-immutable-cert`, { + zone: zone, + certificateAuthority: "lets_encrypt", + hosts: [`${testDomain}-immutable`], + validationMethod: "txt", + validityDays: 365, // Changed from 90 + }), + ).rejects.toThrow("Cannot change validityDays from 90 to 365"); + + // Try to update type (should fail) + await expect( + CertificatePack(`${BRANCH_PREFIX}-immutable-cert`, { + zone: zone, + certificateAuthority: "lets_encrypt", + hosts: [`${testDomain}-immutable`], + validationMethod: "txt", + validityDays: 90, + type: "advanced", // Explicit type change attempt + }), + ).rejects.toThrow("Cannot change type from 'advanced' to 'advanced'"); + } finally { + await destroy(scope); + + if (certificatePack && zone) { + await assertCertificatePackDoesNotExist( + api, + zone.id, + certificatePack.id, + ); + } + } + }); + + test("create certificate pack with zone ID string", async (scope) => { + let zone: Zone | undefined; + let certificatePack: CertificatePack | undefined; + + try { + // Create a test zone + zone = await Zone(`${testDomain}-string-id`, { + name: `${testDomain}-string-id`, + type: "full", + jumpStart: false, + }); + + // Create certificate pack using zone ID string instead of Zone object + certificatePack = await CertificatePack(`${BRANCH_PREFIX}-string-cert`, { + zone: zone.id, // Use zone ID string + certificateAuthority: "lets_encrypt", + hosts: [`${testDomain}-string-id`, `*.${testDomain}-string-id`], + validationMethod: "txt", + validityDays: 30, + cloudflareBranding: true, + }); + + expect(certificatePack.id).toBeTruthy(); + expect(certificatePack.zoneId).toEqual(zone.id); + expect(certificatePack.hosts).toEqual([ + `${testDomain}-string-id`, + `*.${testDomain}-string-id`, + ]); + expect(certificatePack.validityDays).toEqual(30); + expect(certificatePack.cloudflareBranding).toEqual(true); + + // Verify with API + const getResponse = await api.get( + `/zones/${zone.id}/ssl/certificate_packs/${certificatePack.id}`, + ); + expect(getResponse.status).toEqual(200); + + const responseData: any = await getResponse.json(); + expect(responseData.result.hosts).toEqual([ + `${testDomain}-string-id`, + `*.${testDomain}-string-id`, + ]); + expect(responseData.result.validity_days).toEqual(30); + expect(responseData.result.cloudflare_branding).toEqual(true); + } finally { + await destroy(scope); + + if (certificatePack && zone) { + await assertCertificatePackDoesNotExist( + api, + zone.id, + certificatePack.id, + ); + } + } + }); + + test("validate host requirements", async (scope) => { + let zone: Zone | undefined; + + try { + // Create a test zone + zone = await Zone(`${testDomain}-validation`, { + name: `${testDomain}-validation`, + type: "full", + jumpStart: false, + }); + + // Test empty hosts array (should fail) + await expect( + CertificatePack(`${BRANCH_PREFIX}-empty-hosts`, { + zone: zone, + certificateAuthority: "lets_encrypt", + hosts: [], // Empty array + validationMethod: "txt", + validityDays: 90, + }), + ).rejects.toThrow("At least one host must be specified"); + + // Test too many hosts (should fail) + const tooManyHosts = Array.from( + { length: 51 }, + (_, i) => `host${i}.${testDomain}-validation`, + ); + await expect( + CertificatePack(`${BRANCH_PREFIX}-too-many-hosts`, { + zone: zone, + certificateAuthority: "lets_encrypt", + hosts: tooManyHosts, // 51 hosts > 50 limit + validationMethod: "txt", + validityDays: 90, + }), + ).rejects.toThrow("Maximum 50 hosts are allowed per certificate pack"); + } finally { + await destroy(scope); + } + }); + + test("auto-infer zone from hostname", async (scope) => { + let zone: Zone | undefined; + let certificatePack: CertificatePack | undefined; + + try { + // Create a test zone + zone = await Zone(`${testDomain}-auto-infer`, { + name: `${testDomain}-auto-infer`, + type: "full", + jumpStart: false, + }); + + // Create certificate pack without specifying zone - should auto-infer + certificatePack = await CertificatePack( + `${BRANCH_PREFIX}-auto-infer-cert`, + { + // zone: omitted - should auto-infer from hosts + certificateAuthority: "lets_encrypt", + hosts: [`${testDomain}-auto-infer`, `www.${testDomain}-auto-infer`], + validationMethod: "txt", + validityDays: 90, + }, + ); + + expect(certificatePack.id).toBeTruthy(); + expect(certificatePack.zoneId).toEqual(zone.id); + expect(certificatePack.zoneName).toEqual(`${testDomain}-auto-infer`); + expect(certificatePack.hosts).toEqual([ + `${testDomain}-auto-infer`, + `www.${testDomain}-auto-infer`, + ]); + + // Verify certificate pack was created in the correct zone + const getResponse = await api.get( + `/zones/${zone.id}/ssl/certificate_packs/${certificatePack.id}`, + ); + expect(getResponse.status).toEqual(200); + + const responseData: any = await getResponse.json(); + expect(responseData.result.zone_id).toEqual(zone.id); + } finally { + await destroy(scope); + + if (certificatePack && zone) { + await assertCertificatePackDoesNotExist( + api, + zone.id, + certificatePack.id, + ); + } + } + }); + + test("adopt existing certificate pack instead of creating duplicate", async (scope) => { + let zone: Zone | undefined; + let firstCertificatePack: CertificatePack | undefined; + let secondCertificatePack: CertificatePack | undefined; + + try { + // Create a test zone + zone = await Zone(`${testDomain}-adopt`, { + name: `${testDomain}-adopt`, + type: "full", + jumpStart: false, + }); + + // Create first certificate pack + firstCertificatePack = await CertificatePack( + `${BRANCH_PREFIX}-adopt-cert-1`, + { + zone: zone, + certificateAuthority: "lets_encrypt", + hosts: [`${testDomain}-adopt`, `www.${testDomain}-adopt`], + validationMethod: "txt", + validityDays: 90, + }, + ); + + expect(firstCertificatePack.id).toBeTruthy(); + + // Create second certificate pack with same configuration - should adopt existing one + secondCertificatePack = await CertificatePack( + `${BRANCH_PREFIX}-adopt-cert-2`, + { + zone: zone, + certificateAuthority: "lets_encrypt", + hosts: [`${testDomain}-adopt`, `www.${testDomain}-adopt`], + validationMethod: "txt", + validityDays: 90, + }, + ); + + // Should have the same ID (adopted the existing one) + expect(secondCertificatePack.id).toEqual(firstCertificatePack.id); + expect(secondCertificatePack.hosts).toEqual(firstCertificatePack.hosts); + expect(secondCertificatePack.certificateAuthority).toEqual( + firstCertificatePack.certificateAuthority, + ); + expect(secondCertificatePack.validationMethod).toEqual( + firstCertificatePack.validationMethod, + ); + expect(secondCertificatePack.validityDays).toEqual( + firstCertificatePack.validityDays, + ); + } finally { + await destroy(scope); + + // Both certificate pack references point to the same underlying cert, + // so we only need to check once + if (firstCertificatePack && zone) { + await assertCertificatePackDoesNotExist( + api, + zone.id, + firstCertificatePack.id, + ); + } + } + }); + + test("auto-infer zone with wildcard hostname", async (scope) => { + let zone: Zone | undefined; + let certificatePack: CertificatePack | undefined; + + try { + // Create a test zone + zone = await Zone(`${testDomain}-wildcard`, { + name: `${testDomain}-wildcard`, + type: "full", + jumpStart: false, + }); + + // Create certificate pack with wildcard hostname - should auto-infer zone + certificatePack = await CertificatePack( + `${BRANCH_PREFIX}-wildcard-cert`, + { + // zone: omitted - should auto-infer from wildcard host + certificateAuthority: "lets_encrypt", + hosts: [`*.${testDomain}-wildcard`, `${testDomain}-wildcard`], + validationMethod: "txt", + validityDays: 90, + }, + ); + + expect(certificatePack.id).toBeTruthy(); + expect(certificatePack.zoneId).toEqual(zone.id); + expect(certificatePack.zoneName).toEqual(`${testDomain}-wildcard`); + expect(certificatePack.hosts).toEqual([ + `*.${testDomain}-wildcard`, + `${testDomain}-wildcard`, + ]); + } finally { + await destroy(scope); + + if (certificatePack && zone) { + await assertCertificatePackDoesNotExist( + api, + zone.id, + certificatePack.id, + ); + } + } + }); +}); + +/** + * Helper function to verify certificate pack was deleted + */ +async function assertCertificatePackDoesNotExist( + api: ReturnType, + zoneId: string, + certificatePackId: string, +): Promise { + const response = await api.get( + `/zones/${zoneId}/ssl/certificate_packs/${certificatePackId}`, + ); + + // Certificate pack should either not exist (404) or be marked as deleted + if (response.status === 404) { + // Certificate pack not found - expected after deletion + return; + } + + if (response.status === 200) { + const data: any = await response.json(); + if (data.result?.status === "deleted") { + // Certificate pack exists but is marked as deleted - acceptable + return; + } + } + + // If we get here, the certificate pack still exists and is not deleted + throw new Error( + `Certificate pack ${certificatePackId} still exists after deletion attempt`, + ); +} diff --git a/alchemy/test/cloudflare/container-handler.ts b/alchemy/test/cloudflare/container-handler.ts new file mode 100644 index 000000000..0a29834f9 --- /dev/null +++ b/alchemy/test/cloudflare/container-handler.ts @@ -0,0 +1,109 @@ +import { Container } from "@cloudflare/containers"; + +export class MyContainer extends Container { + defaultPort = 8080; // The default port for the container to listen on + sleepAfter = "3m"; // Sleep the container if no requests are made in this timeframe + + envVars = { + MESSAGE: "I was passed in via the container class!", + }; + + override onStart() { + console.log("Container successfully started"); + } + + override onStop() { + console.log("Container successfully shut down"); + } + + override onError(error: unknown) { + console.log("Container error:", error); + } +} + +export default { + async fetch( + request: Request, + env: { MY_CONTAINER: DurableObjectNamespace }, + ): Promise { + const pathname = new URL(request.url).pathname; + // If you want to route requests to a specific container, + // pass a unique container identifier to .get() + + if (pathname.startsWith("/container")) { + const containerInstance = getContainer(env.MY_CONTAINER, pathname); + return containerInstance.fetch(request); + } + + if (pathname.startsWith("/error")) { + const containerInstance = getContainer(env.MY_CONTAINER, "error-test"); + return containerInstance.fetch(request); + } + + if (pathname.startsWith("/lb")) { + const containerInstance = await getRandom(env.MY_CONTAINER, 3); + return containerInstance.fetch(request); + } + + if (pathname.startsWith("/singleton")) { + // getContainer will return a specific instance if no second argument is provided + return getContainer(env.MY_CONTAINER).fetch(request); + } + + return new Response( + "Call /container to start a container with a 10s timeout.\nCall /error to start a container that errors\nCall /lb to test load balancing", + ); + }, +}; + +/** + * Get a random container instances across N instances + * @param binding The Container's Durable Object binding + * @param instances Number of instances to load balance across + * @returns A promise resolving to a container stub ready to handle requests + */ +export async function getRandom( + binding: DurableObjectNamespace, + instances = 3, +): Promise> { + // Generate a random ID within the range of instances + const id = Math.floor(Math.random() * instances).toString(); + + // Always use idFromName for consistent behavior + // idFromString requires a 64-hex digit string which is hard to generate + const objectId = binding.idFromName(`instance-${id}`); + + // Return the stub for the selected instance + return binding.get(objectId); +} + +/** + * Deprecated funtion to get random container instances. Renamed to getRandom + * @param binding The Container's Durable Object binding + * @param instances Number of instances to load balance across + * @returns A promise resolving to a container stub ready to handle requests + */ +export async function loadBalance( + binding: DurableObjectNamespace, + instances = 3, +): Promise> { + console.warn( + "loadBalance is deprecated, please use getRandom instead. This will be removed in a future version.", + ); + return getRandom(binding, instances); +} + +/** + * Get a container stub + * @param binding The Container's Durable Object binding + * @param name The name of the instance to get, uses 'cf-singleton-container' by default + * @returns A container stub ready to handle requests + */ +export const singletonContainerId = "cf-singleton-container"; +export function getContainer( + binding: DurableObjectNamespace, + name?: string, +): DurableObjectStub { + const objectId = binding.idFromName(name ?? singletonContainerId); + return binding.get(objectId); +} diff --git a/alchemy/test/cloudflare/container.test.ts b/alchemy/test/cloudflare/container.test.ts new file mode 100644 index 000000000..dd2d50b01 --- /dev/null +++ b/alchemy/test/cloudflare/container.test.ts @@ -0,0 +1,47 @@ +import path from "node:path"; +import { describe } from "vitest"; +import { alchemy } from "../../src/alchemy.ts"; +import { Container } from "../../src/cloudflare/index.ts"; +import { Worker } from "../../src/cloudflare/worker.ts"; +import { destroy } from "../../src/destroy.ts"; +import "../../src/test/vitest.ts"; +import { BRANCH_PREFIX } from "../util.ts"; + +const test = alchemy.test(import.meta, { + prefix: BRANCH_PREFIX, +}); + +describe("Container Resource", () => { + test("create container", async (scope) => { + try { + const make = async (dockerfile?: string) => + Worker(`container-test-worker${BRANCH_PREFIX}`, { + adopt: true, + entrypoint: path.join(import.meta.dirname, "container-handler.ts"), + compatibilityFlags: ["nodejs_compat"], + compatibilityDate: "2025-06-24", + format: "esm", + bindings: { + MY_CONTAINER: await Container(`container-test${BRANCH_PREFIX}`, { + className: "MyContainer", + name: "test-image", + tag: "latest", + build: { + context: path.join(import.meta.dirname, "container"), + dockerfile, + }, + maxInstances: 1, + }), + }, + }); + + // create + await make(); + // update + await make("Dockerfile.update"); + } finally { + // delete + await destroy(scope); + } + }); +}); diff --git a/alchemy/test/cloudflare/container/Dockerfile b/alchemy/test/cloudflare/container/Dockerfile new file mode 100644 index 000000000..91aafb3b3 --- /dev/null +++ b/alchemy/test/cloudflare/container/Dockerfile @@ -0,0 +1 @@ +FROM python:3.11-slim diff --git a/alchemy/test/cloudflare/container/Dockerfile.update b/alchemy/test/cloudflare/container/Dockerfile.update new file mode 100644 index 000000000..bee3c167c --- /dev/null +++ b/alchemy/test/cloudflare/container/Dockerfile.update @@ -0,0 +1 @@ +FROM python:3.12-slim diff --git a/alchemy/test/cloudflare/custom-domain.test.ts b/alchemy/test/cloudflare/custom-domain.test.ts new file mode 100644 index 000000000..c32c68142 --- /dev/null +++ b/alchemy/test/cloudflare/custom-domain.test.ts @@ -0,0 +1,89 @@ +import { afterAll, describe, expect } from "vitest"; +import { alchemy } from "../../src/alchemy.ts"; +import { + createCloudflareApi, + type CloudflareApi, +} from "../../src/cloudflare/api.ts"; +import { Worker } from "../../src/cloudflare/worker.ts"; +import { Zone } from "../../src/cloudflare/zone.ts"; +import { destroy } from "../../src/destroy.ts"; +import type { Scope } from "../../src/scope.ts"; +import { BRANCH_PREFIX } from "../util.ts"; +// must import this or else alchemy.test won't exist +import "../../src/test/vitest.ts"; + +const test = alchemy.test(import.meta, { + prefix: BRANCH_PREFIX, + quiet: false, +}); + +const testDomain = `${BRANCH_PREFIX}-custom-domain-test.com`; + +let zone: Zone; +let scope: Scope | undefined; + +test.beforeAll(async (_scope) => { + zone = await Zone(`${BRANCH_PREFIX}-zone`, { + name: testDomain, + }); + scope = _scope; +}); + +afterAll(async () => { + if (scope) { + await destroy(scope); + } +}); + +const api = await createCloudflareApi(); + +describe("Custom Domain", () => { + test("should create a custom domain", async (scope) => { + try { + const worker = await Worker(`${BRANCH_PREFIX}-worker-1`, { + domains: [`sub.${testDomain}`], + script: ` + export default { + fetch(request, env) { + return new Response('Hello from ${BRANCH_PREFIX}-worker-1!'); + } + } + `, + adopt: true, + }); + + expect(worker.domains?.[0]).toMatchObject({ + name: `sub.${testDomain}`, + zoneId: zone.id, + }); + await assertCustomDomain(api, worker, `sub.${testDomain}`); + } finally { + await destroy(scope); + } + }); +}); + +export async function assertCustomDomain( + api: CloudflareApi, + worker: Worker, + domain: string, +) { + const domains = (await ( + await api.get(`/accounts/${api.accountId}/workers/domains`) + ).json()) as { + result: { + hostname: string; + zone_id: string; + service: string; + environment: string; + }[]; + }; + expect(domains.result).toContainEqual( + expect.objectContaining({ + hostname: domain, + zone_id: zone.id, + service: worker.name, + environment: "production", + }), + ); +} diff --git a/alchemy/test/cloudflare/d1-database.test.ts b/alchemy/test/cloudflare/d1-database.test.ts index edc0ed7f7..7cb06dfeb 100644 --- a/alchemy/test/cloudflare/d1-database.test.ts +++ b/alchemy/test/cloudflare/d1-database.test.ts @@ -1,6 +1,9 @@ import { describe, expect } from "vitest"; import { alchemy } from "../../src/alchemy.ts"; -import { createCloudflareApi } from "../../src/cloudflare/api.ts"; +import { + type CloudflareApi, + createCloudflareApi, +} from "../../src/cloudflare/api.ts"; import { D1Database, listDatabases } from "../../src/cloudflare/d1-database.ts"; import { Worker } from "../../src/cloudflare/worker.ts"; import { BRANCH_PREFIX } from "../util.ts"; @@ -192,14 +195,11 @@ describe("D1 Database Resource", async () => { expect(database.id).toBeTruthy(); // Now check if the test_migrations_table exists by querying the schema - const resp = await api.post( - `/accounts/${api.accountId}/d1/database/${database.id}/query`, - { - sql: "SELECT name FROM sqlite_master WHERE type='table' AND name='test_migrations_table';", - }, + const tables = await getResults( + api, + database, + "SELECT name FROM sqlite_master WHERE type='table' AND name='test_migrations_table';", ); - const data: any = await resp.json(); - const tables = data.result?.results || data.result?.[0]?.results || []; expect(tables.length).toBeGreaterThan(0); expect(tables[0]?.name).toEqual("test_migrations_table"); @@ -244,16 +244,12 @@ describe("D1 Database Resource", async () => { expect(cloned.id).toBeTruthy(); // Verify the cloned data exists in the target database - const resp = await api.post( - `/accounts/${api.accountId}/d1/database/${cloned.id}/query`, - { - sql: "SELECT * FROM test_clone WHERE id = 1;", - }, + const results = await getResults( + api, + cloned, + "SELECT * FROM test_clone WHERE id = 1;", ); - const data: any = await resp.json(); - const results = data.result?.[0]?.results || []; - expect(results.length).toEqual(1); expect(results[0].name).toEqual("test-data"); } finally { @@ -293,19 +289,12 @@ describe("D1 Database Resource", async () => { expect(cloned.name).toEqual(targetDb); expect(cloned.id).toBeTruthy(); - await new Promise((resolve) => setTimeout(resolve, 2000)); - - // Verify the cloned data exists in the target database - const resp = await api.post( - `/accounts/${api.accountId}/d1/database/${cloned.id}/query`, - { - sql: "SELECT * FROM test_clone_by_name WHERE id = 1;", - }, + const results = await getResults( + api, + cloned, + "SELECT * FROM test_clone_by_name WHERE id = 1;", ); - const data: any = await resp.json(); - const results = data.result?.[0]?.results || []; - expect(results.length).toEqual(1); expect(results[0].value).toEqual("name-lookup-test"); } finally { @@ -345,19 +334,13 @@ describe("D1 Database Resource", async () => { expect(clonedDb.name).toEqual(targetDbId); expect(clonedDb.id).toBeTruthy(); - await new Promise((resolve) => setTimeout(resolve, 2000)); - // Verify the cloned data exists in the target database - const resp = await api.post( - `/accounts/${api.accountId}/d1/database/${clonedDb.id}/query`, - { - sql: "SELECT * FROM direct_clone_test WHERE id = 1;", - }, + const results = await getResults( + api, + clonedDb, + "SELECT * FROM direct_clone_test WHERE id = 1;", ); - const data: any = await resp.json(); - const results = data.result?.[0]?.results || []; - expect(results.length).toEqual(1); expect(results[0].data).toEqual("direct-clone-data"); } finally { @@ -384,6 +367,7 @@ describe("D1 Database Resource", async () => { // Create a worker with the D1 database binding worker = await Worker(workerName, { name: workerName, + adopt: true, script: ` export default { async fetch(request, env, ctx) { @@ -478,6 +462,32 @@ describe("D1 Database Resource", async () => { }, 120000); // Increased timeout for D1 database operations }); +async function getResults( + api: CloudflareApi, + database: D1Database, + sql: string, +): Promise { + // this query is eventually consistent, so we need to retry + // TODO(sam): can we use a D1 Session to ensure strong consistency? + for (let i = 0; i < 10; i++) { + // Verify the cloned data exists in the target database + const resp = await api.post( + `/accounts/${api.accountId}/d1/database/${database.id}/query`, + { + sql, + }, + ); + + const data: any = await resp.json(); + const results = data.result?.[0]?.results || []; + if (results.length > 0) { + return results; + } + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + throw new Error("Failed to get results from database"); +} + async function assertDatabaseDeleted(database: D1Database) { const api = await createCloudflareApi(); try { diff --git a/alchemy/test/cloudflare/dispatch-namespace.test.ts b/alchemy/test/cloudflare/dispatch-namespace.test.ts index d5f7d75b4..75ccfc1c2 100644 --- a/alchemy/test/cloudflare/dispatch-namespace.test.ts +++ b/alchemy/test/cloudflare/dispatch-namespace.test.ts @@ -76,7 +76,7 @@ describe("Dispatch Namespace Resource", () => { }); test("comprehensive dispatch namespace and worker integration", async (scope) => { - const workerName = `${BRANCH_PREFIX}-target-worker`; + const workerName = `${BRANCH_PREFIX}-dispatch-namespace-target-worker`; const dispatcherWorkerName = `${BRANCH_PREFIX}-dispatcher-worker`; const namespaceName = `${BRANCH_PREFIX}-comprehensive-test`; @@ -94,6 +94,7 @@ describe("Dispatch Namespace Resource", () => { // 2. Create a worker in the dispatch namespace targetWorker = await Worker(workerName, { name: workerName, + adopt: true, script: ` export default { async fetch(request, env, ctx) { @@ -102,12 +103,12 @@ describe("Dispatch Namespace Resource", () => { } `, namespace: dispatchNamespace, - url: false, }); // 3. Create a worker bound to the namespace (dispatcher) dispatcherWorker = await Worker(dispatcherWorkerName, { name: dispatcherWorkerName, + adopt: true, script: ` export default { async fetch(request, env, ctx) { @@ -131,8 +132,11 @@ describe("Dispatch Namespace Resource", () => { expect(text).toEqual("Dispatch response: Hello from dispatch namespace!"); } finally { await alchemy.destroy(scope); - if (targetWorker) { - await assertWorkerDoesNotExist(targetWorker.name); + if (targetWorker && dispatchNamespace) { + await assertWorkerInNamespaceDoesNotExist( + targetWorker.name, + dispatchNamespace.namespace, + ); } if (dispatcherWorker) { await assertWorkerDoesNotExist(dispatcherWorker.name); @@ -182,6 +186,7 @@ describe("Dispatch Namespace Resource", () => { // 4. Create a worker in the dispatch namespace with asset bindings assetWorker = await Worker(workerName, { name: workerName, + adopt: true, script: ` export default { async fetch(request, env, ctx) { @@ -199,6 +204,7 @@ describe("Dispatch Namespace Resource", () => { // 5. Create a dispatcher worker that routes to the asset worker dispatcherWorker = await Worker(dispatcherWorkerName, { name: dispatcherWorkerName, + adopt: true, script: ` export default { async fetch(request, env, ctx) { @@ -232,8 +238,11 @@ describe("Dispatch Namespace Resource", () => { } await alchemy.destroy(scope); - if (assetWorker) { - await assertWorkerDoesNotExist(assetWorker.name); + if (assetWorker && dispatchNamespace) { + await assertWorkerInNamespaceDoesNotExist( + assetWorker.name, + dispatchNamespace.namespace, + ); } if (dispatcherWorker) { await assertWorkerDoesNotExist(dispatcherWorker.name); @@ -274,4 +283,16 @@ describe("Dispatch Namespace Resource", () => { expect(response.status).toEqual(404); } + + async function assertWorkerInNamespaceDoesNotExist( + workerName: string, + namespace: string, + ): Promise { + const api = await createCloudflareApi(); + const response = await api.get( + `/accounts/${api.accountId}/workers/dispatch/namespaces/${namespace}/scripts/${workerName}`, + ); + + expect(response.status).toEqual(404); + } }); diff --git a/alchemy/test/cloudflare/dns-records.test.ts b/alchemy/test/cloudflare/dns-records.test.ts index b46ca51e4..8ca06e5bb 100644 --- a/alchemy/test/cloudflare/dns-records.test.ts +++ b/alchemy/test/cloudflare/dns-records.test.ts @@ -73,13 +73,28 @@ describe("DnsRecords Resource", async () => { for (const record of dnsRecords.records) { let response; const start = Date.now(); - const timeout = 10000; // 10 seconds + const timeout = 120_000; // 120 seconds const interval = 500; // 0.5 seconds while (true) { response = await api.get( `/zones/${dnsRecords.zoneId}/dns_records/${record.id}`, ); - if (response.ok) break; + if (response.ok) { + try { + const data: any = await response.json(); + expect(data.result.name).toBe(record.name); + expect(data.result.type).toBe(record.type); + expect(data.result.content).toBe(record.content); + expect(data.result.proxied).toBe(record.proxied); + expect(data.result.comment).toBe(record.comment); + if (record.priority) { + expect(data.result.priority).toBe(record.priority); + } + break; + } catch (err) { + console.error("Error parsing response:", err); + } + } if (Date.now() - start > timeout) { throw new Error( `DNS record ${record.id} did not become available within 10s`, @@ -87,16 +102,6 @@ describe("DnsRecords Resource", async () => { } await new Promise((resolve) => setTimeout(resolve, interval)); } - - const data: any = await response.json(); - expect(data.result.name).toBe(record.name); - expect(data.result.type).toBe(record.type); - expect(data.result.content).toBe(record.content); - expect(data.result.proxied).toBe(record.proxied); - expect(data.result.comment).toBe(record.comment); - if (record.priority) { - expect(data.result.priority).toBe(record.priority); - } } // Update records - modify one record, add one record, remove one record diff --git a/alchemy/test/cloudflare/do-state-store.test.ts b/alchemy/test/cloudflare/do-state-store.test.ts new file mode 100644 index 000000000..47feeab9a --- /dev/null +++ b/alchemy/test/cloudflare/do-state-store.test.ts @@ -0,0 +1,140 @@ +import { afterAll, beforeAll, describe, expect } from "vitest"; +import { alchemy } from "../../src/alchemy.ts"; +import { createCloudflareApi } from "../../src/cloudflare/api.ts"; +import { DOStateStore } from "../../src/cloudflare/do-state-store/index.ts"; +import { + DOStateStoreClient, + getAccountSubdomain, + upsertStateStoreWorker, +} from "../../src/cloudflare/do-state-store/internal.ts"; +import { + assertWorkerDoesNotExist, + deleteWorker, +} from "../../src/cloudflare/worker.ts"; +import { File } from "../../src/fs/file.ts"; +import "../../src/test/vitest.ts"; +import { BRANCH_PREFIX } from "../util.ts"; + +const workerName = `alchemy-state-${BRANCH_PREFIX}`; + +const api = await createCloudflareApi(); +const token = await alchemy.secret.env.ALCHEMY_STATE_TOKEN; +const subdomain = await getAccountSubdomain(api); + +const test = alchemy.test(import.meta, { + stateStore: (scope) => + new DOStateStore(scope, { + worker: { + name: workerName, + }, + }), +}); + +afterAll(async () => { + await deleteWorker(api, { workerName }); + await assertWorkerDoesNotExist(api, workerName); +}); + +describe("DOStateStore", () => { + test("should initialize with lazy worker creation", async (scope) => { + try { + await File("file", { + content: "test", + path: `/tmp/alchemy-state-store-test${BRANCH_PREFIX}`, + }); + } finally { + alchemy.destroy(scope); + } + }); + + describe("Internal", () => { + const name = `${workerName}-internal`; + + const client = new DOStateStoreClient({ + app: "alchemy", + stage: "internal", + url: `https://${name}.${subdomain}.workers.dev`, + token: token.unencrypted, + }); + + beforeAll(async () => { + await upsertStateStoreWorker(api, name, token.unencrypted, true); + await client.waitUntilReady(); + }); + + afterAll(async () => { + await deleteWorker(api, { workerName: name }); + await assertWorkerDoesNotExist(api, name); + }); + + async function expectKeys(prefix: string, keys: string[]) { + const list = await client.rpc("list", { prefix }); + expect(list).toEqual(keys); + const count = await client.rpc("count", { prefix }); + expect(count).toEqual(keys.length); + } + + function createTestState(data: any) { + return { + status: "created" as const, + kind: "test", + id: "test-id", + fqn: "test::test-id", + seq: 1, + data: {}, + props: undefined, + output: data, + }; + } + + test("get, set, list, count, delete", async () => { + const value1 = createTestState({ value: "test-data1" }); + const value2 = createTestState({ value: "test-data2" }); + await client.rpc("set", { key: "root/prefix1/key1", value: value1 }); + await client.rpc("set", { key: "root/prefix2/key2", value: value2 }); + + await expectKeys("root/prefix1", ["key1"]); + await expect( + client.rpc("get", { key: "root/prefix1/key1" }), + ).resolves.toEqual(JSON.stringify(value1)); + await expect( + client.rpc("get", { key: "root/prefix1/key2" }), + ).resolves.toBeUndefined(); + await expect( + client.rpc("all", { prefix: "root/prefix1" }), + ).resolves.toEqual({ key1: JSON.stringify(value1) }); + + await expectKeys("root/prefix2", ["key2"]); + await expect( + client.rpc("get", { key: "root/prefix2/key2" }), + ).resolves.toEqual(JSON.stringify(value2)); + await expect( + client.rpc("get", { key: "root/prefix2/key1" }), + ).resolves.toBeUndefined(); + await expect( + client.rpc("all", { prefix: "root/prefix2" }), + ).resolves.toEqual({ key2: JSON.stringify(value2) }); + + await client.rpc("delete", { key: "root/prefix1/key1" }); + await client.rpc("delete", { key: "root/prefix1" }); + await expect( + client.rpc("get", { key: "root/prefix1/key1" }), + ).resolves.toBeUndefined(); + await expect( + client.rpc("all", { prefix: "root/prefix1" }), + ).resolves.toEqual({}); + + await client.rpc("delete", { key: "root/prefix2/key2" }); + await client.rpc("delete", { key: "root/prefix2" }); + await expect( + client.rpc("get", { key: "root/prefix2/key2" }), + ).resolves.toBeUndefined(); + await expect( + client.rpc("all", { prefix: "root/prefix2" }), + ).resolves.toEqual({}); + + await expectKeys("root/prefix1", []); + await expectKeys("root/prefix2", []); + }); + }); +}); diff --git a/alchemy/test/cloudflare/durable-object.test.ts b/alchemy/test/cloudflare/durable-object.test.ts index 03009876e..9e039860a 100644 --- a/alchemy/test/cloudflare/durable-object.test.ts +++ b/alchemy/test/cloudflare/durable-object.test.ts @@ -68,6 +68,7 @@ describe("Durable Object Namespace", () => { // First create the worker without the DO binding worker = await Worker(workerName, { name: workerName, + adopt: true, script: durableObjectWorkerScript, format: "esm", // No bindings yet @@ -89,6 +90,7 @@ describe("Durable Object Namespace", () => { // Update the worker with the DO binding worker = await Worker(workerName, { name: workerName, + adopt: true, script: durableObjectWorkerScript, format: "esm", bindings: { diff --git a/alchemy/test/cloudflare/hyperdrive.test.ts b/alchemy/test/cloudflare/hyperdrive.test.ts index 4dd7afee2..8d3b17524 100644 --- a/alchemy/test/cloudflare/hyperdrive.test.ts +++ b/alchemy/test/cloudflare/hyperdrive.test.ts @@ -76,6 +76,7 @@ describe("Hyperdrive Resource", () => { const workerName = `${BRANCH_PREFIX}-hyperdrive-test-worker`; worker = await Worker(workerName, { name: workerName, + adopt: true, script: ` export default { async fetch(request, env, ctx) { diff --git a/alchemy/test/cloudflare/images.test.ts b/alchemy/test/cloudflare/images.test.ts index dfad5807b..6ff2573b1 100644 --- a/alchemy/test/cloudflare/images.test.ts +++ b/alchemy/test/cloudflare/images.test.ts @@ -21,6 +21,7 @@ describe("Images Binding", () => { try { worker = await Worker(workerName, { name: workerName, + adopt: true, entrypoint: path.join(import.meta.dirname, "images-handler.ts"), format: "esm", bindings: { diff --git a/alchemy/test/cloudflare/kv-namespace.test.ts b/alchemy/test/cloudflare/kv-namespace.test.ts index 5a3bf4be3..96a0d39fe 100644 --- a/alchemy/test/cloudflare/kv-namespace.test.ts +++ b/alchemy/test/cloudflare/kv-namespace.test.ts @@ -150,6 +150,7 @@ describe("KV Namespace Resource", () => { // Create a worker with the KV Namespace binding worker = await Worker(workerName, { name: workerName, + adopt: true, script: ` export default { async fetch(request, env, ctx) { diff --git a/alchemy/test/cloudflare/pipeline.test.ts b/alchemy/test/cloudflare/pipeline.test.ts index 9385a8466..98f095213 100644 --- a/alchemy/test/cloudflare/pipeline.test.ts +++ b/alchemy/test/cloudflare/pipeline.test.ts @@ -346,6 +346,7 @@ describe("Pipeline Resource", () => { name: bucketName, accessKey: accessKeyId, secretAccessKey: secretAccessKey, + adopt: true, delete: true, empty: true, }); @@ -386,6 +387,7 @@ describe("Pipeline Resource", () => { // Create a worker with the pipeline binding worker = await Worker(workerName, { name: workerName, + adopt: true, script: pipelineWorkerScript, format: "esm", url: true, // Enable workers.dev URL to test the worker diff --git a/alchemy/test/cloudflare/route.test.ts b/alchemy/test/cloudflare/route.test.ts index bc6efd594..659cae15b 100644 --- a/alchemy/test/cloudflare/route.test.ts +++ b/alchemy/test/cloudflare/route.test.ts @@ -617,9 +617,7 @@ describe("Route Resource", () => { ], }); - await expect(workerPromise).rejects.toThrow( - /Duplicate route patterns found/, - ); + await expect(workerPromise).rejects.toThrow(/Duplicate Route found: /); } finally { await destroy(scope); // Worker should not exist since creation failed diff --git a/alchemy/test/cloudflare/secret-key.test.ts b/alchemy/test/cloudflare/secret-key.test.ts index 68cb942a4..d76f74c78 100644 --- a/alchemy/test/cloudflare/secret-key.test.ts +++ b/alchemy/test/cloudflare/secret-key.test.ts @@ -47,6 +47,7 @@ describe("SecretKey Binding", () => { // Create a worker that accesses the JWK SecretKey properties and can sign data worker = await Worker(workerName, { name: workerName, + adopt: true, format: "esm", url: true, bindings: { diff --git a/alchemy/test/cloudflare/secret.test.ts b/alchemy/test/cloudflare/secret.test.ts index f90794abb..8e67f1772 100644 --- a/alchemy/test/cloudflare/secret.test.ts +++ b/alchemy/test/cloudflare/secret.test.ts @@ -41,6 +41,7 @@ describe("Secret Resource", () => { // Create a worker that binds to the secret and returns the secret value worker = await Worker(workerName, { name: workerName, + adopt: true, script: ` export default { async fetch(request, env, ctx) { diff --git a/alchemy/test/cloudflare/unenv.test.ts b/alchemy/test/cloudflare/unenv.test.ts index 0e2413c88..fb4c6411e 100644 --- a/alchemy/test/cloudflare/unenv.test.ts +++ b/alchemy/test/cloudflare/unenv.test.ts @@ -20,6 +20,7 @@ describe("Worker Unenv Tests", () => { // Create a temporary directory for the files // Create the worker using the entrypoint file const worker = await Worker(`${BRANCH_PREFIX}-test-worker-unenv`, { + adopt: true, entrypoint: path.join(import.meta.dirname, "unenv-handler.ts"), format: "esm", url: true, // Enable workers.dev URL to test the worker diff --git a/alchemy/test/cloudflare/version-metadata.test.ts b/alchemy/test/cloudflare/version-metadata.test.ts index 2bcdd46bf..042434edd 100644 --- a/alchemy/test/cloudflare/version-metadata.test.ts +++ b/alchemy/test/cloudflare/version-metadata.test.ts @@ -22,6 +22,7 @@ describe("VersionMetadata Binding", () => { try { worker = await Worker(workerName, { name: workerName, + adopt: true, entrypoint: path.join( import.meta.dirname, "version-metadata-handler.ts", diff --git a/alchemy/test/cloudflare/website.test.ts b/alchemy/test/cloudflare/website.test.ts new file mode 100644 index 000000000..d1c8742c1 --- /dev/null +++ b/alchemy/test/cloudflare/website.test.ts @@ -0,0 +1,212 @@ +import * as fs from "node:fs/promises"; +import * as path from "node:path"; +import { describe, expect } from "vitest"; +import { alchemy } from "../../src/alchemy.ts"; +import { Website } from "../../src/cloudflare/website.ts"; +import { destroy } from "../../src/destroy.ts"; +import { BRANCH_PREFIX } from "../util.ts"; + +import "../../src/test/vitest.ts"; + +const test = alchemy.test(import.meta, { + prefix: BRANCH_PREFIX, +}); + +describe("Website Resource", () => { + test("respects cwd property for wrangler.jsonc placement", async (scope) => { + const name = `${BRANCH_PREFIX}-test-website-cwd`; + const tempDir = path.resolve(".out", "alchemy-website-cwd-test"); + const subDir = path.resolve(tempDir, "subproject"); + const distDir = path.resolve(subDir, "dist"); + const entrypoint = path.resolve(subDir, "worker.ts"); + + try { + // Create temporary directory structure + await fs.rm(tempDir, { recursive: true, force: true }); + await fs.mkdir(subDir, { recursive: true }); + await fs.mkdir(distDir, { recursive: true }); + + // Create a simple index.html in the dist directory + await fs.writeFile( + path.join(distDir, "index.html"), + "Hello Website!", + ); + + // Create a simple worker entrypoint + await fs.writeFile( + entrypoint, + `export default { + async fetch(request, env) { + return new Response("Hello from worker!"); + } + };`, + ); + + // Create website with cwd pointing to subdirectory + const website = await Website(name, { + cwd: subDir, + main: entrypoint, + assets: distDir, // Use absolute path for assets + wrangler: true, + adopt: true, + }); + + // Verify the website was created successfully + expect(website.name).toBe(name); + expect(website.url).toBeDefined(); + + // Verify wrangler.jsonc was created in the correct location (subDir) + const wranglerPath = path.join(subDir, "wrangler.jsonc"); + const wranglerExists = await fs + .access(wranglerPath) + .then(() => true) + .catch(() => false); + + expect(wranglerExists).toBe(true); + + // Verify wrangler.jsonc was NOT created in the root tempDir + const rootWranglerPath = path.join(tempDir, "wrangler.jsonc"); + const rootWranglerExists = await fs + .access(rootWranglerPath) + .then(() => true) + .catch(() => false); + + expect(rootWranglerExists).toBe(false); + + // Verify the contents of wrangler.jsonc + const wranglerContent = await fs.readFile(wranglerPath, "utf-8"); + const wranglerJson = JSON.parse(wranglerContent); + + expect(wranglerJson.name).toBe(name); + expect(wranglerJson.assets).toMatchObject({ + binding: "ASSETS", + directory: expect.stringContaining("dist"), + }); + } finally { + await fs.rm(tempDir, { recursive: true, force: true }); + await destroy(scope); + } + }); + + test("respects cwd property with custom wrangler path", async (scope) => { + const name = `${BRANCH_PREFIX}-test-website-cwd-custom`; + const tempDir = path.resolve(".out", "alchemy-website-cwd-custom-test"); + const subDir = path.join(tempDir, "myproject"); + const distDir = path.join(subDir, "dist"); + const entrypoint = path.join(subDir, "worker.ts"); + + try { + // Create temporary directory structure + await fs.rm(tempDir, { recursive: true, force: true }); + await fs.mkdir(subDir, { recursive: true }); + await fs.mkdir(distDir, { recursive: true }); + + // Create a simple index.html in the dist directory + await fs.writeFile( + path.join(distDir, "index.html"), + "Hello Custom Website!", + ); + + // Create a simple worker entrypoint + await fs.writeFile( + entrypoint, + `export default { + async fetch(request, env) { + return new Response("Hello from custom worker!"); + } + };`, + ); + + // Create website with cwd and custom wrangler filename + const website = await Website(name, { + cwd: subDir, + main: entrypoint, + assets: path.resolve(distDir), // Use absolute path for assets + wrangler: "custom-wrangler.json", + adopt: true, + }); + + // Verify the website was created successfully + expect(website.name).toBe(name); + + // Verify custom wrangler file was created in the correct location (subDir) + const wranglerPath = path.join(subDir, "custom-wrangler.json"); + const wranglerExists = await fs + .access(wranglerPath) + .then(() => true) + .catch(() => false); + + expect(wranglerExists).toBe(true); + + // Verify custom wrangler file was NOT created in the root tempDir + const rootWranglerPath = path.join(tempDir, "custom-wrangler.json"); + const rootWranglerExists = await fs + .access(rootWranglerPath) + .then(() => true) + .catch(() => false); + + expect(rootWranglerExists).toBe(false); + } finally { + await fs.rm(tempDir, { recursive: true, force: true }); + await destroy(scope); + } + }); + + test("places wrangler.jsonc in project root when no cwd specified", async (scope) => { + const name = `${BRANCH_PREFIX}-test-website-default-cwd`; + const tempDir = path.join(".out", "alchemy-website-default-test"); + const distDir = path.join(tempDir, "dist"); + const entrypoint = path.join(tempDir, "worker.ts"); + + try { + // Create temporary directory structure + await fs.rm(tempDir, { recursive: true, force: true }); + await fs.mkdir(distDir, { recursive: true }); + + // Create a simple index.html in the dist directory + await fs.writeFile( + path.join(distDir, "index.html"), + "Hello Default Website!", + ); + + // Create a simple worker entrypoint + await fs.writeFile( + entrypoint, + `export default { + async fetch(request, env) { + return new Response("Hello from default worker!"); + } + };`, + ); + + // Create website without specifying cwd (should use process.cwd()) + const website = await Website(name, { + main: entrypoint, + assets: path.join(tempDir, "dist"), // Use absolute path since no cwd specified + wrangler: true, + adopt: true, + }); + + // Verify the website was created successfully + expect(website.name).toBe(name); + + // Verify wrangler.jsonc was created in the current working directory (project root) + // Since we didn't specify cwd, it should be placed relative to process.cwd() + const wranglerPath = path.join(process.cwd(), "wrangler.jsonc"); + const wranglerExists = await fs + .access(wranglerPath) + .then(() => true) + .catch(() => false); + + expect(wranglerExists).toBe(true); + + // Clean up the wrangler.jsonc file in the project root + if (wranglerExists) { + await fs.rm(wranglerPath, { force: true }); + } + } finally { + await fs.rm(tempDir, { recursive: true, force: true }); + await destroy(scope); + } + }); +}); diff --git a/alchemy/test/cloudflare/worker-stub.test.ts b/alchemy/test/cloudflare/worker-stub.test.ts index 421fa9e22..d491ecedd 100644 --- a/alchemy/test/cloudflare/worker-stub.test.ts +++ b/alchemy/test/cloudflare/worker-stub.test.ts @@ -15,6 +15,7 @@ test("worker-stub-types", () => { const workerA = await Worker("workerA", { entrypoint: "index.ts", name: "workerA", + adopt: true, bindings: { workerB: await WorkerStub("workerB", { name: "workerB", @@ -25,6 +26,7 @@ test("worker-stub-types", () => { const _workerB = await Worker("workerB", { entrypoint: "index.ts", name: "workerB", + adopt: true, bindings: { workerA, }, @@ -39,3 +41,32 @@ test("worker-stub-types", () => { env.workerB.bar(); } }); + +test("worker-stub-url-types", () => { + // type-only test for URL functionality + async function _() { + // Test with URL enabled (default) + const stubWithUrl = await WorkerStub("stub-with-url", { + name: "stub-with-url", + }); + + // Should have URL property + const _url1: string | undefined = stubWithUrl.url; + + // Test with URL explicitly enabled + const stubExplicitUrl = await WorkerStub("stub-explicit-url", { + name: "stub-explicit-url", + url: true, + }); + + const _url2: string | undefined = stubExplicitUrl.url; + + // Test with URL disabled + const stubNoUrl = await WorkerStub("stub-no-url", { + name: "stub-no-url", + url: false, + }); + + const _url3: string | undefined = stubNoUrl.url; + } +}); diff --git a/alchemy/test/cloudflare/worker.test.ts b/alchemy/test/cloudflare/worker.test.ts index 7bd1c0374..79cc3b6a9 100644 --- a/alchemy/test/cloudflare/worker.test.ts +++ b/alchemy/test/cloudflare/worker.test.ts @@ -8,7 +8,12 @@ import { Assets } from "../../src/cloudflare/assets.ts"; import { Self } from "../../src/cloudflare/bindings.ts"; import { DurableObjectNamespace } from "../../src/cloudflare/durable-object-namespace.ts"; import { KVNamespace } from "../../src/cloudflare/kv-namespace.ts"; -import { Worker, WorkerRef } from "../../src/cloudflare/worker.ts"; +import type { SingleStepMigration } from "../../src/cloudflare/worker-migration.ts"; +import { + deleteWorker, + Worker, + WorkerRef, +} from "../../src/cloudflare/worker.ts"; import { destroy } from "../../src/destroy.ts"; import { BRANCH_PREFIX } from "../util.ts"; import { @@ -48,6 +53,7 @@ describe("Worker Resource", () => { // Create a worker with an explicit name worker = await Worker(workerName, { name: workerName, + adopt: true, script: ` addEventListener('fetch', event => { event.respondWith(new Response('Hello world!', { status: 200 })); @@ -70,6 +76,7 @@ describe("Worker Resource", () => { worker = await Worker(workerName, { name: workerName, + adopt: true, script: updatedScript, format: "cjs", }); @@ -116,6 +123,7 @@ describe("Worker Resource", () => { worker = await Worker(workerName, { name: workerName, + adopt: true, script: updatedEsmScript, format: "esm", }); @@ -135,6 +143,7 @@ describe("Worker Resource", () => { // First create with ESM format worker = await Worker(workerName, { name: workerName, + adopt: true, script: ` export default { async fetch(request, env, ctx) { @@ -150,6 +159,7 @@ describe("Worker Resource", () => { // Update to CJS format worker = await Worker(workerName, { name: workerName, + adopt: true, script: ` addEventListener('fetch', event => { event.respondWith(new Response('Hello world!', { status: 200 })); @@ -267,6 +277,7 @@ describe("Worker Resource", () => { // Create a KV namespace const testKv = await KVNamespace("test-kv-namespace", { title: `${BRANCH_PREFIX} Test KV Namespace 1`, + adopt: true, values: [ { key: "testKey", @@ -283,6 +294,7 @@ describe("Worker Resource", () => { name: workerName, script: multiBindingsWorkerScript, format: "esm", + adopt: true, }); expect(worker.id).toBeTruthy(); @@ -298,6 +310,7 @@ describe("Worker Resource", () => { TEST_KV: testKv, API_KEY: "test-api-key-value", }, + adopt: true, }); expect(worker.id).toBeTruthy(); @@ -358,6 +371,7 @@ describe("Worker Resource", () => { APP_DEBUG: "true", }, url: true, // Enable workers.dev URL to test the worker + adopt: true, }); expect(worker.id).toBeTruthy(); @@ -394,6 +408,7 @@ describe("Worker Resource", () => { NEW_VAR: "new-value", }, url: true, + adopt: true, }); await new Promise((resolve) => setTimeout(resolve, 1000)); @@ -492,6 +507,7 @@ describe("Worker Resource", () => { bindings: { ASSETS: assets, }, + adopt: true, }); await new Promise((resolve) => setTimeout(resolve, 1000)); @@ -1323,6 +1339,7 @@ describe("Worker Resource", () => { await Worker("worker1", { name: workerName, script: `export default { async fetch(request, env, ctx) { return new Response('Hello, world!'); } };`, + adopt: true, }); const worker2 = await Worker("worker1", { @@ -1452,7 +1469,6 @@ describe("Worker Resource", () => { version: versionLabel, }); expect(versionWorker.id).toBeTruthy(); - expect(versionWorker.url).toBeTruthy(); expect(versionWorker.bindings?.ASSETS).toBeDefined(); @@ -1474,4 +1490,105 @@ describe("Worker Resource", () => { await assertWorkerDoesNotExist(workerName); } }); + + test("adopt worker with existing migration tag", async (scope) => { + const workerName = `${BRANCH_PREFIX}-test-worker-adopt-migration`; + + try { + await deleteWorker(api, { + workerName, + }); + + const formData = new FormData(); + formData.append( + "worker.js", + ` + export class MyDO {} + export default { + async fetch(request, env, ctx) { + return new Response('Hello from migrated worker!', { status: 200 }); + } + }; + `, + ); + formData.append( + "metadata", + new Blob([ + JSON.stringify({ + compatibility_date: "2025-05-18", + bindings: [ + { + type: "durable_object_namespace", + class_name: "MyDO", + name: "MY_DO", + }, + ], + observability: { + enabled: true, + }, + main_module: "worker.js", + migrations: { + new_tag: "v1", + old_tag: undefined, + new_classes: ["MyDO"], + deleted_classes: [], + renamed_classes: [], + transferred_classes: [], + new_sqlite_classes: [], + } satisfies SingleStepMigration, + }), + ]), + ); + + // Put the worker with migration tag v1 + await api.post( + `/accounts/${api.accountId}/workers/scripts/${workerName}/versions`, + formData, + ); + + // Now adopt the worker using the Worker resource + await Worker(workerName, { + name: workerName, + adopt: true, + script: ` + export class MyDO2 {} + export default { + async fetch(request, env, ctx) { + return new Response('Hello from adopted worker with migration!', { status: 200 }); + } + }; + `, + format: "esm", + bindings: { + MY_DO: new DurableObjectNamespace("test-counter-migration", { + className: "MyDO2", + scriptName: workerName, + }), + }, + }); + + await Worker(workerName, { + name: workerName, + adopt: true, + script: ` + export class MyDO3 {} + export default { + async fetch(request, env, ctx) { + return new Response('Hello from adopted worker with migration!', { status: 200 }); + } + }; + `, + format: "esm", + bindings: { + MY_DO: new DurableObjectNamespace("test-counter-migration", { + className: "MyDO3", + scriptName: workerName, + }), + }, + }); + } finally { + await destroy(scope); + await assertWorkerDoesNotExist(workerName); + } + }); }); diff --git a/alchemy/test/cloudflare/workflow.test.ts b/alchemy/test/cloudflare/workflow.test.ts index 8afe9a955..9ba70a16e 100644 --- a/alchemy/test/cloudflare/workflow.test.ts +++ b/alchemy/test/cloudflare/workflow.test.ts @@ -207,6 +207,7 @@ describe("Workflow", () => { // Create a worker with the workflow binding worker = await Worker(workerName, { name: workerName, + adopt: true, script: workflowWorkerScript, format: "esm", bindings: { @@ -250,6 +251,7 @@ describe("Workflow", () => { // Update the worker with multiple workflow bindings worker = await Worker(workerName, { name: workerName, + adopt: true, script: workflowWorkerScript, format: "esm", bindings: { @@ -317,6 +319,7 @@ describe("Workflow", () => { // First create the worker that defines the workflow with its own binding workflowProviderWorker = await Worker(workflowWorkerName, { name: workflowWorkerName, + adopt: true, script: workflowProviderWorkerScript, format: "esm", url: true, // Enable workers.dev URL @@ -339,6 +342,7 @@ describe("Workflow", () => { // Create the client worker with the cross-script workflow binding clientWorker = await Worker(clientWorkerName, { name: clientWorkerName, + adopt: true, script: clientWorkerScript, format: "esm", url: true, // Enable workers.dev URL @@ -434,6 +438,7 @@ describe("Workflow", () => { // First create the worker that defines the workflow with its own binding workflowProviderWorker = await Worker(workflowWorkerName, { name: workflowWorkerName, + adopt: true, script: workflowProviderWorkerScript, format: "esm", url: true, // Enable workers.dev URL @@ -463,6 +468,7 @@ describe("Workflow", () => { // Create the client worker with the cross-script workflow binding using re-exported syntax clientWorker = await Worker(clientWorkerName, { name: clientWorkerName, + adopt: true, script: clientWorkerScript, format: "esm", url: true, // Enable workers.dev URL diff --git a/alchemy/test/cloudflare/wrangler-json.test.ts b/alchemy/test/cloudflare/wrangler-json.test.ts index 9abbda836..7e52f503e 100644 --- a/alchemy/test/cloudflare/wrangler-json.test.ts +++ b/alchemy/test/cloudflare/wrangler-json.test.ts @@ -493,7 +493,9 @@ describe("WranglerJson Resource", () => { await fs.mkdir(tempDir, { recursive: true }); await fs.writeFile(entrypoint, esmWorkerScript); - const r2Bucket = await R2Bucket(`${BRANCH_PREFIX}-test-r2-bucket`); + const r2Bucket = await R2Bucket(`${BRANCH_PREFIX}-test-r2-bucket`, { + adopt: true, + }); const worker = await Worker(name, { format: "esm", diff --git a/alchemy/test/cloudflare/zone.test.ts b/alchemy/test/cloudflare/zone.test.ts index 56bb2f8ee..793d3970a 100644 --- a/alchemy/test/cloudflare/zone.test.ts +++ b/alchemy/test/cloudflare/zone.test.ts @@ -172,7 +172,7 @@ describe("Zone Resource", () => { expect(zone.name).toEqual(lookupTestDomain); // Use getZoneByDomain to look up the zone we just created - const foundZone = await getZoneByDomain(lookupTestDomain); + const foundZone = await getZoneByDomain(api, lookupTestDomain); // Verify the lookup returned the correct zone expect(foundZone).toBeTruthy(); @@ -186,6 +186,7 @@ describe("Zone Resource", () => { // Test lookup of non-existent domain const nonExistentZone = await getZoneByDomain( + api, `${BRANCH_PREFIX}-non-existent.dev`, ); expect(nonExistentZone).toBeNull(); diff --git a/alchemy/test/create.test.ts b/alchemy/test/create.test.ts index 47d8000ae..8661624c1 100644 --- a/alchemy/test/create.test.ts +++ b/alchemy/test/create.test.ts @@ -1,10 +1,10 @@ import "../src/test/vitest.ts"; +import { execSync } from "node:child_process"; import fs from "node:fs/promises"; import path from "node:path"; import url from "node:url"; import { describe, expect, test } from "vitest"; -import { exec } from "../src/os/exec.ts"; // Get the root directory of the project const __filename = url.fileURLToPath(import.meta.url); @@ -20,11 +20,18 @@ async function runCommand( console.log(`Running: ${command} in ${cwd}`); try { - await exec(command, { cwd, stdio: "inherit", env }); - return { stdout: "", stderr: "" }; + const result = execSync(command, { + cwd, + env: { + ...process.env, + ...env, + }, + }); + return { stdout: result.toString(), stderr: "" }; } catch (error: any) { console.error(`Command failed: ${command}`); - console.error(`Error: ${error.message}`); + console.error(error.stdout.toString()); + console.error(error.stderr.toString()); throw error; } } diff --git a/alchemy/test/docker/api.test.ts b/alchemy/test/docker/api.test.ts new file mode 100644 index 000000000..1dd2437a3 --- /dev/null +++ b/alchemy/test/docker/api.test.ts @@ -0,0 +1,57 @@ +import { describe, expect } from "vitest"; +import { alchemy } from "../../src/alchemy.ts"; +import { DockerApi } from "../../src/docker/api.ts"; +import { BRANCH_PREFIX } from "../util.ts"; + +import "../../src/test/vitest.ts"; + +const test = alchemy.test(import.meta, { + prefix: BRANCH_PREFIX, +}); + +describe("DockerApi", () => { + test("should initialize with default docker path", async (scope) => { + try { + const dockerApi = new DockerApi(); + expect(dockerApi.dockerPath).toBe("docker"); + } finally { + await alchemy.destroy(scope); + } + }); + + test("should initialize with custom docker path", async (scope) => { + try { + const dockerApi = new DockerApi({ dockerPath: "/usr/local/bin/docker" }); + expect(dockerApi.dockerPath).toBe("/usr/local/bin/docker"); + } finally { + await alchemy.destroy(scope); + } + }); + + test("should execute docker command", async (scope) => { + try { + const dockerApi = new DockerApi(); + const result = await dockerApi.exec(["--version"]); + + expect(result).toHaveProperty("stdout"); + expect(result).toHaveProperty("stderr"); + // Docker version output should contain the word "Docker" + expect(result.stdout.includes("Docker")).toBe(true); + } finally { + await alchemy.destroy(scope); + } + }); + + test("should check if docker daemon is running", async (scope) => { + try { + const dockerApi = new DockerApi(); + const isRunning = await dockerApi.isRunning(); + + // This might be true or false depending on whether Docker is installed and running + // Just ensure it returns a boolean + expect(typeof isRunning).toBe("boolean"); + } finally { + await alchemy.destroy(scope); + } + }); +}); diff --git a/alchemy/test/docker/container.test.ts b/alchemy/test/docker/container.test.ts new file mode 100644 index 000000000..d16c693b6 --- /dev/null +++ b/alchemy/test/docker/container.test.ts @@ -0,0 +1,28 @@ +import { describe, expect } from "vitest"; +import { alchemy } from "../../src/alchemy.ts"; +import { Container } from "../../src/docker/container.ts"; +import { BRANCH_PREFIX } from "../util.ts"; + +import "../../src/test/vitest.ts"; + +const test = alchemy.test(import.meta, { + prefix: BRANCH_PREFIX, +}); + +describe("Container", () => { + test("should create a container without starting it", async (scope) => { + try { + // Create a container without starting it to avoid port conflicts + const container = await Container("test-container", { + image: "hello-world:latest", + name: "alchemy-test-container", + start: false, + }); + + expect(container.name).toBe("alchemy-test-container"); + expect(container.state).toBe("created"); + } finally { + await alchemy.destroy(scope); + } + }); +}); diff --git a/alchemy/test/docker/fixtures/build-args/Dockerfile b/alchemy/test/docker/fixtures/build-args/Dockerfile new file mode 100644 index 000000000..529468a95 --- /dev/null +++ b/alchemy/test/docker/fixtures/build-args/Dockerfile @@ -0,0 +1,11 @@ +FROM alpine:latest + +# Define build arguments +ARG MESSAGE=default +ARG VERSION=1.0 + +# Use the build arguments +RUN echo "$MESSAGE (version $VERSION)" > /message.txt + +# Display the content when container runs +CMD ["cat", "/message.txt"] diff --git a/alchemy/test/docker/fixtures/multi-stage/Dockerfile b/alchemy/test/docker/fixtures/multi-stage/Dockerfile new file mode 100644 index 000000000..df79b8bcb --- /dev/null +++ b/alchemy/test/docker/fixtures/multi-stage/Dockerfile @@ -0,0 +1,18 @@ +# Stage 1: Build stage +FROM node:16-alpine AS builder + +WORKDIR /app + +# Create a simple index.js file +RUN echo 'console.log("Hello from Alchemy multi-stage build");' > index.js + +# Stage 2: Production stage +FROM node:16-alpine AS production + +WORKDIR /app + +# Copy only what we need from the builder stage +COPY --from=builder /app/index.js . + +# Set the command +CMD ["node", "index.js"] diff --git a/alchemy/test/docker/fixtures/simple-image/Dockerfile b/alchemy/test/docker/fixtures/simple-image/Dockerfile new file mode 100644 index 000000000..4cf68f4de --- /dev/null +++ b/alchemy/test/docker/fixtures/simple-image/Dockerfile @@ -0,0 +1,2 @@ +FROM alpine:latest +CMD ["echo", "Hello from Alchemy!"] diff --git a/alchemy/test/docker/image.test.ts b/alchemy/test/docker/image.test.ts new file mode 100644 index 000000000..50050af18 --- /dev/null +++ b/alchemy/test/docker/image.test.ts @@ -0,0 +1,134 @@ +import fs from "node:fs"; +import path from "node:path"; +import { describe, expect } from "vitest"; +import { alchemy } from "../../src/alchemy.ts"; +import { Image } from "../../src/docker/image.ts"; +import { BRANCH_PREFIX } from "../util.ts"; + +import "../../src/test/vitest.ts"; + +const test = alchemy.test(import.meta, { + prefix: BRANCH_PREFIX, +}); + +// Helper function to get the absolute path to a fixture +function getFixturePath(fixtureName: string): string { + return path.join( + process.cwd(), + "alchemy", + "test", + "docker", + "fixtures", + fixtureName, + ); +} + +describe("Image", () => { + test("should build a simple image", async (scope) => { + try { + const contextPath = getFixturePath("simple-image"); + + // Ensure the context path exists + expect(fs.existsSync(contextPath)).toBe(true); + expect(fs.existsSync(path.join(contextPath, "Dockerfile"))).toBe(true); + + const image = await Image("test-simple-image", { + name: "alchemy-test", + tag: "simple", + build: { + context: contextPath, + }, + skipPush: true, + }); + + expect(image.name).toBe("alchemy-test"); + expect(image.tag).toBe("simple"); + expect(image.imageRef).toBe("alchemy-test:simple"); + expect(image.build?.context).toBe(contextPath); + // imageId might not be available in a CI environment where Docker is not running + if (image.imageId) { + expect(image.imageId.length).toBeGreaterThan(0); + } + } finally { + await alchemy.destroy(scope); + } + }); + + test("should handle build arguments", async (scope) => { + try { + const contextPath = getFixturePath("build-args"); + + // Ensure the context path exists + expect(fs.existsSync(contextPath)).toBe(true); + expect(fs.existsSync(path.join(contextPath, "Dockerfile"))).toBe(true); + + const image = await Image("test-build-args", { + name: "alchemy-test", + tag: "args", + build: { + context: contextPath, + args: { + MESSAGE: "Hello from Alchemy", + VERSION: "2.0", + }, + }, + skipPush: true, + }); + + expect(image.name).toBe("alchemy-test"); + expect(image.tag).toBe("args"); + expect(image.build?.args).toEqual({ + MESSAGE: "Hello from Alchemy", + VERSION: "2.0", + }); + } finally { + await alchemy.destroy(scope); + } + }); + + test("should support multi-stage builds with target", async (scope) => { + try { + const contextPath = getFixturePath("multi-stage"); + + // Ensure the context path exists + expect(fs.existsSync(contextPath)).toBe(true); + expect(fs.existsSync(path.join(contextPath, "Dockerfile"))).toBe(true); + + const image = await Image("test-multi-stage", { + name: "alchemy-test", + tag: "multi", + build: { + context: contextPath, + target: "builder", // Target the builder stage + }, + skipPush: true, + }); + + expect(image.name).toBe("alchemy-test"); + expect(image.tag).toBe("multi"); + expect(image.build?.target).toBe("builder"); + } finally { + await alchemy.destroy(scope); + } + }); + + test("should handle invalid context path", async (scope) => { + try { + await Image("test-invalid-context", { + name: "alchemy-test", + tag: "invalid", + build: { + context: "/non/existent/path", + }, + skipPush: true, + }); + } catch (error) { + expect(error).toBeInstanceOf(Error); + expect((error as Error).message).toBe( + "ENOENT: no such file or directory, access '/non/existent/path'", + ); + } finally { + await alchemy.destroy(scope); + } + }); +}); diff --git a/alchemy/test/docker/network.test.ts b/alchemy/test/docker/network.test.ts new file mode 100644 index 000000000..00d2a767e --- /dev/null +++ b/alchemy/test/docker/network.test.ts @@ -0,0 +1,26 @@ +import { describe, expect } from "vitest"; +import { alchemy } from "../../src/alchemy.ts"; +import { Network } from "../../src/docker/network.ts"; +import { BRANCH_PREFIX } from "../util.ts"; + +import "../../src/test/vitest.ts"; + +const test = alchemy.test(import.meta, { + prefix: BRANCH_PREFIX, +}); + +describe("Network", () => { + test("should create a test network with default driver", async (scope) => { + try { + const networkName = `alchemy-test-network-${Date.now()}`; + const network = await Network("test-network", { + name: networkName, + }); + + expect(network.name).toBe(networkName); + expect(network.driver).toBe("bridge"); // default value + } finally { + await alchemy.destroy(scope); + } + }); +}); diff --git a/alchemy/test/docker/remote-image.test.ts b/alchemy/test/docker/remote-image.test.ts new file mode 100644 index 000000000..77d061776 --- /dev/null +++ b/alchemy/test/docker/remote-image.test.ts @@ -0,0 +1,43 @@ +import { describe, expect } from "vitest"; +import { alchemy } from "../../src/alchemy.ts"; +import { RemoteImage } from "../../src/docker/remote-image.ts"; +import { BRANCH_PREFIX } from "../util.ts"; + +import "../../src/test/vitest.ts"; + +const test = alchemy.test(import.meta, { + prefix: BRANCH_PREFIX, +}); + +describe("RemoteImage", () => { + test("should pull a small test image", async (scope) => { + try { + // Use a small test image to avoid long download times + const image = await RemoteImage("hello-world-image", { + name: "hello-world", + tag: "latest", + }); + + expect(image.name).toBe("hello-world"); + expect(image.tag).toBe("latest"); + expect(image.imageRef).toBe("hello-world:latest"); + } finally { + await alchemy.destroy(scope); + } + }); + + test("should fail when using a non-existent tag", async (scope) => { + try { + await expect( + RemoteImage("non-existent-image", { + name: "non-existent", + tag: "test-tag-123", + }), + ).rejects.toThrow( + "Command failed with exit code 1: Error response from daemon: pull access denied for non-existent, repository does not exist or may require 'docker login'", + ); + } finally { + await alchemy.destroy(scope); + } + }); +}); diff --git a/alchemy/test/docker/volume.test.ts b/alchemy/test/docker/volume.test.ts new file mode 100644 index 000000000..3f7568ac1 --- /dev/null +++ b/alchemy/test/docker/volume.test.ts @@ -0,0 +1,93 @@ +import { describe, expect } from "vitest"; +import { alchemy } from "../../src/alchemy.ts"; +import { Volume } from "../../src/docker/volume.ts"; +import { BRANCH_PREFIX } from "../util.ts"; + +import "../../src/test/vitest.ts"; + +const test = alchemy.test(import.meta, { + prefix: BRANCH_PREFIX, +}); + +describe("Volume", () => { + test("should create a volume with default driver", async (scope) => { + try { + const volumeName = `alchemy-test-volume-${Date.now()}`; + const volume = await Volume("test-volume", { + name: volumeName, + }); + + expect(volume.name).toBe(volumeName); + expect(volume.driver).toBe("local"); // default value + expect(volume.id).toBe(volumeName); // volume ID is same as name + } finally { + await alchemy.destroy(scope); + } + }); + + test("should create a volume with custom driver and options", async (scope) => { + try { + const volumeName = `alchemy-test-volume-custom-${Date.now()}`; + const volume = await Volume("test-volume-custom", { + name: volumeName, + driver: "local", + driverOpts: { + type: "tmpfs", + device: "tmpfs", + o: "size=100m,uid=1000", + }, + }); + + expect(volume.name).toBe(volumeName); + expect(volume.driver).toBe("local"); + expect(volume.driverOpts).toEqual({ + type: "tmpfs", + device: "tmpfs", + o: "size=100m,uid=1000", + }); + } finally { + await alchemy.destroy(scope); + } + }); + + test("should create a volume with labels", async (scope) => { + try { + const volumeName = `alchemy-test-volume-labels-${Date.now()}`; + const volume = await Volume("test-volume-labels", { + name: volumeName, + labels: [ + { name: "com.example.usage", value: "test-volume" }, + { name: "com.example.created-by", value: "alchemy-tests" }, + ], + }); + + expect(volume.name).toBe(volumeName); + expect(volume.labels).toEqual([ + { name: "com.example.usage", value: "test-volume" }, + { name: "com.example.created-by", value: "alchemy-tests" }, + ]); + } finally { + await alchemy.destroy(scope); + } + }); + + test("should create a volume with labels as record", async (scope) => { + try { + const volumeName = `alchemy-test-volume-record-labels-${Date.now()}`; + const volume = await Volume("test-volume-record-labels", { + name: volumeName, + labels: { + "com.example.usage": "test-volume", + "com.example.created-by": "alchemy-tests", + }, + }); + + expect(volume.name).toBe(volumeName); + // When we pass labels as a record, it should be processed correctly internally + // but not reflected in the output since we're preserving the input format + expect(volume.labels).toBeUndefined(); + } finally { + await alchemy.destroy(scope); + } + }); +}); diff --git a/alchemy/test/github/comment.test.ts b/alchemy/test/github/comment.test.ts index b21046f89..65fa11a8d 100644 --- a/alchemy/test/github/comment.test.ts +++ b/alchemy/test/github/comment.test.ts @@ -16,7 +16,7 @@ const owner = process.env.GITHUB_OWNER || "sam-goodwin"; const repository = process.env.GITHUB_REPO || "test-alchemy-resources"; describe("GitHubComment Resource", { concurrent: false }, () => { - test.skipIf(!!process.env.CI)( + test.skipIf(true)( "create, update, and preserve comment on issue", async (scope) => { // Create an authenticated client for testing diff --git a/alchemy/test/github/repository-webhook.test.ts b/alchemy/test/github/repository-webhook.test.ts new file mode 100644 index 000000000..f530f5315 --- /dev/null +++ b/alchemy/test/github/repository-webhook.test.ts @@ -0,0 +1,152 @@ +import { describe, expect } from "vitest"; +import { alchemy } from "../../src/alchemy.ts"; +import { destroy } from "../../src/destroy.ts"; +import { createGitHubClient } from "../../src/github/client.ts"; +import { RepositoryWebhook } from "../../src/github/repository-webhook.ts"; +import "../../src/test/vitest.ts"; +import { BRANCH_PREFIX } from "../util.ts"; + +const test = alchemy.test(import.meta, { + prefix: BRANCH_PREFIX, +}); + +const testOwner = "sam-goodwin"; +const testRepo = "test-alchemy-resources"; + +describe("RepositoryWebhook Resource", () => { + const testWebhookId = `${BRANCH_PREFIX}-webhook`; + + test("create, update, and delete webhook", async (scope) => { + let webhook: RepositoryWebhook | undefined; + const octokit = await createGitHubClient(); + + try { + // Create a test webhook + webhook = await RepositoryWebhook(testWebhookId, { + owner: testOwner, + repository: testRepo, + url: "https://example.com/webhook", + events: ["push"], + secret: "test-secret", + contentType: "application/json", + }); + + expect(webhook.webhookId).toBeTruthy(); + expect(webhook.url).toEqual("https://example.com/webhook"); + expect(webhook.events).toEqual(["push"]); + expect(webhook.contentType).toEqual("application/json"); + + // Verify webhook was created by querying the GitHub API directly + const { data: webhooks } = await octokit.rest.repos.listWebhooks({ + owner: testOwner, + repo: testRepo, + }); + + const createdWebhook = webhooks.find((w) => w.id === webhook!.webhookId); + expect(createdWebhook).toBeTruthy(); + expect(createdWebhook!.config.url).toEqual("https://example.com/webhook"); + + // Update the webhook + webhook = await RepositoryWebhook(testWebhookId, { + owner: testOwner, + repository: testRepo, + url: "https://updated.example.com/webhook", + events: ["push", "pull_request"], + secret: "updated-secret", + contentType: "application/x-www-form-urlencoded", + }); + + expect(webhook.webhookId).toEqual(webhook.webhookId); + expect(webhook.url).toEqual("https://updated.example.com/webhook"); + expect(webhook.events).toEqual(["push", "pull_request"]); + expect(webhook.contentType).toEqual("application/x-www-form-urlencoded"); + + // Verify webhook was updated + const { data: updatedWebhooks } = await octokit.rest.repos.listWebhooks({ + owner: testOwner, + repo: testRepo, + }); + + const updatedWebhook = updatedWebhooks.find( + (w) => w.id === webhook!.webhookId, + ); + expect(updatedWebhook).toBeTruthy(); + expect(updatedWebhook!.config.url).toEqual( + "https://updated.example.com/webhook", + ); + expect(updatedWebhook!.config.content_type).toEqual( + "application/x-www-form-urlencoded", + ); + } catch (err) { + // log the error or else it's silently swallowed by destroy errors + console.log(err); + throw err; + } finally { + // Always clean up, even if test assertions fail + await destroy(scope); + + // Verify webhook was deleted + if (webhook?.webhookId) { + const { data: finalWebhooks } = await octokit.rest.repos.listWebhooks({ + owner: testOwner, + repo: testRepo, + }); + + const deletedWebhook = finalWebhooks.find( + (w) => w.id === webhook!.webhookId, + ); + expect(deletedWebhook).toBeFalsy(); + } + } + }); + + test("create webhook with minimal configuration", async (scope) => { + let webhook: RepositoryWebhook | undefined; + + try { + // Create a webhook with minimal configuration + webhook = await RepositoryWebhook(`${testWebhookId}-minimal`, { + owner: testOwner, + repository: testRepo, + url: "https://minimal.example.com/webhook", + }); + + expect(webhook.webhookId).toBeTruthy(); + expect(webhook.url).toEqual("https://minimal.example.com/webhook"); + expect(webhook.events).toEqual(["push"]); // Default events + expect(webhook.contentType).toEqual("application/json"); // Default content type + } catch (err) { + console.log(err); + throw err; + } finally { + await destroy(scope); + } + }); + + test("create webhook for all events", async (scope) => { + let webhook: RepositoryWebhook | undefined; + + try { + // Create a webhook that listens to all events + webhook = await RepositoryWebhook(`${testWebhookId}-all-events`, { + owner: testOwner, + repository: testRepo, + url: "https://all-events.example.com/webhook", + events: ["*"], + insecureSsl: true, + active: false, + }); + + expect(webhook.webhookId).toBeTruthy(); + expect(webhook.url).toEqual("https://all-events.example.com/webhook"); + expect(webhook.events).toEqual(["*"]); + expect(webhook.insecureSsl).toBeTruthy(); + expect(webhook.active).toBeFalsy(); + } catch (err) { + console.log(err); + throw err; + } finally { + await destroy(scope); + } + }); +}); diff --git a/alchemy/test/replace.test.ts b/alchemy/test/replace.test.ts new file mode 100644 index 000000000..963eca3d7 --- /dev/null +++ b/alchemy/test/replace.test.ts @@ -0,0 +1,346 @@ +import { describe, expect, test as vitestTest } from "vitest"; +import { alchemy } from "../src/alchemy.js"; +import type { Context } from "../src/context.js"; +import { destroy } from "../src/destroy.js"; +import { Resource, ResourceID } from "../src/resource.js"; +import type { Scope } from "../src/scope.js"; +import "../src/test/vitest.js"; +import { BRANCH_PREFIX } from "./util.js"; + +const test = alchemy.test(import.meta, { + prefix: BRANCH_PREFIX, +}); + +const deleted: string[] = []; +const failed = new Set(); + +interface Replacable extends Resource<"Replacable"> { + name: string; +} + +const Replacable = Resource( + "Replacable", + async function ( + this: Context, + _id: string, + props: { + name: string; + fail?: boolean; + child?: boolean; + replaceOnCreate?: boolean; + }, + ) { + if (props.replaceOnCreate && this.phase === "create") { + this.replace(); + } + if (this.phase === "delete") { + if (props.fail) { + if (!failed.has(props.name)) { + failed.add(props.name); + throw new Error(`Failed to delete ${props.name}`); + } + } + deleted.push(props.name); + return this.destroy(); + } + if (this.phase === "update") { + if (props.name !== this.output.name) { + this.replace(); + } + } + if (props.child) { + await Replacable("child", { + name: "child", + }); + } + return this({ + name: props.name, + }); + }, +); + +describe("Replace", () => { + test("replace should flush through to downstream resources", async (scope) => { + try { + let resource = await Replacable("replaceable", { + name: "foo-0", + }); + expect(deleted).not.toContain("foo-0"); + expect(resource.name).toBe("foo-0"); + resource = await Replacable("replaceable", { + name: "bar-0", + }); + // the output should have changed + expect(resource.name).toBe("bar-0"); + // but the resource should not have been deleted + expect(deleted).not.toContain("foo-0"); + expect(deleted).not.toContain("bar-0"); + // the state should contain a record of the replaced resource + expect(await scope.get("pendingDeletions")).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + resource: expect.objectContaining({ + [ResourceID]: "replaceable", + name: "foo-0", + }), + }), + ]), + ); + + await scope.finalize(); + + expect(await scope.get("pendingDeletions")).not.toEqual( + expect.arrayContaining([ + expect.objectContaining({ + resource: expect.objectContaining({ + [ResourceID]: "replaceable", + }), + }), + ]), + ); + + expect(deleted).toContain("foo-0"); + expect(deleted).not.toContain("bar-0"); + } finally { + await destroy(scope); + expect(deleted).toContain("bar-0"); + } + }); + + test("replace in subsequent scopes", async (scope) => { + try { + let resource = await alchemy.run("foo", () => + Replacable("replaceable", { + name: "foo-1", + }), + ); + expect(deleted).not.toContain("foo-1"); + expect(resource.name).toBe("foo-1"); + let foo: Scope; + resource = await alchemy.run("foo", (scope) => { + foo = scope; + return Replacable("replaceable", { + name: "bar-1", + }); + }); + // the output should have changed + expect(resource.name).toBe("bar-1"); + // but the resource should not have been deleted + expect(deleted).not.toContain("foo-1"); + expect(deleted).not.toContain("bar-1"); + // the state should contain a record of the replaced resource + expect(await foo!.get("pendingDeletions")).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + resource: expect.objectContaining({ + [ResourceID]: "replaceable", + name: "foo-1", + }), + }), + ]), + ); + // now, we finalize the scope + await scope.finalize(); + // the state should no longer contain the replaced record + expect(await foo!.get("pendingDeletions")).not.toEqual( + expect.arrayContaining([ + expect.objectContaining({ + resource: expect.objectContaining({ + [ResourceID]: "replaceable", + }), + }), + ]), + ); + // the delete of the replaced resource should have been flushed through + expect(deleted).toContain("foo-1"); + expect(deleted).not.toContain("bar-1"); + } finally { + await destroy(scope); + expect(deleted).toContain("bar-1"); + } + }); + + test("replace and destroy simultaneously", async (scope) => { + try { + let resource = await alchemy.run("foo", () => + Replacable("replaceable", { + name: "foo-2", + }), + ); + expect(deleted).not.toContain("foo-2"); + expect(resource.name).toBe("foo-2"); + resource = await alchemy.run("foo", () => + Replacable("replaceable", { + name: "bar-2", + }), + ); + // the output should have changed + expect(resource.name).toBe("bar-2"); + // but the resource should not have been deleted + expect(deleted).not.toContain("foo-2"); + expect(deleted).not.toContain("bar-2"); + // now, we destroy the scope that contains both replaced and replacement resources + // both must be deleted + await destroy(scope); + // the delete of the replaced resource should have been flushed through + expect(deleted).toContain("foo-2"); + expect(deleted).toContain("bar-2"); + // replaced resource should be deleted first + expect(deleted.indexOf("foo-2") < deleted.indexOf("bar-2")); + } finally { + await destroy(scope); + } + }); + + test("replaced resource should be deleted on second try if it fails the first time", async (scope) => { + try { + let resource = await alchemy.run("foo", () => + Replacable("replaceable", { + name: "foo-3", + fail: true, + }), + ); + expect(deleted).not.toContain("foo-3"); + expect(resource.name).toBe("foo-3"); + resource = await alchemy.run("foo", () => + Replacable("replaceable", { + name: "bar-3", + }), + ); + // the output should have changed + expect(resource.name).toBe("bar-3"); + // but the resource should not have been deleted + expect(deleted).not.toContain("foo-3"); + expect(deleted).not.toContain("bar-3"); + // first deletion of replaced resource will fail + try { + await scope!.finalize(); + } catch (e: any) { + expect(e.message).toBe("Failed to delete foo-3"); + } + // none of the resource are deleted + expect(deleted).not.toContain("foo-3"); + expect(deleted).not.toContain("bar-3"); + // then deletion succeeds on a re-run + await scope!.finalize(); + // foo-3 should be deleted + expect(deleted).toContain("foo-3"); + expect(deleted).not.toContain("bar-3"); + + await destroy(scope); + // replaced resource should be deleted first + expect(deleted.indexOf("foo-3") < deleted.indexOf("bar-3")); + } finally { + await destroy(scope); + } + }); + + test("when replacing a resource multiple times, all replacements should be deleted", async (scope) => { + try { + let _resource = await Replacable("replaceable", { + name: "foo-4", + }); + _resource = await Replacable("replaceable", { + name: "bar-4", + }); + _resource = await Replacable("replaceable", { + name: "baz-4", + }); + expect(await scope.get("pendingDeletions")).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + resource: expect.objectContaining({ + [ResourceID]: "replaceable", + name: "foo-4", + }), + }), + expect.objectContaining({ + resource: expect.objectContaining({ + [ResourceID]: "replaceable", + name: "bar-4", + }), + }), + ]), + ); + await scope.finalize(); + expect(deleted).toContain("foo-4"); + expect(deleted).toContain("bar-4"); + } finally { + await destroy(scope); + } + }); + + test("cannot replace a resource that has child resources", async (scope) => { + try { + await alchemy.run("foo", () => + Replacable("replaceable", { + name: "foo-4", + child: true, + }), + ); + await expect( + alchemy.run("foo", () => + Replacable("replaceable", { + name: "baz-4", + }), + ), + ).rejects.toThrow("has children and cannot be replaced."); + } finally { + await destroy(scope); + } + }); + + test("cannot replace a resource in create phase", async (scope) => { + try { + await expect( + alchemy.run("test", () => + Replacable("replaceable", { + name: "foo-5", + replaceOnCreate: true, + }), + ), + ).rejects.toThrow("cannot be replaced in create phase."); + } finally { + await destroy(scope); + } + }); + + vitestTest( + "replace should not trigger deletion of entire stage scope", + async () => { + let app: Scope; + try { + app = await alchemy("Replace-Testing", { + stage: "dev", + quiet: true, + }); + + await Replacable("foo-6", { + name: "foo-6", + }); + await Replacable("bar-6", { + name: "bar-6", + }); + await Replacable("bar-6", { + name: "baz-6", + }); + + expect(deleted).not.toContain("foo-6"); + expect(deleted).not.toContain("bar-6"); + expect(deleted).not.toContain("baz-6"); + + await app.finalize(); + + expect(deleted).not.toContain("foo-6"); + expect(deleted).toContain("bar-6"); + expect(deleted).not.toContain("baz-6"); + } finally { + await destroy(app!); + + expect(deleted).toContain("foo-6"); + expect(deleted).toContain("bar-6"); + expect(deleted).toContain("baz-6"); + } + }, + ); +}); diff --git a/alchemy/test/scope.test.ts b/alchemy/test/scope.test.ts index 380afca0f..e25a2add1 100644 --- a/alchemy/test/scope.test.ts +++ b/alchemy/test/scope.test.ts @@ -117,6 +117,37 @@ describe("Scope", () => { await destroy(scope); } }); + + test("scope CRUD operations should work", async (scope) => { + try { + const innerScope = await alchemy.run("innerScope", async (scope) => { + await scope.set("foo", "foo-1"); + expect(await scope.get("foo")).toBe("foo-1"); + await scope.delete("foo"); + expect(await scope.get("foo")).toBeUndefined(); + await scope.set("bar", "baz-1"); + return scope; + }); + expect(await innerScope.get("bar")).toBe("baz-1"); + await innerScope.delete("bar"); + expect(await innerScope.get("bar")).toBeUndefined(); + } finally { + await destroy(scope); + } + }); + + test("scope CRUD operations should work across instances of the same scope", async (scope) => { + try { + await alchemy.run("innerScope", async (scope) => { + await scope.set("foo", "foo-1"); + }); + await alchemy.run("innerScope", async (scope) => { + expect(await scope.get("foo")).toBe("foo-1"); + }); + } finally { + await destroy(scope); + } + }); }); const Nested = Resource("Nested", async function (this, _id: string) { diff --git a/alchemy/test/stripe/price.test.ts b/alchemy/test/stripe/price.test.ts index b449ca808..01b23b1de 100644 --- a/alchemy/test/stripe/price.test.ts +++ b/alchemy/test/stripe/price.test.ts @@ -5,6 +5,7 @@ import { destroy } from "../../src/destroy.ts"; import { createStripeClient } from "../../src/stripe/client.ts"; import { Price } from "../../src/stripe/price.ts"; import { Product } from "../../src/stripe/product.ts"; +import { Meter } from "../../src/stripe/meter.ts"; import { BRANCH_PREFIX } from "../util.ts"; import "../../src/test/vitest.ts"; @@ -304,7 +305,7 @@ describe("Price Resource", () => { // Verify with Stripe API (need to expand tiers) const stripePrice = await stripe.prices.retrieve(price.id, { expand: ["tiers"], - } as any); + }); expect(stripePrice.billing_scheme).toEqual("tiered"); expect(stripePrice.tiers_mode).toEqual("graduated"); expect(stripePrice.tiers).toHaveLength(3); @@ -465,6 +466,117 @@ describe("Price Resource", () => { } }); + test("create metered price with billing meter", async (scope) => { + let product: Product | undefined; + let meter: Meter | undefined; + let price: Price | undefined; + + try { + // Create a test product + product = await Product(`${testProductId}-meter`, { + name: `${BRANCH_PREFIX} Meter Price Test Product`, + description: "A product for meter price testing", + }); + + // Create a real meter + meter = await Meter(`${testProductId}-meter-meter`, { + displayName: "API Usage Meter", + eventName: `${BRANCH_PREFIX}_api_usage`, + defaultAggregation: { + formula: "sum", + }, + customerMapping: { + type: "by_id", + eventPayloadKey: "stripe_customer_id", + }, + valueSettings: { + eventPayloadKey: "value", + }, + }); + + expect(meter.id).toBeTruthy(); + + // Create a metered price with meter + price = await Price(`${testPriceId}-meter`, { + product: product.id, + currency: "usd", + billingScheme: "tiered", + tiersMode: "graduated", + recurring: { + interval: "month", + usageType: "metered", + meter: meter.id, + }, + tiers: [ + { + upTo: 1000, + unitAmount: 0, + }, + { + upTo: "inf", + unitAmount: 10, + }, + ], + }); + + expect(price.id).toBeTruthy(); + expect(price).toMatchObject({ + product: product.id, + currency: "usd", + billingScheme: "tiered", + tiersMode: "graduated", + recurring: { + interval: "month", + usageType: "metered", + meter: meter.id, + }, + }); + + // Verify the meter is correctly set + expect(price.recurring?.meter).toEqual(meter.id); + } catch (err) { + console.log(err); + throw err; + } finally { + await destroy(scope); + + if (price?.id) { + await assertPriceDeactivated(price.id); + } + if (product?.id) { + await assertProductDeactivated(product.id); + } + // Note: Meter cleanup is handled by destroy(scope) + } + }); + + test("meter validation", async (scope) => { + const product = await Product(`${testProductId}-meter-validation`, { + name: `${BRANCH_PREFIX} Meter Validation Test Product`, + description: "A product for meter validation testing", + }); + + try { + // Test: meter requires metered usage type + await expect( + Price(`${testPriceId}-meter-invalid-usage`, { + product: product.id, + currency: "usd", + recurring: { + interval: "month", + usageType: "licensed", // Not metered + meter: "meter_test_invalid", + }, + }), + ).rejects.toThrow( + "Meter can only be set for prices with recurring.usageType = 'metered'", + ); + } finally { + await destroy(scope); + await assertProductDeactivated(product.id); + } + }); + test("price adoption fails without lookup key", async (scope) => { const firstId = `${testPriceId}-no-key-first`; const secondId = `${testPriceId}-no-key-second`; diff --git a/alchemy/tsconfig.workers.json b/alchemy/tsconfig.workers.json new file mode 100644 index 000000000..506197997 --- /dev/null +++ b/alchemy/tsconfig.workers.json @@ -0,0 +1,9 @@ +{ + "extends": "../tsconfig.base.json", + "include": ["workers/**/*.ts"], + "compilerOptions": { + "composite": true, + "noEmit": true, + "types": ["@cloudflare/workers-types"] + } +} diff --git a/alchemy/src/cloudflare/do-state-store/worker.ts b/alchemy/workers/do-state-store.ts similarity index 71% rename from alchemy/src/cloudflare/do-state-store/worker.ts rename to alchemy/workers/do-state-store.ts index 9f19fb9fa..db6865122 100644 --- a/alchemy/src/cloudflare/do-state-store/worker.ts +++ b/alchemy/workers/do-state-store.ts @@ -1,8 +1,8 @@ import { DurableObject, WorkerEntrypoint } from "cloudflare:workers"; import { Fs } from "dofs"; import { timingSafeEqual } from "node:crypto"; -import type { State } from "../../state.ts"; -import type { DOStateStoreAPI } from "./types.ts"; +import type { DOStateStoreAPI } from "../src/cloudflare/do-state-store/types.ts"; +import type { State } from "../src/state.ts"; interface Env { DOFS_STATE_STORE: DurableObjectNamespace; @@ -63,10 +63,20 @@ export default class extends WorkerEntrypoint { if (!token) { return false; } - return timingSafeEqual( - new TextEncoder().encode(token), - new TextEncoder().encode(this.env.DOFS_TOKEN), + + // Convert both to fixed-length buffers + const expectedBuffer = Buffer.from(this.env.DOFS_TOKEN, "utf8"); + const providedBuffer = Buffer.alloc(expectedBuffer.length); + + // Copy provided data, truncating or padding as needed + Buffer.from(token, "utf8").copy( + providedBuffer, + 0, + 0, + expectedBuffer.length, ); + + return timingSafeEqual(expectedBuffer, providedBuffer); } private getStub(request: Request): DurableObjectStub { @@ -127,7 +137,16 @@ export class DOFSStateStore extends DurableObject { async all(prefix: string): Promise> { const keys = this.list(prefix); - return await this.getBatch(keys); + const result: Record = {}; + await Promise.all( + keys.map(async (key) => { + const value = await this.get(`${prefix}/${key}`); + if (value) { + result[key] = value; + } + }), + ); + return result; } count(prefix: string): number { @@ -135,19 +154,17 @@ export class DOFSStateStore extends DurableObject { } delete(key: string): void { - try { - this.fs.unlink(key); - } catch (error) { - if (isErrorCode(error, "ENOENT")) { - return; + if (this.isFile(`${key}.json`)) { + this.fs.unlink(`${key}.json`); + } + if (this.isDirectory(key)) { + this.fs.rmdir(key); } - throw error; - } } async get(key: string): Promise { try { - const file = this.fs.readFile(key); + const file = this.fs.readFile(`${key}.json`); return new Response(file).text(); } catch (error) { if (isErrorCode(error, "ENOENT")) { @@ -171,31 +188,56 @@ export class DOFSStateStore extends DurableObject { } list(prefix: string): string[] { - try { - return this.fs - .listDir(prefix, { recursive: true }) - .filter((item) => item !== "." && item !== ".."); - } catch (error) { - if (isErrorCode(error, "ENOENT")) { - return []; - } - throw error; + const path = prefix.endsWith("/") ? prefix.slice(0, -1) : prefix; + if (!this.isDirectory(path)) { + return []; } + const files = this.fs.listDir(path); + return files.filter((item) => item.endsWith(".json")).map((item) => item.slice(0, -5)); } async set(key: string, value: State): Promise { this.ensureDir(key); - await this.fs.writeFile(key, JSON.stringify(value)); + await this.fs.writeFile(`${key}.json`, JSON.stringify(value)); } private ensureDir(path: string): void { const dir = path.split("/").slice(0, -1).join("/"); try { this.fs.mkdir(dir, { recursive: true }); - } catch { - // directory already exists, ignore + } catch (error) { + if (isErrorCode(error, "EEXIST")) { + return; + } + throw error; } } + + private stat(path: string): "file" | "directory" | "not-found" { + try { + const stat = this.fs.stat(path); + if (stat.isDirectory) { + return "directory"; + } + if (stat.isFile) { + return "file"; + } + throw new APIError(`Invalid file type for ${path}`, 500); + } catch (error) { + if (isErrorCode(error, "ENOENT")) { + return "not-found"; + } + throw error; + } + } + + private isFile(path: string): boolean { + return this.stat(path) === "file"; + } + + private isDirectory(path: string): boolean { + return this.stat(path) === "directory"; + } } const isErrorCode = (error: unknown, code: string) => { diff --git a/alchemy/workers/mixed-mode-proxy-worker.ts b/alchemy/workers/mixed-mode-proxy-worker.ts new file mode 100644 index 000000000..cba03ca15 --- /dev/null +++ b/alchemy/workers/mixed-mode-proxy-worker.ts @@ -0,0 +1,46 @@ +/// + +// Copied from https://github.com/cloudflare/workers-sdk/blob/70ba9fbf905a9ba5fe158d0bc8d48f6bf31712a2/packages/wrangler/templates/mixedMode/proxyServerWorker/index.ts +/** biome-ignore-all lint/style/noNonNullAssertion: wrangler did it not me */ + +export default { + async fetch(request, env) { + const targetBinding = request.headers.get("MF-Binding"); + + if (targetBinding) { + const originalHeaders = new Headers(); + for (const [name, value] of request.headers) { + if (name.startsWith("mf-header-")) { + originalHeaders.set(name.slice("mf-header-".length), value); + } else if (name === "upgrade") { + // The `Upgrade` header needs to be special-cased to prevent: + // TypeError: Worker tried to return a WebSocket in a response to a request which did not contain the header "Upgrade: websocket" + originalHeaders.set(name, value); + } + } + let fetcher = env[targetBinding]; + + // Special case the Dispatch Namespace binding because it has a top-level synchronous .get() call + const dispatchNamespaceOptions = originalHeaders.get( + "MF-Dispatch-Namespace-Options", + ); + if (dispatchNamespaceOptions) { + const { name, args, options } = JSON.parse(dispatchNamespaceOptions); + fetcher = (env[targetBinding] as DispatchNamespace).get( + name, + args, + options, + ); + } + return (fetcher as Fetcher).fetch( + request.headers.get("MF-URL")!, + new Request(request, { + redirect: "manual", + headers: originalHeaders, + }), + ); + } + return new Response("Provide a binding", { status: 400 }); + }, + } satisfies ExportedHandler>; + \ No newline at end of file diff --git a/bun.lock b/bun.lock index 99529da09..529f92e62 100644 --- a/bun.lock +++ b/bun.lock @@ -19,13 +19,14 @@ "lint-staged": "^15.3.0", "openai": "^4.103.0", "typescript": "latest", + "vite": "^6.3.5", "vitest": "^3.1.4", "yaml": "^2.7.1", }, }, "alchemy": { "name": "alchemy", - "version": "0.34.3", + "version": "0.41.2", "bin": { "alchemy": "bin/alchemy.mjs", }, @@ -55,30 +56,40 @@ "@aws-sdk/client-resource-groups-tagging-api": "^3.830.0", "@aws-sdk/client-s3": "3.726.1", "@biomejs/biome": "^1.9.4", + "@clack/prompts": "^0.11.0", + "@cloudflare/containers": "^0.0.13", "@cloudflare/puppeteer": "^1.0.2", - "@cloudflare/workers-types": "^4.20250303.0", + "@cloudflare/workers-types": "^4.20250620.0", "@iarna/toml": "^2.2.5", - "@inquirer/prompts": "^7.0.0", "@octokit/rest": "^21.1.1", "@types/bun": "latest", "@types/diff": "^5.0.0", + "@types/fs-extra": "^11.0.4", "@types/libsodium-wrappers": "^0.7.14", "@types/node": "latest", "@types/turndown": "^5.0.5", "ai": "^4.1.16", + "alchemy": "^0.37.1", "arktype": "^2.1.16", "braintrust": "^0.0.201", "change-case": "^5.4.4", "cloudflare": "^4.2.0", "cpx": "^1.5.0", + "dofs": "^0.1.0", + "execa": "^9.6.0", + "fs-extra": "^11.3.0", + "globby": "^14.1.0", "libsodium-wrappers": "^0.7.15", - "miniflare": "^4.0.0", + "miniflare": "^4.20250617.4", + "nitro-cloudflare-dev": "^0.2.2", "openpgp": "^6.1.0", + "picocolors": "^1.1.1", "prettier": "^3.5.3", + "trpc-cli": "^0.9.2", "turndown": "^7.2.0", "typedoc": "^0.28.1", "typedoc-plugin-markdown": "^4.6.0", - "typescript": "latest", + "typescript": "^5.8.3", "vite": "^6.0.7", "vitepress": "^1.6.3", "wrangler": "^3.114.0", @@ -101,7 +112,6 @@ "ai": "^4.0.0", "arktype": "^2.0.0", "cloudflare": "^4.0.0", - "dofs": "^0.0.1", "hono": "^4.0.0", "prettier": "^3.0.0", "stripe": "^17.0.0", @@ -120,6 +130,142 @@ "vue": "^3.5.13", }, }, + "alchemy/templates/astro": { + "name": "astro", + "version": "0.0.1", + "dependencies": { + "astro": "^5.10.0", + }, + "devDependencies": { + "@astrojs/cloudflare": "^12.6.0", + "@cloudflare/workers-types": "^4.20250620.0", + "alchemy": "workspace:*", + "miniflare": "^4.20250617.3", + "typescript": "^5.8.3", + }, + }, + "alchemy/templates/nuxt": { + "name": "nuxt", + "dependencies": { + "nuxt": "^3.17.5", + "vue": "^3.5.16", + "vue-router": "^4.5.1", + }, + "devDependencies": { + "@cloudflare/workers-types": "^4.20250620.0", + "alchemy": "workspace:*", + "miniflare": "^4.20250617.3", + "nitro-cloudflare-dev": "^0.2.2", + "typescript": "^5.8.3", + }, + }, + "alchemy/templates/react-router": { + "name": "react-router", + "dependencies": { + "isbot": "^5.1.27", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "react-router": "^7.5.3", + }, + "devDependencies": { + "@cloudflare/vite-plugin": "^1.0.12", + "@react-router/dev": "^7.5.3", + "@tailwindcss/vite": "^4.1.4", + "@types/node": "^20", + "@types/react": "^19.1.2", + "@types/react-dom": "^19.1.2", + "alchemy": "workspace:*", + "miniflare": "^4.20250617.3", + "tailwindcss": "^4.1.4", + "typescript": "^5.8.3", + "vite": "^6.3.3", + "vite-tsconfig-paths": "^5.1.4", + "wrangler": "^4.20.5", + }, + }, + "alchemy/templates/rwsdk": { + "name": "rwsdk", + "version": "1.0.0", + "dependencies": { + "@prisma/adapter-d1": "~6.8.2", + "@prisma/client": "~6.8.2", + "@simplewebauthn/browser": "^13.1.0", + "@simplewebauthn/server": "^13.1.1", + "rwsdk": "0.0.89", + }, + "devDependencies": { + "@cloudflare/workers-types": "^4.20250620.0", + "@types/node": "^22.14.0", + "@types/react": "^19.1.2", + "@types/react-dom": "^19.1.2", + "alchemy": "workspace:*", + "miniflare": "^4.20250617.3", + "prisma": "~6.8.2", + "typescript": "^5.8.3", + "vibe-rules": "^0.2.31", + "vite": "^6.2.6", + "wrangler": "^4.16.0", + }, + }, + "alchemy/templates/sveltekit": { + "name": "sveltekit", + "version": "0.0.1", + "devDependencies": { + "@cloudflare/workers-types": "^4.20250620.0", + "@sveltejs/adapter-auto": "^6.0.0", + "@sveltejs/adapter-cloudflare": "^7.0.4", + "@sveltejs/kit": "^2.16.0", + "@sveltejs/vite-plugin-svelte": "^5.1.0", + "@tailwindcss/vite": "^4.0.0", + "alchemy": "workspace:*", + "miniflare": "^4.20250617.3", + "prettier-plugin-tailwindcss": "^0.6.11", + "svelte": "^5.0.0", + "svelte-check": "^4.0.0", + "tailwindcss": "^4.0.0", + "typescript": "^5.8.3", + "vite": "^6.2.6", + "vite-plugin-devtools-json": "^0.2.0", + }, + }, + "alchemy/templates/tanstack-start": { + "name": "tanstack-start", + "dependencies": { + "@tanstack/react-router": "^1.121.27", + "@tanstack/react-router-devtools": "^1.121.27", + "@tanstack/react-start": "^1.121.32", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "tailwind-merge": "3", + "zod": "^3.24.2", + }, + "devDependencies": { + "@cloudflare/workers-types": "^4.20250620.0", + "@tailwindcss/vite": "^4.1.10", + "@types/node": "^22.5.4", + "@types/react": "^19.0.8", + "@types/react-dom": "^19.0.3", + "alchemy": "workspace:*", + "autoprefixer": "^10.4.20", + "miniflare": "^4.20250617.3", + "postcss": "^8.5.6", + "tailwindcss": "4", + "typescript": "^5.8.3", + "vite": "^6.3.5", + "vite-tsconfig-paths": "^5.1.4", + }, + }, + "alchemy/templates/typescript": { + "name": "typescript", + "version": "0.0.0", + "devDependencies": { + "@cloudflare/workers-types": "^4.20250620.0", + "@types/node": "^24.0.3", + "alchemy": "workspace:*", + "miniflare": "^4.20250617.3", + "typescript": "^5.8.3", + }, + }, "examples/aws-app": { "name": "aws-app", "version": "0.0.0", @@ -143,13 +289,25 @@ "tsx": "^4.19.3", }, }, + "examples/cloudflare-container": { + "name": "cloudflare-container", + "version": "0.0.0", + "devDependencies": { + "@cloudflare/workers-types": "latest", + "@types/node": "^24.0.1", + "alchemy": "workspace:*", + "hono": "^4.8.3", + "typescript": "^5.8.3", + }, + }, "examples/cloudflare-nuxt-pipeline": { "name": "nuxt-pipeline", "dependencies": { "@cloudflare/workers-types": "^4.20250421.0", "alchemy": "workspace:*", "cloudflare": "^4.2.0", - "nuxt": "^3.16.2", + "nuxt": "^3.17.5", + "vite": "^6.0.0", "vue": "^3.5.13", "vue-router": "^4.5.0", }, @@ -283,6 +441,20 @@ "alchemy": "workspace:*", }, }, + "examples/cloudflare-worker-simple": { + "name": "cloudflare-worker-simple", + "version": "0.0.0", + "devDependencies": { + "@cloudflare/workers-types": "latest", + "@types/node": "^24.0.1", + "alchemy": "workspace:*", + "typescript": "^5.8.3", + }, + }, + "examples/docker": { + "name": "docker", + "version": "0.0.0", + }, "stacks": { "name": "alchemy-stacks", "version": "0.0.1", @@ -320,31 +492,31 @@ "@algolia/autocomplete-shared": ["@algolia/autocomplete-shared@1.17.7", "", { "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", "algoliasearch": ">= 4.9.1 < 6" } }, "sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg=="], - "@algolia/client-abtesting": ["@algolia/client-abtesting@5.27.0", "", { "dependencies": { "@algolia/client-common": "5.27.0", "@algolia/requester-browser-xhr": "5.27.0", "@algolia/requester-fetch": "5.27.0", "@algolia/requester-node-http": "5.27.0" } }, "sha512-SITU5umoknxETtw67TxJu9njyMkWiH8pM+Bvw4dzfuIrIAT6Y1rmwV4y0A0didWoT+6xVuammIykbtBMolBcmg=="], + "@algolia/client-abtesting": ["@algolia/client-abtesting@5.29.0", "", { "dependencies": { "@algolia/client-common": "5.29.0", "@algolia/requester-browser-xhr": "5.29.0", "@algolia/requester-fetch": "5.29.0", "@algolia/requester-node-http": "5.29.0" } }, "sha512-AM/6LYMSTnZvAT5IarLEKjYWOdV+Fb+LVs8JRq88jn8HH6bpVUtjWdOZXqX1hJRXuCAY8SdQfb7F8uEiMNXdYQ=="], - "@algolia/client-analytics": ["@algolia/client-analytics@5.27.0", "", { "dependencies": { "@algolia/client-common": "5.27.0", "@algolia/requester-browser-xhr": "5.27.0", "@algolia/requester-fetch": "5.27.0", "@algolia/requester-node-http": "5.27.0" } }, "sha512-go1b9qIZK5vYEQ7jD2bsfhhhVsoh9cFxQ5xF8TzTsg2WOCZR3O92oXCkq15SOK0ngJfqDU6a/k0oZ4KuEnih1Q=="], + "@algolia/client-analytics": ["@algolia/client-analytics@5.29.0", "", { "dependencies": { "@algolia/client-common": "5.29.0", "@algolia/requester-browser-xhr": "5.29.0", "@algolia/requester-fetch": "5.29.0", "@algolia/requester-node-http": "5.29.0" } }, "sha512-La34HJh90l0waw3wl5zETO8TuukeUyjcXhmjYZL3CAPLggmKv74mobiGRIb+mmBENybiFDXf/BeKFLhuDYWMMQ=="], - "@algolia/client-common": ["@algolia/client-common@5.27.0", "", {}, "sha512-tnFOzdNuMzsz93kOClj3fKfuYoF3oYaEB5bggULSj075GJ7HUNedBEm7a6ScrjtnOaOtipbnT7veUpHA4o4wEQ=="], + "@algolia/client-common": ["@algolia/client-common@5.29.0", "", {}, "sha512-T0lzJH/JiCxQYtCcnWy7Jf1w/qjGDXTi2npyF9B9UsTvXB97GRC6icyfXxe21mhYvhQcaB1EQ/J2575FXxi2rA=="], - "@algolia/client-insights": ["@algolia/client-insights@5.27.0", "", { "dependencies": { "@algolia/client-common": "5.27.0", "@algolia/requester-browser-xhr": "5.27.0", "@algolia/requester-fetch": "5.27.0", "@algolia/requester-node-http": "5.27.0" } }, "sha512-y1qgw39qZijjQBXrqZTiwK1cWgWGRiLpJNWBv9w36nVMKfl9kInrfsYmdBAfmlhVgF/+Woe0y1jQ7pa4HyShAw=="], + "@algolia/client-insights": ["@algolia/client-insights@5.29.0", "", { "dependencies": { "@algolia/client-common": "5.29.0", "@algolia/requester-browser-xhr": "5.29.0", "@algolia/requester-fetch": "5.29.0", "@algolia/requester-node-http": "5.29.0" } }, "sha512-A39F1zmHY9aev0z4Rt3fTLcGN5AG1VsVUkVWy6yQG5BRDScktH+U5m3zXwThwniBTDV1HrPgiGHZeWb67GkR2Q=="], - "@algolia/client-personalization": ["@algolia/client-personalization@5.27.0", "", { "dependencies": { "@algolia/client-common": "5.27.0", "@algolia/requester-browser-xhr": "5.27.0", "@algolia/requester-fetch": "5.27.0", "@algolia/requester-node-http": "5.27.0" } }, "sha512-XluG9qPZKEbiLoIfXTKbABsWDNOMPx0t6T2ImJTTeuX+U/zBdmfcqqgcgkqXp+vbXof/XX/4of9Eqo1JaqEmKw=="], + "@algolia/client-personalization": ["@algolia/client-personalization@5.29.0", "", { "dependencies": { "@algolia/client-common": "5.29.0", "@algolia/requester-browser-xhr": "5.29.0", "@algolia/requester-fetch": "5.29.0", "@algolia/requester-node-http": "5.29.0" } }, "sha512-ibxmh2wKKrzu5du02gp8CLpRMeo+b/75e4ORct98CT7mIxuYFXowULwCd6cMMkz/R0LpKXIbTUl15UL5soaiUQ=="], - "@algolia/client-query-suggestions": ["@algolia/client-query-suggestions@5.27.0", "", { "dependencies": { "@algolia/client-common": "5.27.0", "@algolia/requester-browser-xhr": "5.27.0", "@algolia/requester-fetch": "5.27.0", "@algolia/requester-node-http": "5.27.0" } }, "sha512-V8/To+SsAl2sdw2AAjeLJuCW1L+xpz+LAGerJK7HKqHzE5yQhWmIWZTzqYQcojkii4iBMYn0y3+uReWqT8XVSQ=="], + "@algolia/client-query-suggestions": ["@algolia/client-query-suggestions@5.29.0", "", { "dependencies": { "@algolia/client-common": "5.29.0", "@algolia/requester-browser-xhr": "5.29.0", "@algolia/requester-fetch": "5.29.0", "@algolia/requester-node-http": "5.29.0" } }, "sha512-VZq4/AukOoJC2WSwF6J5sBtt+kImOoBwQc1nH3tgI+cxJBg7B77UsNC+jT6eP2dQCwGKBBRTmtPLUTDDnHpMgA=="], - "@algolia/client-search": ["@algolia/client-search@5.27.0", "", { "dependencies": { "@algolia/client-common": "5.27.0", "@algolia/requester-browser-xhr": "5.27.0", "@algolia/requester-fetch": "5.27.0", "@algolia/requester-node-http": "5.27.0" } }, "sha512-EJJ7WmvmUXZdchueKFCK8UZFyLqy4Hz64snNp0cTc7c0MKaSeDGYEDxVsIJKp15r7ORaoGxSyS4y6BGZMXYuCg=="], + "@algolia/client-search": ["@algolia/client-search@5.29.0", "", { "dependencies": { "@algolia/client-common": "5.29.0", "@algolia/requester-browser-xhr": "5.29.0", "@algolia/requester-fetch": "5.29.0", "@algolia/requester-node-http": "5.29.0" } }, "sha512-cZ0Iq3OzFUPpgszzDr1G1aJV5UMIZ4VygJ2Az252q4Rdf5cQMhYEIKArWY/oUjMhQmosM8ygOovNq7gvA9CdCg=="], - "@algolia/ingestion": ["@algolia/ingestion@1.27.0", "", { "dependencies": { "@algolia/client-common": "5.27.0", "@algolia/requester-browser-xhr": "5.27.0", "@algolia/requester-fetch": "5.27.0", "@algolia/requester-node-http": "5.27.0" } }, "sha512-xNCyWeqpmEo4EdmpG57Fs1fJIQcPwt5NnJ6MBdXnUdMVXF4f5PHgza+HQWQQcYpCsune96jfmR0v7us6gRIlCw=="], + "@algolia/ingestion": ["@algolia/ingestion@1.29.0", "", { "dependencies": { "@algolia/client-common": "5.29.0", "@algolia/requester-browser-xhr": "5.29.0", "@algolia/requester-fetch": "5.29.0", "@algolia/requester-node-http": "5.29.0" } }, "sha512-scBXn0wO5tZCxmO6evfa7A3bGryfyOI3aoXqSQBj5SRvNYXaUlFWQ/iKI70gRe/82ICwE0ICXbHT/wIvxOW7vw=="], - "@algolia/monitoring": ["@algolia/monitoring@1.27.0", "", { "dependencies": { "@algolia/client-common": "5.27.0", "@algolia/requester-browser-xhr": "5.27.0", "@algolia/requester-fetch": "5.27.0", "@algolia/requester-node-http": "5.27.0" } }, "sha512-P0NDiEFyt9UYQLBI0IQocIT7xHpjMpoFN3UDeerbztlkH9HdqT0GGh1SHYmNWpbMWIGWhSJTtz6kSIWvFu4+pw=="], + "@algolia/monitoring": ["@algolia/monitoring@1.29.0", "", { "dependencies": { "@algolia/client-common": "5.29.0", "@algolia/requester-browser-xhr": "5.29.0", "@algolia/requester-fetch": "5.29.0", "@algolia/requester-node-http": "5.29.0" } }, "sha512-FGWWG9jLFhsKB7YiDjM2dwQOYnWu//7Oxrb2vT96N7+s+hg1mdHHfHNRyEudWdxd4jkMhBjeqNA21VbTiOIPVg=="], - "@algolia/recommend": ["@algolia/recommend@5.27.0", "", { "dependencies": { "@algolia/client-common": "5.27.0", "@algolia/requester-browser-xhr": "5.27.0", "@algolia/requester-fetch": "5.27.0", "@algolia/requester-node-http": "5.27.0" } }, "sha512-cqfTMF1d1cc7hg0vITNAFxJZas7MJ4Obc36WwkKpY23NOtGb+4tH9X7UKlQa2PmTgbXIANoJ/DAQTeiVlD2I4Q=="], + "@algolia/recommend": ["@algolia/recommend@5.29.0", "", { "dependencies": { "@algolia/client-common": "5.29.0", "@algolia/requester-browser-xhr": "5.29.0", "@algolia/requester-fetch": "5.29.0", "@algolia/requester-node-http": "5.29.0" } }, "sha512-xte5+mpdfEARAu61KXa4ewpjchoZuJlAlvQb8ptK6hgHlBHDnYooy1bmOFpokaAICrq/H9HpoqNUX71n+3249A=="], - "@algolia/requester-browser-xhr": ["@algolia/requester-browser-xhr@5.27.0", "", { "dependencies": { "@algolia/client-common": "5.27.0" } }, "sha512-ErenYTcXl16wYXtf0pxLl9KLVxIztuehqXHfW9nNsD8mz9OX42HbXuPzT7y6JcPiWJpc/UU/LY5wBTB65vsEUg=="], + "@algolia/requester-browser-xhr": ["@algolia/requester-browser-xhr@5.29.0", "", { "dependencies": { "@algolia/client-common": "5.29.0" } }, "sha512-og+7Em75aPHhahEUScq2HQ3J7ULN63Levtd87BYMpn6Im5d5cNhaC4QAUsXu6LWqxRPgh4G+i+wIb6tVhDhg2A=="], - "@algolia/requester-fetch": ["@algolia/requester-fetch@5.27.0", "", { "dependencies": { "@algolia/client-common": "5.27.0" } }, "sha512-CNOvmXsVi+IvT7z1d+6X7FveVkgEQwTNgipjQCHTIbF9KSMfZR7tUsJC+NpELrm10ALdOMauah84ybs9rw1cKQ=="], + "@algolia/requester-fetch": ["@algolia/requester-fetch@5.29.0", "", { "dependencies": { "@algolia/client-common": "5.29.0" } }, "sha512-JCxapz7neAy8hT/nQpCvOrI5JO8VyQ1kPvBiaXWNC1prVq0UMYHEL52o1BsPvtXfdQ7BVq19OIq6TjOI06mV/w=="], - "@algolia/requester-node-http": ["@algolia/requester-node-http@5.27.0", "", { "dependencies": { "@algolia/client-common": "5.27.0" } }, "sha512-Nx9EdLYZDsaYFTthqmc0XcVvsx6jqeEX8fNiYOB5i2HboQwl8pJPj1jFhGqoGd0KG7KFR+sdPO5/e0EDDAru2Q=="], + "@algolia/requester-node-http": ["@algolia/requester-node-http@5.29.0", "", { "dependencies": { "@algolia/client-common": "5.29.0" } }, "sha512-lVBD81RBW5VTdEYgnzCz7Pf9j2H44aymCP+/eHGJu4vhU+1O8aKf3TVBgbQr5UM6xoe8IkR/B112XY6YIG2vtg=="], "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], @@ -362,7 +534,7 @@ "@astrojs/check": ["@astrojs/check@0.9.4", "", { "dependencies": { "@astrojs/language-server": "^2.15.0", "chokidar": "^4.0.1", "kleur": "^4.1.5", "yargs": "^17.7.2" }, "peerDependencies": { "typescript": "^5.0.0" }, "bin": { "astro-check": "dist/bin.js" } }, "sha512-IOheHwCtpUfvogHHsvu0AbeRZEnjJg3MopdLddkJE70mULItS/Vh37BHcI00mcOJcH1vhD3odbpvWokpxam7xA=="], - "@astrojs/cloudflare": ["@astrojs/cloudflare@12.5.4", "", { "dependencies": { "@astrojs/internal-helpers": "0.6.1", "@astrojs/underscore-redirects": "0.6.1", "@cloudflare/workers-types": "^4.20250507.0", "tinyglobby": "^0.2.13", "vite": "^6.3.5", "wrangler": "^4.14.1" }, "peerDependencies": { "astro": "^5.0.0" } }, "sha512-WKUeMP2tIbddEu0tlVEPj8o9m/8CJB6who3a3jupuIyR56ltmW924ZOMYtp/C9uxH7KeDJXrMszRj3LHs9U97w=="], + "@astrojs/cloudflare": ["@astrojs/cloudflare@12.6.0", "", { "dependencies": { "@astrojs/internal-helpers": "0.6.1", "@astrojs/underscore-redirects": "1.0.0", "@cloudflare/workers-types": "^4.20250507.0", "tinyglobby": "^0.2.13", "vite": "^6.3.5", "wrangler": "^4.14.1" }, "peerDependencies": { "astro": "^5.0.0" } }, "sha512-pQ8bokC59GEiXvyXpC4swBNoL7C/EknP+82KFzQwgR/Aeo5N1oPiAoPHgJbpPya/YF4E26WODdCQfBQDvLRfuw=="], "@astrojs/compiler": ["@astrojs/compiler@2.12.2", "", {}, "sha512-w2zfvhjNCkNMmMMOn5b0J8+OmUaBL1o40ipMvqcG6NRpdC+lKxmTi48DT8Xw0SzJ3AfmeFLB45zXZXtmbsjcgw=="], @@ -376,7 +548,7 @@ "@astrojs/telemetry": ["@astrojs/telemetry@3.3.0", "", { "dependencies": { "ci-info": "^4.2.0", "debug": "^4.4.0", "dlv": "^1.1.3", "dset": "^3.1.4", "is-docker": "^3.0.0", "is-wsl": "^3.1.0", "which-pm-runs": "^1.1.0" } }, "sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ=="], - "@astrojs/underscore-redirects": ["@astrojs/underscore-redirects@0.6.1", "", {}, "sha512-4bMLrs2KW+8/vHEE5Ffv2HbxCbbgXO+2N6MpoCsMXUlUoi7pgEEx8kbkzMXJ2dZtWF3gvwm9lvgjnFeanC2LGg=="], + "@astrojs/underscore-redirects": ["@astrojs/underscore-redirects@1.0.0", "", {}, "sha512-qZxHwVnmb5FXuvRsaIGaqWgnftjCuMY+GSbaVZdBmE4j8AfgPqKPxYp8SUERyJcjpKCEmO4wD6ybuGH8A2kVRQ=="], "@astrojs/yaml2ts": ["@astrojs/yaml2ts@0.2.2", "", { "dependencies": { "yaml": "^2.5.0" } }, "sha512-GOfvSr5Nqy2z5XiwqTouBBpy5FyI6DEe+/g/Mk5am9SjILN1S5fOEvYK0GuWHg98yS/dobP4m8qyqw/URW35fQ=="], @@ -400,19 +572,19 @@ "@aws-sdk/client-iam": ["@aws-sdk/client-iam@3.830.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/credential-provider-node": "3.830.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", "@aws-sdk/middleware-user-agent": "3.828.0", "@aws-sdk/region-config-resolver": "3.821.0", "@aws-sdk/types": "3.821.0", "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", "@aws-sdk/util-user-agent-node": "3.828.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.5.3", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", "@smithy/middleware-endpoint": "^4.1.11", "@smithy/middleware-retry": "^4.1.12", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.19", "@smithy/util-defaults-mode-node": "^4.0.19", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.5", "@smithy/util-utf8": "^4.0.0", "@smithy/util-waiter": "^4.0.5", "tslib": "^2.6.2" } }, "sha512-ZSWZyqtDXd51B2970tB5XI6+DFtw9c09a0C5v7UP8DY7wusNwqeM28jsFU7HqRAZTM4Hbf7ozC6OOj8uvH/rsQ=="], - "@aws-sdk/client-lambda": ["@aws-sdk/client-lambda@3.830.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/credential-provider-node": "3.830.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", "@aws-sdk/middleware-user-agent": "3.828.0", "@aws-sdk/region-config-resolver": "3.821.0", "@aws-sdk/types": "3.821.0", "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", "@aws-sdk/util-user-agent-node": "3.828.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.5.3", "@smithy/eventstream-serde-browser": "^4.0.4", "@smithy/eventstream-serde-config-resolver": "^4.1.2", "@smithy/eventstream-serde-node": "^4.0.4", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", "@smithy/middleware-endpoint": "^4.1.11", "@smithy/middleware-retry": "^4.1.12", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.19", "@smithy/util-defaults-mode-node": "^4.0.19", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.5", "@smithy/util-stream": "^4.2.2", "@smithy/util-utf8": "^4.0.0", "@smithy/util-waiter": "^4.0.5", "tslib": "^2.6.2" } }, "sha512-rdZPFteEN4Tw8vNVKh5rwPhf2zJz3cazx2gf1NSB8+GtleefY6bsAqyOQueSMSkE6fGZw7kmaPJBvWDxzMX4nQ=="], + "@aws-sdk/client-lambda": ["@aws-sdk/client-lambda@3.833.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/credential-provider-node": "3.830.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", "@aws-sdk/middleware-user-agent": "3.828.0", "@aws-sdk/region-config-resolver": "3.821.0", "@aws-sdk/types": "3.821.0", "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", "@aws-sdk/util-user-agent-node": "3.828.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.5.3", "@smithy/eventstream-serde-browser": "^4.0.4", "@smithy/eventstream-serde-config-resolver": "^4.1.2", "@smithy/eventstream-serde-node": "^4.0.4", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", "@smithy/middleware-endpoint": "^4.1.11", "@smithy/middleware-retry": "^4.1.12", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.19", "@smithy/util-defaults-mode-node": "^4.0.19", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.5", "@smithy/util-stream": "^4.2.2", "@smithy/util-utf8": "^4.0.0", "@smithy/util-waiter": "^4.0.5", "tslib": "^2.6.2" } }, "sha512-m56p1MB+/t+QFfZZzQoqlVcITn8PRYa7yzvuO2LbR9YsQ97uZyVCu970RAPSkW6/SjyWTUvXtQw3x/1KITcrvw=="], "@aws-sdk/client-resource-groups-tagging-api": ["@aws-sdk/client-resource-groups-tagging-api@3.830.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/credential-provider-node": "3.830.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", "@aws-sdk/middleware-user-agent": "3.828.0", "@aws-sdk/region-config-resolver": "3.821.0", "@aws-sdk/types": "3.821.0", "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", "@aws-sdk/util-user-agent-node": "3.828.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.5.3", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", "@smithy/middleware-endpoint": "^4.1.11", "@smithy/middleware-retry": "^4.1.12", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.19", "@smithy/util-defaults-mode-node": "^4.0.19", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.5", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-uvW329J9VppMsnHZUEWVA/JrbEJER8cX48lEaS7BxNpUjPrVhoLGxvQTiXevwJ9R2DriFdsyl1g1noDgrC1JZw=="], "@aws-sdk/client-s3": ["@aws-sdk/client-s3@3.726.1", "", { "dependencies": { "@aws-crypto/sha1-browser": "5.2.0", "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/client-sso-oidc": "3.726.0", "@aws-sdk/client-sts": "3.726.1", "@aws-sdk/core": "3.723.0", "@aws-sdk/credential-provider-node": "3.726.0", "@aws-sdk/middleware-bucket-endpoint": "3.726.0", "@aws-sdk/middleware-expect-continue": "3.723.0", "@aws-sdk/middleware-flexible-checksums": "3.723.0", "@aws-sdk/middleware-host-header": "3.723.0", "@aws-sdk/middleware-location-constraint": "3.723.0", "@aws-sdk/middleware-logger": "3.723.0", "@aws-sdk/middleware-recursion-detection": "3.723.0", "@aws-sdk/middleware-sdk-s3": "3.723.0", "@aws-sdk/middleware-ssec": "3.723.0", "@aws-sdk/middleware-user-agent": "3.726.0", "@aws-sdk/region-config-resolver": "3.723.0", "@aws-sdk/signature-v4-multi-region": "3.723.0", "@aws-sdk/types": "3.723.0", "@aws-sdk/util-endpoints": "3.726.0", "@aws-sdk/util-user-agent-browser": "3.723.0", "@aws-sdk/util-user-agent-node": "3.726.0", "@aws-sdk/xml-builder": "3.723.0", "@smithy/config-resolver": "^4.0.0", "@smithy/core": "^3.0.0", "@smithy/eventstream-serde-browser": "^4.0.0", "@smithy/eventstream-serde-config-resolver": "^4.0.0", "@smithy/eventstream-serde-node": "^4.0.0", "@smithy/fetch-http-handler": "^5.0.0", "@smithy/hash-blob-browser": "^4.0.0", "@smithy/hash-node": "^4.0.0", "@smithy/hash-stream-node": "^4.0.0", "@smithy/invalid-dependency": "^4.0.0", "@smithy/md5-js": "^4.0.0", "@smithy/middleware-content-length": "^4.0.0", "@smithy/middleware-endpoint": "^4.0.0", "@smithy/middleware-retry": "^4.0.0", "@smithy/middleware-serde": "^4.0.0", "@smithy/middleware-stack": "^4.0.0", "@smithy/node-config-provider": "^4.0.0", "@smithy/node-http-handler": "^4.0.0", "@smithy/protocol-http": "^5.0.0", "@smithy/smithy-client": "^4.0.0", "@smithy/types": "^4.0.0", "@smithy/url-parser": "^4.0.0", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.0", "@smithy/util-defaults-mode-node": "^4.0.0", "@smithy/util-endpoints": "^3.0.0", "@smithy/util-middleware": "^4.0.0", "@smithy/util-retry": "^4.0.0", "@smithy/util-stream": "^4.0.0", "@smithy/util-utf8": "^4.0.0", "@smithy/util-waiter": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-UpOGcob87DiuS2d3fW6vDZg94g57mNiOSkzvR/6GOdvBSlUgk8LLwVzGASB71FdKMl1EGEr4MeD5uKH9JsG+dw=="], - "@aws-sdk/client-sagemaker": ["@aws-sdk/client-sagemaker@3.830.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/credential-provider-node": "3.830.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", "@aws-sdk/middleware-user-agent": "3.828.0", "@aws-sdk/region-config-resolver": "3.821.0", "@aws-sdk/types": "3.821.0", "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", "@aws-sdk/util-user-agent-node": "3.828.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.5.3", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", "@smithy/middleware-endpoint": "^4.1.11", "@smithy/middleware-retry": "^4.1.12", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.19", "@smithy/util-defaults-mode-node": "^4.0.19", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.5", "@smithy/util-utf8": "^4.0.0", "@smithy/util-waiter": "^4.0.5", "@types/uuid": "^9.0.1", "tslib": "^2.6.2", "uuid": "^9.0.1" } }, "sha512-4+iAamSczZkyION7XqyqLfnXPWIA1D5YROK16NyKzq9SInEUQYM/aVMRVzPq7SKfmRNrrZNR736T6dGeWj6Jkw=="], + "@aws-sdk/client-sagemaker": ["@aws-sdk/client-sagemaker@3.833.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/credential-provider-node": "3.830.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", "@aws-sdk/middleware-user-agent": "3.828.0", "@aws-sdk/region-config-resolver": "3.821.0", "@aws-sdk/types": "3.821.0", "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", "@aws-sdk/util-user-agent-node": "3.828.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.5.3", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", "@smithy/middleware-endpoint": "^4.1.11", "@smithy/middleware-retry": "^4.1.12", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.19", "@smithy/util-defaults-mode-node": "^4.0.19", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.5", "@smithy/util-utf8": "^4.0.0", "@smithy/util-waiter": "^4.0.5", "@types/uuid": "^9.0.1", "tslib": "^2.6.2", "uuid": "^9.0.1" } }, "sha512-mHtUkTtHYd47YZt0L56mCjCyeOysrzeuipU520ID7TlhnyRro0FzY7EZ5VhNDyfjNVU+tyd8BxoRFmFRsR9Vfg=="], "@aws-sdk/client-ses": ["@aws-sdk/client-ses@3.830.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/credential-provider-node": "3.830.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", "@aws-sdk/middleware-user-agent": "3.828.0", "@aws-sdk/region-config-resolver": "3.821.0", "@aws-sdk/types": "3.821.0", "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", "@aws-sdk/util-user-agent-node": "3.828.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.5.3", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", "@smithy/middleware-endpoint": "^4.1.11", "@smithy/middleware-retry": "^4.1.12", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.19", "@smithy/util-defaults-mode-node": "^4.0.19", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.5", "@smithy/util-utf8": "^4.0.0", "@smithy/util-waiter": "^4.0.5", "tslib": "^2.6.2" } }, "sha512-Y2XaJkqHJ7qM4cpCw3YS96fMZgT44mP3HLP+9dU0ct29L+iwf3zhigJGQzakieMdJfuTFZe7Vi6s1RbcWv5v5w=="], "@aws-sdk/client-sesv2": ["@aws-sdk/client-sesv2@3.830.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/credential-provider-node": "3.830.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", "@aws-sdk/middleware-user-agent": "3.828.0", "@aws-sdk/region-config-resolver": "3.821.0", "@aws-sdk/signature-v4-multi-region": "3.826.0", "@aws-sdk/types": "3.821.0", "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", "@aws-sdk/util-user-agent-node": "3.828.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.5.3", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", "@smithy/middleware-endpoint": "^4.1.11", "@smithy/middleware-retry": "^4.1.12", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.19", "@smithy/util-defaults-mode-node": "^4.0.19", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.5", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-7Il9UByZfo4C2Mkp9UPKEzoVlzYaVY+pAQwmKiutWVYbxiv8/WyWhnJsnXav8t9L4Xfh5n7iSuqvD8+2Dv050Q=="], - "@aws-sdk/client-sqs": ["@aws-sdk/client-sqs@3.830.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/credential-provider-node": "3.830.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", "@aws-sdk/middleware-sdk-sqs": "3.826.0", "@aws-sdk/middleware-user-agent": "3.828.0", "@aws-sdk/region-config-resolver": "3.821.0", "@aws-sdk/types": "3.821.0", "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", "@aws-sdk/util-user-agent-node": "3.828.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.5.3", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/md5-js": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", "@smithy/middleware-endpoint": "^4.1.11", "@smithy/middleware-retry": "^4.1.12", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.19", "@smithy/util-defaults-mode-node": "^4.0.19", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.5", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-akkCO/phYSQNLHNNCWZ2uN7e6fzmNUAUStzFMP0ipd6Pmr/fqbISDlkZyPW+J60qq2BDzueQrpxzrPTJZdO4hg=="], + "@aws-sdk/client-sqs": ["@aws-sdk/client-sqs@3.831.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/credential-provider-node": "3.830.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", "@aws-sdk/middleware-sdk-sqs": "3.826.0", "@aws-sdk/middleware-user-agent": "3.828.0", "@aws-sdk/region-config-resolver": "3.821.0", "@aws-sdk/types": "3.821.0", "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", "@aws-sdk/util-user-agent-node": "3.828.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.5.3", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/md5-js": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", "@smithy/middleware-endpoint": "^4.1.11", "@smithy/middleware-retry": "^4.1.12", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.19", "@smithy/util-defaults-mode-node": "^4.0.19", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.5", "@smithy/util-utf8": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-7UioW8vkCar7PzR1MknLqMrLJ/15mdd4tPQvVlaHdDZxstwbTmm1yY6PGZUwIjr7Ju4ZP8XpnTmThKp0N+PJ6g=="], "@aws-sdk/client-ssm": ["@aws-sdk/client-ssm@3.830.0", "", { "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/credential-provider-node": "3.830.0", "@aws-sdk/middleware-host-header": "3.821.0", "@aws-sdk/middleware-logger": "3.821.0", "@aws-sdk/middleware-recursion-detection": "3.821.0", "@aws-sdk/middleware-user-agent": "3.828.0", "@aws-sdk/region-config-resolver": "3.821.0", "@aws-sdk/types": "3.821.0", "@aws-sdk/util-endpoints": "3.828.0", "@aws-sdk/util-user-agent-browser": "3.821.0", "@aws-sdk/util-user-agent-node": "3.828.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.5.3", "@smithy/fetch-http-handler": "^5.0.4", "@smithy/hash-node": "^4.0.4", "@smithy/invalid-dependency": "^4.0.4", "@smithy/middleware-content-length": "^4.0.4", "@smithy/middleware-endpoint": "^4.1.11", "@smithy/middleware-retry": "^4.1.12", "@smithy/middleware-serde": "^4.0.8", "@smithy/middleware-stack": "^4.0.4", "@smithy/node-config-provider": "^4.1.3", "@smithy/node-http-handler": "^4.0.6", "@smithy/protocol-http": "^5.1.2", "@smithy/smithy-client": "^4.4.3", "@smithy/types": "^4.3.1", "@smithy/url-parser": "^4.0.4", "@smithy/util-base64": "^4.0.0", "@smithy/util-body-length-browser": "^4.0.0", "@smithy/util-body-length-node": "^4.0.0", "@smithy/util-defaults-mode-browser": "^4.0.19", "@smithy/util-defaults-mode-node": "^4.0.19", "@smithy/util-endpoints": "^3.0.6", "@smithy/util-middleware": "^4.0.4", "@smithy/util-retry": "^4.0.5", "@smithy/util-utf8": "^4.0.0", "@smithy/util-waiter": "^4.0.5", "@types/uuid": "^9.0.1", "tslib": "^2.6.2", "uuid": "^9.0.1" } }, "sha512-eeYYfVldV7Od/tMcyWumufspKGwlu53XjhgF8zMC7e7P2D541iQ6LwvHuwScw8xhlqfHsBzMS/yCnsDeM4kNmw=="], @@ -440,7 +612,7 @@ "@aws-sdk/credential-provider-web-identity": ["@aws-sdk/credential-provider-web-identity@3.830.0", "", { "dependencies": { "@aws-sdk/core": "3.826.0", "@aws-sdk/nested-clients": "3.830.0", "@aws-sdk/types": "3.821.0", "@smithy/property-provider": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-hPYrKsZeeOdLROJ59T6Y8yZ0iwC/60L3qhZXjapBFjbqBtMaQiMTI645K6xVXBioA6vxXq7B4aLOhYqk6Fy/Ww=="], - "@aws-sdk/credential-providers": ["@aws-sdk/credential-providers@3.830.0", "", { "dependencies": { "@aws-sdk/client-cognito-identity": "3.830.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/credential-provider-cognito-identity": "3.830.0", "@aws-sdk/credential-provider-env": "3.826.0", "@aws-sdk/credential-provider-http": "3.826.0", "@aws-sdk/credential-provider-ini": "3.830.0", "@aws-sdk/credential-provider-node": "3.830.0", "@aws-sdk/credential-provider-process": "3.826.0", "@aws-sdk/credential-provider-sso": "3.830.0", "@aws-sdk/credential-provider-web-identity": "3.830.0", "@aws-sdk/nested-clients": "3.830.0", "@aws-sdk/types": "3.821.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.5.3", "@smithy/credential-provider-imds": "^4.0.6", "@smithy/node-config-provider": "^4.1.3", "@smithy/property-provider": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-Q16Yf52L9QWsRhaaG/Q6eUkUWGUrbKTM2ba8at8ZZ8tsGaKO5pYgXUTErxB1bin11S6JszinbLqUf9G9oUExxA=="], + "@aws-sdk/credential-providers": ["@aws-sdk/credential-providers@3.834.0", "", { "dependencies": { "@aws-sdk/client-cognito-identity": "3.830.0", "@aws-sdk/core": "3.826.0", "@aws-sdk/credential-provider-cognito-identity": "3.830.0", "@aws-sdk/credential-provider-env": "3.826.0", "@aws-sdk/credential-provider-http": "3.826.0", "@aws-sdk/credential-provider-ini": "3.830.0", "@aws-sdk/credential-provider-node": "3.830.0", "@aws-sdk/credential-provider-process": "3.826.0", "@aws-sdk/credential-provider-sso": "3.830.0", "@aws-sdk/credential-provider-web-identity": "3.830.0", "@aws-sdk/nested-clients": "3.830.0", "@aws-sdk/types": "3.821.0", "@smithy/config-resolver": "^4.1.4", "@smithy/core": "^3.5.3", "@smithy/credential-provider-imds": "^4.0.6", "@smithy/node-config-provider": "^4.1.3", "@smithy/property-provider": "^4.0.4", "@smithy/types": "^4.3.1", "tslib": "^2.6.2" } }, "sha512-ORIWCrLuqJnJg0fuI0rPhwaeuzqnIIJsbSkg1WV2XuiOpWXwLC/CfzhAbelQAv07/sRywZMnKqws0OOWg/ieYg=="], "@aws-sdk/endpoint-cache": ["@aws-sdk/endpoint-cache@3.804.0", "", { "dependencies": { "mnemonist": "0.38.3", "tslib": "^2.6.2" } }, "sha512-TQVDkA/lV6ua75ELZaichMzlp6x7tDa1bqdy/+0ZftmODPtKXuOOEcJxmdN7Ui/YRo1gkRz2D9txYy7IlNg1Og=="], @@ -572,27 +744,33 @@ "@capsizecss/unpack": ["@capsizecss/unpack@2.4.0", "", { "dependencies": { "blob-to-buffer": "^1.2.8", "cross-fetch": "^3.0.4", "fontkit": "^2.0.2" } }, "sha512-GrSU71meACqcmIUxPYOJvGKF0yryjN/L1aCuE9DViCTJI7bfkjgYDPD1zbNDcINJwSSP6UaBZY9GAbYDO7re0Q=="], + "@clack/core": ["@clack/core@0.5.0", "", { "dependencies": { "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-p3y0FIOwaYRUPRcMO7+dlmLh8PSRcrjuTndsiA0WAFbWES0mLZlrjVoBRZ9DzkPFJZG6KGkJmoEAY0ZcVWTkow=="], + + "@clack/prompts": ["@clack/prompts@0.11.0", "", { "dependencies": { "@clack/core": "0.5.0", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-pMN5FcrEw9hUkZA4f+zLlzivQSeQf5dRGJjSUbvVYDLvpKCdQx5OaknvKzgbtXOizhP+SJJJjqEbOe55uKKfAw=="], + + "@cloudflare/containers": ["@cloudflare/containers@0.0.13", "", {}, "sha512-qFO2ApEJGRfLlsOqK3ZkvpU3bl7Pj1SquXHs/uEgmK1FTBf2R908T8v7TUlxZpzaJbMM7rLRoCqT0vfUthRqWA=="], + "@cloudflare/kv-asset-handler": ["@cloudflare/kv-asset-handler@0.4.0", "", { "dependencies": { "mime": "^3.0.0" } }, "sha512-+tv3z+SPp+gqTIcImN9o0hqE9xyfQjI1XD9pL6NuKjua9B1y7mNYv0S9cP+QEbA4ppVgGZEmKOvHX5G5Ei1CVA=="], "@cloudflare/puppeteer": ["@cloudflare/puppeteer@1.0.2", "", { "dependencies": { "@puppeteer/browsers": "2.2.3", "debug": "4.3.4", "devtools-protocol": "0.0.1273771", "ws": "8.17.0" } }, "sha512-I4UmeOg/9lmdrDqcXp1ZZALjCpk6ysygMZifVoI75YNuVHmvOjfkaXWRE1p/WgAs9phDeZrY7AqoK1YLGHwwww=="], - "@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.3.2", "", { "peerDependencies": { "unenv": "2.0.0-rc.17", "workerd": "^1.20250508.0" }, "optionalPeers": ["workerd"] }, "sha512-MtUgNl+QkQyhQvv5bbWP+BpBC1N0me4CHHuP2H4ktmOMKdB/6kkz/lo+zqiA4mEazb4y+1cwyNjVrQ2DWeE4mg=="], + "@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.3.3", "", { "peerDependencies": { "unenv": "2.0.0-rc.17", "workerd": "^1.20250508.0" }, "optionalPeers": ["workerd"] }, "sha512-/M3MEcj3V2WHIRSW1eAQBPRJ6JnGQHc6JKMAPLkDb7pLs3m6X9ES/+K3ceGqxI6TKeF32AWAi7ls0AYzVxCP0A=="], - "@cloudflare/vite-plugin": ["@cloudflare/vite-plugin@1.6.0", "", { "dependencies": { "@cloudflare/unenv-preset": "2.3.2", "@mjackson/node-fetch-server": "^0.6.1", "@rollup/plugin-replace": "^6.0.1", "get-port": "^7.1.0", "miniflare": "4.20250604.1", "picocolors": "^1.1.1", "tinyglobby": "^0.2.12", "unenv": "2.0.0-rc.17", "wrangler": "4.20.0", "ws": "8.18.0" }, "peerDependencies": { "vite": "^6.1.0" } }, "sha512-pK5uqCXzXePnpUY4tDIFE1TJNMnybdInN28QU+il1ElbJUuVCM/9YnzJIvYzSrDbXzVUSAIZDleqf5kAOjQALg=="], + "@cloudflare/vite-plugin": ["@cloudflare/vite-plugin@1.7.4", "", { "dependencies": { "@cloudflare/unenv-preset": "2.3.3", "@mjackson/node-fetch-server": "^0.6.1", "@rollup/plugin-replace": "^6.0.1", "get-port": "^7.1.0", "miniflare": "4.20250617.3", "picocolors": "^1.1.1", "tinyglobby": "^0.2.12", "unenv": "2.0.0-rc.17", "wrangler": "4.20.5", "ws": "8.18.0" }, "peerDependencies": { "vite": "^6.1.0" } }, "sha512-MI76rn3roInZ3AskSClHj2VVhZB9a9KCthBNU5/pQ04Ohceb9AfZcaqAuGgR6/XrEW0ac7LDqbF2mlCXDOaDAQ=="], - "@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20250604.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-PI6AWAzhHg75KVhYkSWFBf3HKCHstpaKg4nrx6LYZaEvz0TaTz+JQpYU2fNAgGFmVsK5xEzwFTGh3DAVAKONPw=="], + "@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20250617.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-toG8JUKVLIks4oOJLe9FeuixE84pDpMZ32ip7mCpE7JaFc5BqGFvevk0YC/db3T71AQlialjRwioH3jS/dzItA=="], - "@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20250604.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-hOiZZSop7QRQgGERtTIy9eU5GvPpIsgE2/BDsUdHMl7OBZ7QLniqvgDzLNDzj0aTkCldm9Yl/Z+C7aUgRdOccw=="], + "@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20250617.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-JTX0exbC9/ZtMmQQA8tDZEZFMXZrxOpTUj2hHnsUkErWYkr5SSZH04RBhPg6dU4VL8bXuB5/eJAh7+P9cZAp7g=="], - "@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20250604.0", "", { "os": "linux", "cpu": "x64" }, "sha512-S0R9r7U4nv9qejYygQj01hArC4KUbQQ4u29rvegR0MGoXZY8AHIEuJxon0kE7r7aWFJxvl4W3tOH+5hwW51LYw=="], + "@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20250617.0", "", { "os": "linux", "cpu": "x64" }, "sha512-8jkSoVRJ+1bOx3tuWlZCGaGCV2ew7/jFMl6V3CPXOoEtERUHsZBQLVkQIGKcmC/LKSj7f/mpyBUeu2EPTo2HEg=="], - "@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20250604.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-BTFU/rXpNy03wpeueI2P7q1vVjbg2V6mCyyFGqDqMn2gSVYXH1G0zFNolV13PQXa0HgaqM6oYnqtAxluqbA+kQ=="], + "@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20250617.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-YAzcOyu897z5dQKFzme1oujGWMGEJCR7/Wrrm1nSP6dqutxFPTubRADM8BHn2CV3ij//vaPnAeLmZE3jVwOwig=="], - "@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20250604.0", "", { "os": "win32", "cpu": "x64" }, "sha512-tW/U9/qDmDZBeoEVcK5skb2uouVAMXMzt7o/uGvaIFLeZsQkOp4NBmvoQQd+nbOc7nVCJIwFoSMokd89AhzCkA=="], + "@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20250617.0", "", { "os": "win32", "cpu": "x64" }, "sha512-XWM/6sagDrO0CYDKhXhPjM23qusvIN1ju9ZEml6gOQs8tNOFnq6Cn6X9FAmnyapRFCGUSEC3HZYJAm7zwVKaMA=="], "@cloudflare/workers-shared": ["@cloudflare/workers-shared@0.17.5", "", { "dependencies": { "mime": "^3.0.0", "zod": "^3.22.3" } }, "sha512-e2tjozEy3/8JnPcddYFuMjW9As+aX0i7egciPE8b+mufS33QCtdFEzZKCK8utFzby0tx9TkxGFLJ+cmSrJ+tLw=="], - "@cloudflare/workers-types": ["@cloudflare/workers-types@4.20250614.0", "", {}, "sha512-a2r9Yamj+7YlXUeGbnpwZdxGyTx1rMyLdt2xtzk46HgpGq3QV8ka8s3B+tB4OzDPXH9x5TmplwlO9vTJkCXG1w=="], + "@cloudflare/workers-types": ["@cloudflare/workers-types@4.20250620.0", "", {}, "sha512-EVvRB/DJEm6jhdKg+A4Qm4y/ry1cIvylSgSO3/f/Bv161vldDRxaXM2YoQQWFhLOJOw0qtrHsKOD51KYxV1XCw=="], "@colors/colors": ["@colors/colors@1.6.0", "", {}, "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA=="], @@ -712,7 +890,9 @@ "@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="], - "@gerrit0/mini-shiki": ["@gerrit0/mini-shiki@3.6.0", "", { "dependencies": { "@shikijs/engine-oniguruma": "^3.6.0", "@shikijs/langs": "^3.6.0", "@shikijs/themes": "^3.6.0", "@shikijs/types": "^3.6.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-KaeJvPNofTEZR9EzVNp/GQzbQqkGfjiu6k3CXKvhVTX+8OoAKSX/k7qxLKOX3B0yh2XqVAc93rsOu48CGt2Qug=="], + "@gerrit0/mini-shiki": ["@gerrit0/mini-shiki@3.7.0", "", { "dependencies": { "@shikijs/engine-oniguruma": "^3.7.0", "@shikijs/langs": "^3.7.0", "@shikijs/themes": "^3.7.0", "@shikijs/types": "^3.7.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-7iY9wg4FWXmeoFJpUL2u+tsmh0d0jcEJHAIzVxl3TG4KL493JNnisdLAILZ77zcD+z3J0keEXZ+lFzUgzQzPDg=="], + + "@hexagon/base64": ["@hexagon/base64@1.1.28", "", {}, "sha512-lhqDEAvWixy3bZ+UOYbPwUbBkwBq5C1LAJ/xPC8Oi+lL54oyakv/npbA0aU2hgCsx/1NUd4IBvV03+aUBWxerw=="], "@humanfs/core": ["@humanfs/core@0.19.1", "", {}, "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA=="], @@ -726,7 +906,7 @@ "@iconify-json/logos": ["@iconify-json/logos@1.2.4", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-XC4If5D/hbaZvUkTV8iaZuGlQCyG6CNOlaAaJaGa13V5QMYwYjgtKk3vPP8wz3wtTVNVEVk3LRx1fOJz+YnSMw=="], - "@iconify-json/simple-icons": ["@iconify-json/simple-icons@1.2.38", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-mvMeFQgVjoHanQE9Q7ihmriEXAorjLZW+crUgQspDjFpzWuQp2RZMTppl1MN6TQztMVTsNFgF6LDKsp+v1RYRg=="], + "@iconify-json/simple-icons": ["@iconify-json/simple-icons@1.2.39", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-XlhW73c4dHvUrwWckVY76HDjnaZ2fWKD6hNZtd5kuv23GC0g3Lu0MXnYscpkIYOeiXO+Gtlw8FM53J7C84mCtA=="], "@iconify-json/vscode-icons": ["@iconify-json/vscode-icons@1.2.23", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-gFTcKecKra2/b5SbGDgHGI/l8CuikHyBPmqGlK+YCmS8AK72dtDQbUekdoACsju/3TYS37QvdPoOQwnyx2LdYg=="], @@ -776,34 +956,6 @@ "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.2", "", { "os": "win32", "cpu": "x64" }, "sha512-aUdT6zEYtDKCaxkofmmJDJYGCf0+pJg3eU9/oBuqvEeoB9dKI6ZLc/1iLJCTuJQDO4ptntAlkUmHgGjyuobZbw=="], - "@inquirer/checkbox": ["@inquirer/checkbox@4.1.8", "", { "dependencies": { "@inquirer/core": "^10.1.13", "@inquirer/figures": "^1.0.12", "@inquirer/type": "^3.0.7", "ansi-escapes": "^4.3.2", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-d/QAsnwuHX2OPolxvYcgSj7A9DO9H6gVOy2DvBTx+P2LH2iRTo/RSGV3iwCzW024nP9hw98KIuDmdyhZQj1UQg=="], - - "@inquirer/confirm": ["@inquirer/confirm@5.1.12", "", { "dependencies": { "@inquirer/core": "^10.1.13", "@inquirer/type": "^3.0.7" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-dpq+ielV9/bqgXRUbNH//KsY6WEw9DrGPmipkpmgC1Y46cwuBTNx7PXFWTjc3MQ+urcc0QxoVHcMI0FW4Ok0hg=="], - - "@inquirer/core": ["@inquirer/core@10.1.13", "", { "dependencies": { "@inquirer/figures": "^1.0.12", "@inquirer/type": "^3.0.7", "ansi-escapes": "^4.3.2", "cli-width": "^4.1.0", "mute-stream": "^2.0.0", "signal-exit": "^4.1.0", "wrap-ansi": "^6.2.0", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-1viSxebkYN2nJULlzCxES6G9/stgHSepZ9LqqfdIGPHj5OHhiBUXVS0a6R0bEC2A+VL4D9w6QB66ebCr6HGllA=="], - - "@inquirer/editor": ["@inquirer/editor@4.2.13", "", { "dependencies": { "@inquirer/core": "^10.1.13", "@inquirer/type": "^3.0.7", "external-editor": "^3.1.0" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-WbicD9SUQt/K8O5Vyk9iC2ojq5RHoCLK6itpp2fHsWe44VxxcA9z3GTWlvjSTGmMQpZr+lbVmrxdHcumJoLbMA=="], - - "@inquirer/expand": ["@inquirer/expand@4.0.15", "", { "dependencies": { "@inquirer/core": "^10.1.13", "@inquirer/type": "^3.0.7", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-4Y+pbr/U9Qcvf+N/goHzPEXiHH8680lM3Dr3Y9h9FFw4gHS+zVpbj8LfbKWIb/jayIB4aSO4pWiBTrBYWkvi5A=="], - - "@inquirer/figures": ["@inquirer/figures@1.0.12", "", {}, "sha512-MJttijd8rMFcKJC8NYmprWr6hD3r9Gd9qUC0XwPNwoEPWSMVJwA2MlXxF+nhZZNMY+HXsWa+o7KY2emWYIn0jQ=="], - - "@inquirer/input": ["@inquirer/input@4.1.12", "", { "dependencies": { "@inquirer/core": "^10.1.13", "@inquirer/type": "^3.0.7" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-xJ6PFZpDjC+tC1P8ImGprgcsrzQRsUh9aH3IZixm1lAZFK49UGHxM3ltFfuInN2kPYNfyoPRh+tU4ftsjPLKqQ=="], - - "@inquirer/number": ["@inquirer/number@3.0.15", "", { "dependencies": { "@inquirer/core": "^10.1.13", "@inquirer/type": "^3.0.7" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-xWg+iYfqdhRiM55MvqiTCleHzszpoigUpN5+t1OMcRkJrUrw7va3AzXaxvS+Ak7Gny0j2mFSTv2JJj8sMtbV2g=="], - - "@inquirer/password": ["@inquirer/password@4.0.15", "", { "dependencies": { "@inquirer/core": "^10.1.13", "@inquirer/type": "^3.0.7", "ansi-escapes": "^4.3.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-75CT2p43DGEnfGTaqFpbDC2p2EEMrq0S+IRrf9iJvYreMy5mAWj087+mdKyLHapUEPLjN10mNvABpGbk8Wdraw=="], - - "@inquirer/prompts": ["@inquirer/prompts@7.5.3", "", { "dependencies": { "@inquirer/checkbox": "^4.1.8", "@inquirer/confirm": "^5.1.12", "@inquirer/editor": "^4.2.13", "@inquirer/expand": "^4.0.15", "@inquirer/input": "^4.1.12", "@inquirer/number": "^3.0.15", "@inquirer/password": "^4.0.15", "@inquirer/rawlist": "^4.1.3", "@inquirer/search": "^3.0.15", "@inquirer/select": "^4.2.3" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-8YL0WiV7J86hVAxrh3fE5mDCzcTDe1670unmJRz6ArDgN+DBK1a0+rbnNWp4DUB5rPMwqD5ZP6YHl9KK1mbZRg=="], - - "@inquirer/rawlist": ["@inquirer/rawlist@4.1.3", "", { "dependencies": { "@inquirer/core": "^10.1.13", "@inquirer/type": "^3.0.7", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-7XrV//6kwYumNDSsvJIPeAqa8+p7GJh7H5kRuxirct2cgOcSWwwNGoXDRgpNFbY/MG2vQ4ccIWCi8+IXXyFMZA=="], - - "@inquirer/search": ["@inquirer/search@3.0.15", "", { "dependencies": { "@inquirer/core": "^10.1.13", "@inquirer/figures": "^1.0.12", "@inquirer/type": "^3.0.7", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-YBMwPxYBrADqyvP4nNItpwkBnGGglAvCLVW8u4pRmmvOsHUtCAUIMbUrLX5B3tFL1/WsLGdQ2HNzkqswMs5Uaw=="], - - "@inquirer/select": ["@inquirer/select@4.2.3", "", { "dependencies": { "@inquirer/core": "^10.1.13", "@inquirer/figures": "^1.0.12", "@inquirer/type": "^3.0.7", "ansi-escapes": "^4.3.2", "yoctocolors-cjs": "^2.1.2" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-OAGhXU0Cvh0PhLz9xTF/kx6g6x+sP+PcyTiLvCrewI99P3BBeexD+VbuwkNDvqGkk3y2h5ZiWLeRP7BFlhkUDg=="], - - "@inquirer/type": ["@inquirer/type@3.0.7", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-PfunHQcjwnju84L+ycmcMKB/pTPIngjUJvfnRhKY6FKPuYXlM4aQCb/nIdTFR6BEhMjFvngzvng/vBAJMZpLSA=="], - "@ioredis/commands": ["@ioredis/commands@1.2.0", "", {}, "sha512-Sx1pU8EM64o2BrqNpEO1CNLtKQwyhuXuqyfH7oGKCk+1a33d2r5saW8zNwm3j6BTExtjrv2BxTgzzkMwts6vGg=="], "@isaacs/balanced-match": ["@isaacs/balanced-match@4.0.1", "", {}, "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ=="], @@ -830,6 +982,8 @@ "@kwsites/promise-deferred": ["@kwsites/promise-deferred@1.1.1", "", {}, "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw=="], + "@levischuck/tiny-cbor": ["@levischuck/tiny-cbor@0.2.11", "", {}, "sha512-llBRm4dT4Z89aRsm6u2oEZ8tfwL/2l6BwpZ7JcyieouniDECM5AqNgr/y08zalEIvW3RSK4upYyybDcmjXqAow=="], + "@mapbox/node-pre-gyp": ["@mapbox/node-pre-gyp@2.0.0", "", { "dependencies": { "consola": "^3.2.3", "detect-libc": "^2.0.0", "https-proxy-agent": "^7.0.5", "node-fetch": "^2.6.7", "nopt": "^8.0.0", "semver": "^7.5.3", "tar": "^7.4.0" }, "bin": { "node-pre-gyp": "bin/node-pre-gyp" } }, "sha512-llMXd39jtP0HpQLVI37Bf1m2ADlEb35GYSh1SDSLsBhR+5iCxiNGlT31yqbNtVHygHAtMy6dWFERpU2JgufhPg=="], "@mixmark-io/domino": ["@mixmark-io/domino@2.2.0", "", {}, "sha512-Y28PR25bHXUg88kCV7nivXrP2Nj2RueZ3/l/jdx6J9f8J4nsEGcgX0Qe6lt7Pa+J79+kPiJU3LguR6O/6zrLOw=="], @@ -902,7 +1056,7 @@ "@octokit/plugin-rest-endpoint-methods": ["@octokit/plugin-rest-endpoint-methods@13.5.0", "", { "dependencies": { "@octokit/types": "^13.10.0" }, "peerDependencies": { "@octokit/core": ">=6" } }, "sha512-9Pas60Iv9ejO3WlAX3maE1+38c5nqbJXV5GrncEfkndIpZrJ/WPMRd2xYDcPPEt5yzpxcjw9fWNoPhsSGzqKqw=="], - "@octokit/request": ["@octokit/request@9.2.3", "", { "dependencies": { "@octokit/endpoint": "^10.1.4", "@octokit/request-error": "^6.1.8", "@octokit/types": "^14.0.0", "fast-content-type-parse": "^2.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-Ma+pZU8PXLOEYzsWf0cn/gY+ME57Wq8f49WTXA8FMHp2Ps9djKw//xYJ1je8Hm0pR2lU9FUGeJRWOtxq6olt4w=="], + "@octokit/request": ["@octokit/request@9.2.4", "", { "dependencies": { "@octokit/endpoint": "^10.1.4", "@octokit/request-error": "^6.1.8", "@octokit/types": "^14.0.0", "fast-content-type-parse": "^2.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-q8ybdytBmxa6KogWlNa818r0k1wlqzNC+yNkcQDECHvQo8Vmstrg18JwqJHdJdUiHD2sjlwBgSm9kHkOKe2iyA=="], "@octokit/request-error": ["@octokit/request-error@6.1.8", "", { "dependencies": { "@octokit/types": "^14.0.0" } }, "sha512-WEi/R0Jmq+IJKydWlKDmryPcmdYSVjL3ekaiEL1L9eo1sUnqMJ+grqmC9cjk7CA7+b2/T397tO5d8YLOH3qYpQ=="], @@ -992,6 +1146,16 @@ "@parcel/watcher-win32-x64": ["@parcel/watcher-win32-x64@2.5.1", "", { "os": "win32", "cpu": "x64" }, "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA=="], + "@peculiar/asn1-android": ["@peculiar/asn1-android@2.3.16", "", { "dependencies": { "@peculiar/asn1-schema": "^2.3.15", "asn1js": "^3.0.5", "tslib": "^2.8.1" } }, "sha512-a1viIv3bIahXNssrOIkXZIlI2ePpZaNmR30d4aBL99mu2rO+mT9D6zBsp7H6eROWGtmwv0Ionp5olJurIo09dw=="], + + "@peculiar/asn1-ecc": ["@peculiar/asn1-ecc@2.3.15", "", { "dependencies": { "@peculiar/asn1-schema": "^2.3.15", "@peculiar/asn1-x509": "^2.3.15", "asn1js": "^3.0.5", "tslib": "^2.8.1" } }, "sha512-/HtR91dvgog7z/WhCVdxZJ/jitJuIu8iTqiyWVgRE9Ac5imt2sT/E4obqIVGKQw7PIy+X6i8lVBoT6wC73XUgA=="], + + "@peculiar/asn1-rsa": ["@peculiar/asn1-rsa@2.3.15", "", { "dependencies": { "@peculiar/asn1-schema": "^2.3.15", "@peculiar/asn1-x509": "^2.3.15", "asn1js": "^3.0.5", "tslib": "^2.8.1" } }, "sha512-p6hsanvPhexRtYSOHihLvUUgrJ8y0FtOM97N5UEpC+VifFYyZa0iZ5cXjTkZoDwxJ/TTJ1IJo3HVTB2JJTpXvg=="], + + "@peculiar/asn1-schema": ["@peculiar/asn1-schema@2.3.15", "", { "dependencies": { "asn1js": "^3.0.5", "pvtsutils": "^1.3.6", "tslib": "^2.8.1" } }, "sha512-QPeD8UA8axQREpgR5UTAfu2mqQmm97oUqahDtNdBcfj3qAnoXzFdQW+aNf/tD2WVXF8Fhmftxoj0eMIT++gX2w=="], + + "@peculiar/asn1-x509": ["@peculiar/asn1-x509@2.3.15", "", { "dependencies": { "@peculiar/asn1-schema": "^2.3.15", "asn1js": "^3.0.5", "pvtsutils": "^1.3.6", "tslib": "^2.8.1" } }, "sha512-0dK5xqTqSLaxv1FHXIcd4Q/BZNuopg+u1l23hT9rOmQ1g4dNtw0g/RnEi+TboB0gOwGtrWn269v27cMgchFIIg=="], + "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], "@polka/url": ["@polka/url@1.0.0-next.29", "", {}, "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww=="], @@ -1002,6 +1166,24 @@ "@poppinss/exception": ["@poppinss/exception@1.2.1", "", {}, "sha512-aQypoot0HPSJa6gDPEPTntc1GT6QINrSbgRlRhadGW2WaYqUK3tK4Bw9SBMZXhmxd3GeAlZjVcODHgiu+THY7A=="], + "@prisma/adapter-d1": ["@prisma/adapter-d1@6.8.2", "", { "dependencies": { "@cloudflare/workers-types": "4.20250214.0", "@prisma/driver-adapter-utils": "6.8.2", "ky": "1.7.5" } }, "sha512-YAFT6y0E5WBOY6IeHbT2vORa8h6bpJTEBlGC3by96bcLDSVBA0vdgTkBgg+rFuO4zEN727dWR/IdOaW0/zsHZg=="], + + "@prisma/client": ["@prisma/client@6.8.2", "", { "peerDependencies": { "prisma": "*", "typescript": ">=5.1.0" }, "optionalPeers": ["prisma", "typescript"] }, "sha512-5II+vbyzv4si6Yunwgkj0qT/iY0zyspttoDrL3R4BYgLdp42/d2C8xdi9vqkrYtKt9H32oFIukvyw3Koz5JoDg=="], + + "@prisma/config": ["@prisma/config@6.8.2", "", { "dependencies": { "jiti": "2.4.2" } }, "sha512-ZJY1fF4qRBPdLQ/60wxNtX+eu89c3AkYEcP7L3jkp0IPXCNphCYxikTg55kPJLDOG6P0X+QG5tCv6CmsBRZWFQ=="], + + "@prisma/debug": ["@prisma/debug@6.8.2", "", {}, "sha512-4muBSSUwJJ9BYth5N8tqts8JtiLT8QI/RSAzEogwEfpbYGFo9mYsInsVo8dqXdPO2+Rm5OG5q0qWDDE3nyUbVg=="], + + "@prisma/driver-adapter-utils": ["@prisma/driver-adapter-utils@6.8.2", "", { "dependencies": { "@prisma/debug": "6.8.2" } }, "sha512-5+CzN/41gBsRmA3ekbVy1TXnSImSPBtMlxWAttVH6tg94bv4zGGRmyk5tUCdT83nl0hG1Sq2oMXR7ml6aqILvw=="], + + "@prisma/engines": ["@prisma/engines@6.8.2", "", { "dependencies": { "@prisma/debug": "6.8.2", "@prisma/engines-version": "6.8.0-43.2060c79ba17c6bb9f5823312b6f6b7f4a845738e", "@prisma/fetch-engine": "6.8.2", "@prisma/get-platform": "6.8.2" } }, "sha512-XqAJ//LXjqYRQ1RRabs79KOY4+v6gZOGzbcwDQl0D6n9WBKjV7qdrbd042CwSK0v0lM9MSHsbcFnU2Yn7z8Zlw=="], + + "@prisma/engines-version": ["@prisma/engines-version@6.8.0-43.2060c79ba17c6bb9f5823312b6f6b7f4a845738e", "", {}, "sha512-Rkik9lMyHpFNGaLpPF3H5q5TQTkm/aE7DsGM5m92FZTvWQsvmi6Va8On3pWvqLHOt5aPUvFb/FeZTmphI4CPiQ=="], + + "@prisma/fetch-engine": ["@prisma/fetch-engine@6.8.2", "", { "dependencies": { "@prisma/debug": "6.8.2", "@prisma/engines-version": "6.8.0-43.2060c79ba17c6bb9f5823312b6f6b7f4a845738e", "@prisma/get-platform": "6.8.2" } }, "sha512-lCvikWOgaLOfqXGacEKSNeenvj0n3qR5QvZUOmPE2e1Eh8cMYSobxonCg9rqM6FSdTfbpqp9xwhSAOYfNqSW0g=="], + + "@prisma/get-platform": ["@prisma/get-platform@6.8.2", "", { "dependencies": { "@prisma/debug": "6.8.2" } }, "sha512-vXSxyUgX3vm1Q70QwzwkjeYfRryIvKno1SXbIqwSptKwqKzskINnDUcx85oX+ys6ooN2ATGSD0xN2UTfg6Zcow=="], + "@puppeteer/browsers": ["@puppeteer/browsers@2.10.5", "", { "dependencies": { "debug": "^4.4.1", "extract-zip": "^2.0.1", "progress": "^2.0.3", "proxy-agent": "^6.5.0", "semver": "^7.7.2", "tar-fs": "^3.0.8", "yargs": "^17.7.2" }, "bin": { "browsers": "lib/cjs/main-cli.js" } }, "sha512-eifa0o+i8dERnngJwKrfp3dEq7ia5XFyoqB17S4gK8GhsQE4/P8nxOfQSE0zQHxzzLo/cmF+7+ywEQ7wK7Fb+w=="], "@react-router/dev": ["@react-router/dev@7.6.2", "", { "dependencies": { "@babel/core": "^7.21.8", "@babel/generator": "^7.21.5", "@babel/parser": "^7.21.8", "@babel/plugin-syntax-decorators": "^7.22.10", "@babel/plugin-syntax-jsx": "^7.21.4", "@babel/preset-typescript": "^7.21.5", "@babel/traverse": "^7.23.2", "@babel/types": "^7.22.5", "@npmcli/package-json": "^4.0.1", "@react-router/node": "7.6.2", "arg": "^5.0.1", "babel-dead-code-elimination": "^1.0.6", "chokidar": "^4.0.0", "dedent": "^1.5.3", "es-module-lexer": "^1.3.1", "exit-hook": "2.2.1", "fs-extra": "^10.0.0", "jsesc": "3.0.2", "lodash": "^4.17.21", "pathe": "^1.1.2", "picocolors": "^1.1.1", "prettier": "^2.7.1", "react-refresh": "^0.14.0", "semver": "^7.3.7", "set-cookie-parser": "^2.6.0", "valibot": "^0.41.0", "vite-node": "^3.1.4" }, "peerDependencies": { "@react-router/serve": "^7.6.2", "react-router": "^7.6.2", "typescript": "^5.1.0", "vite": "^5.1.0 || ^6.0.0", "wrangler": "^3.28.2 || ^4.0.0" }, "optionalPeers": ["@react-router/serve", "typescript", "wrangler"], "bin": { "react-router": "bin.js" } }, "sha512-BuG83Ug2C/P+zMYErTz/KKuXoxbOefh3oR66r13XWG9txwooC9nt2QDt2u8yt7Eo/9BATnx+TmXnOHEWqMyB8w=="], @@ -1012,11 +1194,11 @@ "@redwoodjs/starter-drizzle": ["@redwoodjs/starter-drizzle@workspace:examples/cloudflare-redwood"], - "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.11", "", {}, "sha512-L/gAA/hyCSuzTF1ftlzUSI/IKr2POHsv1Dd78GfqkR83KMNuswWD61JxGV2L7nRwBBBSDr6R1gCkdTmoN7W4ag=="], + "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.19", "", {}, "sha512-3FL3mnMbPu0muGOCaKAhhFEYmqv9eTfPSJRJmANrCwtgK8VuxpsZDGK+m0LYAGoyO8+0j5uRe4PeyPDK1yA/hA=="], "@rollup/plugin-alias": ["@rollup/plugin-alias@5.1.1", "", { "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-PR9zDb+rOzkRb2VD+EuKB7UC41vU5DIwZ5qqCpk0KJudcWAyi8rvYOhS7+L5aZCspw1stTViLgN5v6FF1p5cgQ=="], - "@rollup/plugin-commonjs": ["@rollup/plugin-commonjs@28.0.5", "", { "dependencies": { "@rollup/pluginutils": "^5.0.1", "commondir": "^1.0.1", "estree-walker": "^2.0.2", "fdir": "^6.2.0", "is-reference": "1.2.1", "magic-string": "^0.30.3", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^2.68.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-lytLp2JgAMwqJY6ve3OSROXr2XuEYHjnsQN3hmnxC+w11dI91LuUw4Yc1kk2FqKXeMG8psoFejFgK+znoij0cg=="], + "@rollup/plugin-commonjs": ["@rollup/plugin-commonjs@28.0.6", "", { "dependencies": { "@rollup/pluginutils": "^5.0.1", "commondir": "^1.0.1", "estree-walker": "^2.0.2", "fdir": "^6.2.0", "is-reference": "1.2.1", "magic-string": "^0.30.3", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^2.68.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-XSQB1K7FUU5QP+3lOQmVCE3I0FcbbNvmNT4VJSj93iUjayaARrTQeoRdiYQoftAJBLrR9t2agwAd3ekaTgHNlw=="], "@rollup/plugin-inject": ["@rollup/plugin-inject@5.0.5", "", { "dependencies": { "@rollup/pluginutils": "^5.0.1", "estree-walker": "^2.0.2", "magic-string": "^0.30.3" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-2+DEJbNBoPROPkgTDNe8/1YXWcqxbN5DTjASVIOx8HS+pITXushyNiBV56RB08zuptzz8gT3YfkqriTBVycepg=="], @@ -1028,47 +1210,47 @@ "@rollup/plugin-terser": ["@rollup/plugin-terser@0.4.4", "", { "dependencies": { "serialize-javascript": "^6.0.1", "smob": "^1.0.0", "terser": "^5.17.4" }, "peerDependencies": { "rollup": "^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A=="], - "@rollup/pluginutils": ["@rollup/pluginutils@5.1.4", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ=="], + "@rollup/pluginutils": ["@rollup/pluginutils@5.2.0", "", { "dependencies": { "@types/estree": "^1.0.0", "estree-walker": "^2.0.2", "picomatch": "^4.0.2" }, "peerDependencies": { "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0" }, "optionalPeers": ["rollup"] }, "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw=="], - "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.43.0", "", { "os": "android", "cpu": "arm" }, "sha512-Krjy9awJl6rKbruhQDgivNbD1WuLb8xAclM4IR4cN5pHGAs2oIMMQJEiC3IC/9TZJ+QZkmZhlMO/6MBGxPidpw=="], + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.44.0", "", { "os": "android", "cpu": "arm" }, "sha512-xEiEE5oDW6tK4jXCAyliuntGR+amEMO7HLtdSshVuhFnKTYoeYMyXQK7pLouAJJj5KHdwdn87bfHAR2nSdNAUA=="], - "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.43.0", "", { "os": "android", "cpu": "arm64" }, "sha512-ss4YJwRt5I63454Rpj+mXCXicakdFmKnUNxr1dLK+5rv5FJgAxnN7s31a5VchRYxCFWdmnDWKd0wbAdTr0J5EA=="], + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.44.0", "", { "os": "android", "cpu": "arm64" }, "sha512-uNSk/TgvMbskcHxXYHzqwiyBlJ/lGcv8DaUfcnNwict8ba9GTTNxfn3/FAoFZYgkaXXAdrAA+SLyKplyi349Jw=="], - "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.43.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-eKoL8ykZ7zz8MjgBenEF2OoTNFAPFz1/lyJ5UmmFSz5jW+7XbH1+MAgCVHy72aG59rbuQLcJeiMrP8qP5d/N0A=="], + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.44.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-VGF3wy0Eq1gcEIkSCr8Ke03CWT+Pm2yveKLaDvq51pPpZza3JX/ClxXOCmTYYq3us5MvEuNRTaeyFThCKRQhOA=="], - "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.43.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-SYwXJgaBYW33Wi/q4ubN+ldWC4DzQY62S4Ll2dgfr/dbPoF50dlQwEaEHSKrQdSjC6oIe1WgzosoaNoHCdNuMg=="], + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.44.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-fBkyrDhwquRvrTxSGH/qqt3/T0w5Rg0L7ZIDypvBPc1/gzjJle6acCpZ36blwuwcKD/u6oCE/sRWlUAcxLWQbQ=="], - "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.43.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-SV+U5sSo0yujrjzBF7/YidieK2iF6E7MdF6EbYxNz94lA+R0wKl3SiixGyG/9Klab6uNBIqsN7j4Y/Fya7wAjQ=="], + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.44.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-u5AZzdQJYJXByB8giQ+r4VyfZP+walV+xHWdaFx/1VxsOn6eWJhK2Vl2eElvDJFKQBo/hcYIBg/jaKS8ZmKeNQ=="], - "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.43.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-J7uCsiV13L/VOeHJBo5SjasKiGxJ0g+nQTrBkAsmQBIdil3KhPnSE9GnRon4ejX1XDdsmK/l30IYLiAaQEO0Cg=="], + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.44.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-qC0kS48c/s3EtdArkimctY7h3nHicQeEUdjJzYVJYR3ct3kWSafmn6jkNCA8InbUdge6PVx6keqjk5lVGJf99g=="], - "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.43.0", "", { "os": "linux", "cpu": "arm" }, "sha512-gTJ/JnnjCMc15uwB10TTATBEhK9meBIY+gXP4s0sHD1zHOaIh4Dmy1X9wup18IiY9tTNk5gJc4yx9ctj/fjrIw=="], + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.44.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x+e/Z9H0RAWckn4V2OZZl6EmV0L2diuX3QB0uM1r6BvhUIv6xBPL5mrAX2E3e8N8rEHVPwFfz/ETUbV4oW9+lQ=="], - "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.43.0", "", { "os": "linux", "cpu": "arm" }, "sha512-ZJ3gZynL1LDSIvRfz0qXtTNs56n5DI2Mq+WACWZ7yGHFUEirHBRt7fyIk0NsCKhmRhn7WAcjgSkSVVxKlPNFFw=="], + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.44.0", "", { "os": "linux", "cpu": "arm" }, "sha512-1exwiBFf4PU/8HvI8s80icyCcnAIB86MCBdst51fwFmH5dyeoWVPVgmQPcKrMtBQ0W5pAs7jBCWuRXgEpRzSCg=="], - "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.43.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-8FnkipasmOOSSlfucGYEu58U8cxEdhziKjPD2FIa0ONVMxvl/hmONtX/7y4vGjdUhjcTHlKlDhw3H9t98fPvyA=="], + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.44.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-ZTR2mxBHb4tK4wGf9b8SYg0Y6KQPjGpR4UWwTFdnmjB4qRtoATZ5dWn3KsDwGa5Z2ZBOE7K52L36J9LueKBdOQ=="], - "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.43.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-KPPyAdlcIZ6S9C3S2cndXDkV0Bb1OSMsX0Eelr2Bay4EsF9yi9u9uzc9RniK3mcUGCLhWY9oLr6er80P5DE6XA=="], + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.44.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-GFWfAhVhWGd4r6UxmnKRTBwP1qmModHtd5gkraeW2G490BpFOZkFtem8yuX2NyafIP/mGpRJgTJ2PwohQkUY/Q=="], - "@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.43.0", "", { "os": "linux", "cpu": "none" }, "sha512-HPGDIH0/ZzAZjvtlXj6g+KDQ9ZMHfSP553za7o2Odegb/BEfwJcR0Sw0RLNpQ9nC6Gy8s+3mSS9xjZ0n3rhcYg=="], + "@rollup/rollup-linux-loongarch64-gnu": ["@rollup/rollup-linux-loongarch64-gnu@4.44.0", "", { "os": "linux", "cpu": "none" }, "sha512-xw+FTGcov/ejdusVOqKgMGW3c4+AgqrfvzWEVXcNP6zq2ue+lsYUgJ+5Rtn/OTJf7e2CbgTFvzLW2j0YAtj0Gg=="], - "@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.43.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-gEmwbOws4U4GLAJDhhtSPWPXUzDfMRedT3hFMyRAvM9Mrnj+dJIFIeL7otsv2WF3D7GrV0GIewW0y28dOYWkmw=="], + "@rollup/rollup-linux-powerpc64le-gnu": ["@rollup/rollup-linux-powerpc64le-gnu@4.44.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-bKGibTr9IdF0zr21kMvkZT4K6NV+jjRnBoVMt2uNMG0BYWm3qOVmYnXKzx7UhwrviKnmK46IKMByMgvpdQlyJQ=="], - "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.43.0", "", { "os": "linux", "cpu": "none" }, "sha512-XXKvo2e+wFtXZF/9xoWohHg+MuRnvO29TI5Hqe9xwN5uN8NKUYy7tXUG3EZAlfchufNCTHNGjEx7uN78KsBo0g=="], + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.44.0", "", { "os": "linux", "cpu": "none" }, "sha512-vV3cL48U5kDaKZtXrti12YRa7TyxgKAIDoYdqSIOMOFBXqFj2XbChHAtXquEn2+n78ciFgr4KIqEbydEGPxXgA=="], - "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.43.0", "", { "os": "linux", "cpu": "none" }, "sha512-ruf3hPWhjw6uDFsOAzmbNIvlXFXlBQ4nk57Sec8E8rUxs/AI4HD6xmiiasOOx/3QxS2f5eQMKTAwk7KHwpzr/Q=="], + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.44.0", "", { "os": "linux", "cpu": "none" }, "sha512-TDKO8KlHJuvTEdfw5YYFBjhFts2TR0VpZsnLLSYmB7AaohJhM8ctDSdDnUGq77hUh4m/djRafw+9zQpkOanE2Q=="], - "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.43.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-QmNIAqDiEMEvFV15rsSnjoSmO0+eJLoKRD9EAa9rrYNwO/XRCtOGM3A5A0X+wmG+XRrw9Fxdsw+LnyYiZWWcVw=="], + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.44.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-8541GEyktXaw4lvnGp9m84KENcxInhAt6vPWJ9RodsB/iGjHoMB2Pp5MVBCiKIRxrxzJhGCxmNzdu+oDQ7kwRA=="], - "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.43.0", "", { "os": "linux", "cpu": "x64" }, "sha512-jAHr/S0iiBtFyzjhOkAics/2SrXE092qyqEg96e90L3t9Op8OTzS6+IX0Fy5wCt2+KqeHAkti+eitV0wvblEoQ=="], + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.44.0", "", { "os": "linux", "cpu": "x64" }, "sha512-iUVJc3c0o8l9Sa/qlDL2Z9UP92UZZW1+EmQ4xfjTc1akr0iUFZNfxrXJ/R1T90h/ILm9iXEY6+iPrmYB3pXKjw=="], - "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.43.0", "", { "os": "linux", "cpu": "x64" }, "sha512-3yATWgdeXyuHtBhrLt98w+5fKurdqvs8B53LaoKD7P7H7FKOONLsBVMNl9ghPQZQuYcceV5CDyPfyfGpMWD9mQ=="], + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.44.0", "", { "os": "linux", "cpu": "x64" }, "sha512-PQUobbhLTQT5yz/SPg116VJBgz+XOtXt8D1ck+sfJJhuEsMj2jSej5yTdp8CvWBSceu+WW+ibVL6dm0ptG5fcA=="], - "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.43.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-wVzXp2qDSCOpcBCT5WRWLmpJRIzv23valvcTwMHEobkjippNf+C3ys/+wf07poPkeNix0paTNemB2XrHr2TnGw=="], + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.44.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-M0CpcHf8TWn+4oTxJfh7LQuTuaYeXGbk0eageVjQCKzYLsajWS/lFC94qlRqOlyC2KvRT90ZrfXULYmukeIy7w=="], - "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.43.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-fYCTEyzf8d+7diCw8b+asvWDCLMjsCEA8alvtAutqJOJp/wL5hs1rWSqJ1vkjgW0L2NB4bsYJrpKkiIPRR9dvw=="], + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.44.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-3XJ0NQtMAXTWFW8FqZKcw3gOQwBtVWP/u8TpHP3CRPXD7Pd6s8lLdH3sHWh8vqKCyyiI8xW5ltJScQmBU9j7WA=="], - "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.43.0", "", { "os": "win32", "cpu": "x64" }, "sha512-SnGhLiE5rlK0ofq8kzuDkM0g7FN1s5VYY+YSMTibP7CqShxCQvqtNxTARS4xX4PFJfHjG0ZQYX9iGzI3FQh5Aw=="], + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.44.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Q2Mgwt+D8hd5FIPUuPDsvPR7Bguza6yTkJxspDGkZj7tBRn2y4KSWYuIXpftFSjBra76TbKerCV7rgFPQrn+wQ=="], "@sec-ant/readable-stream": ["@sec-ant/readable-stream@0.4.1", "", {}, "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg=="], @@ -1076,11 +1258,11 @@ "@shikijs/engine-javascript": ["@shikijs/engine-javascript@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^3.1.0" } }, "sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w=="], - "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.6.0", "", { "dependencies": { "@shikijs/types": "3.6.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-nmOhIZ9yT3Grd+2plmW/d8+vZ2pcQmo/UnVwXMUXAKTXdi+LK0S08Ancrz5tQQPkxvjBalpMW2aKvwXfelauvA=="], + "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.7.0", "", { "dependencies": { "@shikijs/types": "3.7.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-5BxcD6LjVWsGu4xyaBC5bu8LdNgPCVBnAkWTtOCs/CZxcB22L8rcoWfv7Hh/3WooVjBZmFtyxhgvkQFedPGnFw=="], - "@shikijs/langs": ["@shikijs/langs@3.6.0", "", { "dependencies": { "@shikijs/types": "3.6.0" } }, "sha512-IdZkQJaLBu1LCYCwkr30hNuSDfllOT8RWYVZK1tD2J03DkiagYKRxj/pDSl8Didml3xxuyzUjgtioInwEQM/TA=="], + "@shikijs/langs": ["@shikijs/langs@3.7.0", "", { "dependencies": { "@shikijs/types": "3.7.0" } }, "sha512-1zYtdfXLr9xDKLTGy5kb7O0zDQsxXiIsw1iIBcNOO8Yi5/Y1qDbJ+0VsFoqTlzdmneO8Ij35g7QKF8kcLyznCQ=="], - "@shikijs/themes": ["@shikijs/themes@3.6.0", "", { "dependencies": { "@shikijs/types": "3.6.0" } }, "sha512-Fq2j4nWr1DF4drvmhqKq8x5vVQ27VncF8XZMBuHuQMZvUSS3NBgpqfwz/FoGe36+W6PvniZ1yDlg2d4kmYDU6w=="], + "@shikijs/themes": ["@shikijs/themes@3.7.0", "", { "dependencies": { "@shikijs/types": "3.7.0" } }, "sha512-VJx8497iZPy5zLiiCTSIaOChIcKQwR0FebwE9S3rcN0+J/GTWwQ1v/bqhTbpbY3zybPKeO8wdammqkpXc4NVjQ=="], "@shikijs/transformers": ["@shikijs/transformers@2.5.0", "", { "dependencies": { "@shikijs/core": "2.5.0", "@shikijs/types": "2.5.0" } }, "sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg=="], @@ -1088,6 +1270,10 @@ "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="], + "@simplewebauthn/browser": ["@simplewebauthn/browser@13.1.0", "", {}, "sha512-WuHZ/PYvyPJ9nxSzgHtOEjogBhwJfC8xzYkPC+rR/+8chl/ft4ngjiK8kSU5HtRJfczupyOh33b25TjYbvwAcg=="], + + "@simplewebauthn/server": ["@simplewebauthn/server@13.1.1", "", { "dependencies": { "@hexagon/base64": "^1.1.27", "@levischuck/tiny-cbor": "^0.2.2", "@peculiar/asn1-android": "^2.3.10", "@peculiar/asn1-ecc": "^2.3.8", "@peculiar/asn1-rsa": "^2.3.8", "@peculiar/asn1-schema": "^2.3.8", "@peculiar/asn1-x509": "^2.3.8" } }, "sha512-1hsLpRHfSuMB9ee2aAdh0Htza/X3f4djhYISrggqGe3xopNjOcePiSDkDDoPzDYaaMCrbqGP1H2TYU7bgL9PmA=="], + "@sindresorhus/is": ["@sindresorhus/is@7.0.2", "", {}, "sha512-d9xRovfKNz1SKieM0qJdO+PQonjnnIfSNWfHYnBSJ9hkjm0ZPw6HlxscDXYstp3z+7V2GOFHc+J0CYrYTjqCJw=="], "@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@4.0.0", "", {}, "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ=="], @@ -1202,33 +1388,33 @@ "@sveltejs/adapter-cloudflare": ["@sveltejs/adapter-cloudflare@7.0.4", "", { "dependencies": { "@cloudflare/workers-types": "^4.20250507.0", "worktop": "0.8.0-next.18" }, "peerDependencies": { "@sveltejs/kit": "^2.0.0", "wrangler": "^4.0.0" } }, "sha512-pYJDICmhatM9lofkjLR+nhAJ4prEPjmwmx+J7hBuMSfrrNEVk+THfAahuWNizcxae4mO1MQKjIRMLpVnKyNE5g=="], - "@sveltejs/kit": ["@sveltejs/kit@2.21.5", "", { "dependencies": { "@sveltejs/acorn-typescript": "^1.0.5", "@types/cookie": "^0.6.0", "acorn": "^8.14.1", "cookie": "^0.6.0", "devalue": "^5.1.0", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.0", "sirv": "^3.0.0", "vitefu": "^1.0.6" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "vite": "^5.0.3 || ^6.0.0" }, "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-P5m7yZtvD1Kx/Z6JcjgJtdMqef/tCGMDrd9B9S2q8j+FMnkeKTMxW1nidnjVzk4HEDRGf4IlBI94/niy6t3hLA=="], + "@sveltejs/kit": ["@sveltejs/kit@2.22.0", "", { "dependencies": { "@sveltejs/acorn-typescript": "^1.0.5", "@types/cookie": "^0.6.0", "acorn": "^8.14.1", "cookie": "^0.6.0", "devalue": "^5.1.0", "esm-env": "^1.2.2", "kleur": "^4.1.5", "magic-string": "^0.30.5", "mrmime": "^2.0.0", "sade": "^1.8.1", "set-cookie-parser": "^2.6.0", "sirv": "^3.0.0", "vitefu": "^1.0.6" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0", "svelte": "^4.0.0 || ^5.0.0-next.0", "vite": "^5.0.3 || ^6.0.0 || ^7.0.0-beta.0" }, "bin": { "svelte-kit": "svelte-kit.js" } }, "sha512-DJm0UxVgzXq+1MUfiJK4Ridk7oIQsIets6JwHiEl97sI6nXScfXe+BeqNhzB7jQIVBb3BM51U4hNk8qQxRXBAA=="], "@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@5.1.0", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^4.0.1", "debug": "^4.4.1", "deepmerge": "^4.3.1", "kleur": "^4.1.5", "magic-string": "^0.30.17", "vitefu": "^1.0.6" }, "peerDependencies": { "svelte": "^5.0.0", "vite": "^6.0.0" } }, "sha512-wojIS/7GYnJDYIg1higWj2ROA6sSRWvcR1PO/bqEyFr/5UZah26c8Cz4u0NaqjPeVltzsVpt2Tm8d2io0V+4Tw=="], "@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@4.0.1", "", { "dependencies": { "debug": "^4.3.7" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^5.0.0", "svelte": "^5.0.0", "vite": "^6.0.0" } }, "sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw=="], - "@swc/core": ["@swc/core@1.12.1", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.23" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.12.1", "@swc/core-darwin-x64": "1.12.1", "@swc/core-linux-arm-gnueabihf": "1.12.1", "@swc/core-linux-arm64-gnu": "1.12.1", "@swc/core-linux-arm64-musl": "1.12.1", "@swc/core-linux-x64-gnu": "1.12.1", "@swc/core-linux-x64-musl": "1.12.1", "@swc/core-win32-arm64-msvc": "1.12.1", "@swc/core-win32-ia32-msvc": "1.12.1", "@swc/core-win32-x64-msvc": "1.12.1" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-aKXdDTqxTVFl/bKQZ3EQUjEMBEoF6JBv29moMZq0kbVO43na6u/u+3Vcbhbrh+A2N0X5OL4RaveuWfAjEgOmeA=="], + "@swc/core": ["@swc/core@1.12.5", "", { "dependencies": { "@swc/counter": "^0.1.3", "@swc/types": "^0.1.23" }, "optionalDependencies": { "@swc/core-darwin-arm64": "1.12.5", "@swc/core-darwin-x64": "1.12.5", "@swc/core-linux-arm-gnueabihf": "1.12.5", "@swc/core-linux-arm64-gnu": "1.12.5", "@swc/core-linux-arm64-musl": "1.12.5", "@swc/core-linux-x64-gnu": "1.12.5", "@swc/core-linux-x64-musl": "1.12.5", "@swc/core-win32-arm64-msvc": "1.12.5", "@swc/core-win32-ia32-msvc": "1.12.5", "@swc/core-win32-x64-msvc": "1.12.5" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" }, "optionalPeers": ["@swc/helpers"] }, "sha512-KxA0PHHIuUBmQ/Oi+xFpVzILj2Oo37sTtftCbyowQlyx5YOknEOw1kLpas5hMcpznXgFyAWbpK71xQps4INPgA=="], - "@swc/core-darwin-arm64": ["@swc/core-darwin-arm64@1.12.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-nUjWVcJ3YS2N40ZbKwYO2RJ4+o2tWYRzNOcIQp05FqW0+aoUCVMdAUUzQinPDynfgwVshDAXCKemY8X7nN5MaA=="], + "@swc/core-darwin-arm64": ["@swc/core-darwin-arm64@1.12.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-3WF+naP/qkt5flrTfJr+p07b522JcixKvIivM7FgvllA6LjJxf+pheoILrTS8IwrNAK/XtHfKWYcGY+3eaA4mA=="], - "@swc/core-darwin-x64": ["@swc/core-darwin-x64@1.12.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-OGm4a4d3OeJn+tRt8H/eiHgTFrJbS6r8mi/Ob65tAEXZGHN900T2kR7c5ALr0V2hBOQ8BfhexwPoQlGQP/B95w=="], + "@swc/core-darwin-x64": ["@swc/core-darwin-x64@1.12.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-GCcD3dft8YN7unTBcW02Fx41jXp2MNQHCjx5ceWSEYOGvn7vBSUp7k7LkfTxGN5Ftxb9a1mxhPq8r4rD2u/aPw=="], - "@swc/core-linux-arm-gnueabihf": ["@swc/core-linux-arm-gnueabihf@1.12.1", "", { "os": "linux", "cpu": "arm" }, "sha512-76YeeQKyK0EtNkQiNBZ0nbVGooPf9IucY0WqVXVpaU4wuG7ZyLEE2ZAIgXafIuzODGQoLfetue7I8boMxh1/MA=="], + "@swc/core-linux-arm-gnueabihf": ["@swc/core-linux-arm-gnueabihf@1.12.5", "", { "os": "linux", "cpu": "arm" }, "sha512-jWlzP/Y4+wbE/EJM+WGIDQsklLFV3g5LmbYTBgrY4+5nb517P31mkBzf5y2knfNWPrL7HzNu0578j3Zi2E6Iig=="], - "@swc/core-linux-arm64-gnu": ["@swc/core-linux-arm64-gnu@1.12.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-BxJDIJPq1+aCh9UsaSAN6wo3tuln8UhNXruOrzTI8/ElIig/3sAueDM6Eq7GvZSGGSA7ljhNATMJ0elD7lFatQ=="], + "@swc/core-linux-arm64-gnu": ["@swc/core-linux-arm64-gnu@1.12.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-GkzgIUz+2r6J6Tn3hb7/4ByaWHRrRZt4vuN9BLAd+y65m2Bt0vlEpPtWhrB/TVe4hEkFR+W5PDETLEbUT4i0tQ=="], - "@swc/core-linux-arm64-musl": ["@swc/core-linux-arm64-musl@1.12.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-NhLdbffSXvY0/FwUSAl4hKBlpe5GHQGXK8DxTo3HHjLsD9sCPYieo3vG0NQoUYAy4ZUY1WeGjyxeq4qZddJzEQ=="], + "@swc/core-linux-arm64-musl": ["@swc/core-linux-arm64-musl@1.12.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-g0AJ7QmZPj3Uw+C5pDa48LAUG7JBgQmB0mN5cW+s2mjaFKT0mTSxYALtx/MDZwJExDPo0yJV8kSbFO1tvFPyhg=="], - "@swc/core-linux-x64-gnu": ["@swc/core-linux-x64-gnu@1.12.1", "", { "os": "linux", "cpu": "x64" }, "sha512-CrYnV8SZIgArQ9LKH0xEF95PKXzX9WkRSc5j55arOSBeDCeDUQk1Bg/iKdnDiuj5HC1hZpvzwMzSBJjv+Z70jA=="], + "@swc/core-linux-x64-gnu": ["@swc/core-linux-x64-gnu@1.12.5", "", { "os": "linux", "cpu": "x64" }, "sha512-PeYoSziNy+iNiBHPtAsO84bzBne/mbCsG5ijYkAhS1GVsDgohClorUvRXXhcUZoX2gr8TfSI9WLHo30K+DKiHg=="], - "@swc/core-linux-x64-musl": ["@swc/core-linux-x64-musl@1.12.1", "", { "os": "linux", "cpu": "x64" }, "sha512-BQMl3d0HaGB0/h2xcKlGtjk/cGRn2tnbsaChAKcjFdCepblKBCz1pgO/mL7w5iXq3s57wMDUn++71/a5RAkZOA=="], + "@swc/core-linux-x64-musl": ["@swc/core-linux-x64-musl@1.12.5", "", { "os": "linux", "cpu": "x64" }, "sha512-EJrfCCIyuV5LLmYgKtIMwtgsnjVesdFe0IgQzEKs9OfB6cL6g7WO9conn8BkGX8jphVa7jChKxShDGkreWWDzA=="], - "@swc/core-win32-arm64-msvc": ["@swc/core-win32-arm64-msvc@1.12.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-b7NeGnpqTfmIGtUqXBl0KqoSmOnH64nRZoT5l4BAGdvwY7nxitWR94CqZuwyLPty/bLywmyDA9uO12Kvgb3+gg=="], + "@swc/core-win32-arm64-msvc": ["@swc/core-win32-arm64-msvc@1.12.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-FnwT7fxkJJMgsfiDoZKEVGyCzrPFbzpflFAAoTCUCu3MaHw6mW55o/MAAfofvJ1iIcEpec4o93OilsmKtpyO5Q=="], - "@swc/core-win32-ia32-msvc": ["@swc/core-win32-ia32-msvc@1.12.1", "", { "os": "win32", "cpu": "ia32" }, "sha512-iU/29X2D7cHBp1to62cUg/5Xk8K+lyOJiKIGGW5rdzTW/c2zz3d/ehgpzVP/rqC4NVr88MXspqHU4il5gmDajw=="], + "@swc/core-win32-ia32-msvc": ["@swc/core-win32-ia32-msvc@1.12.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-jW6l4KFt9mIXSpGseE6BQOEFmbIeXeShDuWgldEJXKeXf/uPs8wrqv80XBIUwVpK0ZbmJwPQ0waGVj8UM3th2Q=="], - "@swc/core-win32-x64-msvc": ["@swc/core-win32-x64-msvc@1.12.1", "", { "os": "win32", "cpu": "x64" }, "sha512-+Zh+JKDwiFqV5N9yAd2DhYVGPORGh9cfenu1ptr9yge+eHAf7vZJcC3rnj6QMR1QJh0Y5VC9+YBjRFjZVA7XDw=="], + "@swc/core-win32-x64-msvc": ["@swc/core-win32-x64-msvc@1.12.5", "", { "os": "win32", "cpu": "x64" }, "sha512-AZszwuEjlz1tSNLQRm3T5OZJ5eebxjJlDQnnzXJmg0B7DJMRoaAe1HTLOmejxjFK6yWr7fh+pSeCw2PgQLxgqA=="], "@swc/counter": ["@swc/counter@0.1.3", "", {}, "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="], @@ -1266,64 +1452,66 @@ "@tailwindcss/vite": ["@tailwindcss/vite@4.1.10", "", { "dependencies": { "@tailwindcss/node": "4.1.10", "@tailwindcss/oxide": "4.1.10", "tailwindcss": "4.1.10" }, "peerDependencies": { "vite": "^5.2.0 || ^6" } }, "sha512-QWnD5HDY2IADv+vYR82lOhqOlS1jSCUUAmfem52cXAhRTKxpDh3ARX8TTXJTCCO7Rv7cD2Nlekabv02bwP3a2A=="], - "@tanstack/directive-functions-plugin": ["@tanstack/directive-functions-plugin@1.121.19", "", { "dependencies": { "@babel/code-frame": "7.26.2", "@babel/core": "^7.26.8", "@babel/traverse": "^7.26.8", "@babel/types": "^7.26.8", "@tanstack/router-utils": "^1.121.19", "babel-dead-code-elimination": "^1.0.10", "tiny-invariant": "^1.3.3" }, "peerDependencies": { "vite": ">=6.0.0" } }, "sha512-w9kkAgqM2Vjz27pujXkkROTjBjAM9d8YkZ2b253ySPUwHMcASX6be6C/hzRPboodKBcRndzHR6pY32gLF4bBXA=="], + "@tanstack/directive-functions-plugin": ["@tanstack/directive-functions-plugin@1.121.31", "", { "dependencies": { "@babel/code-frame": "7.26.2", "@babel/core": "^7.26.8", "@babel/traverse": "^7.26.8", "@babel/types": "^7.26.8", "@tanstack/router-utils": "^1.121.21", "babel-dead-code-elimination": "^1.0.10", "tiny-invariant": "^1.3.3" }, "peerDependencies": { "vite": ">=6.0.0" } }, "sha512-SC+oUtp7R0TM3wKY1xyGDb5aq0QrshEUxcpVJhJ/fUTKI9DClOW5uuQuwbaY5SW0pM0PbwzwMwau8JtXVKJNiQ=="], - "@tanstack/history": ["@tanstack/history@1.115.0", "", {}, "sha512-K7JJNrRVvyjAVnbXOH2XLRhFXDkeP54Kt2P4FR1Kl2KDGlIbkua5VqZQD2rot3qaDrpufyUa63nuLai1kOLTsQ=="], + "@tanstack/history": ["@tanstack/history@1.121.34", "", {}, "sha512-YL8dGi5ZU+xvtav2boRlw4zrRghkY6hvdcmHhA0RGSJ/CBgzv+cbADW9eYJLx74XMZvIQ1pp6VMbrpXnnM5gHA=="], - "@tanstack/react-router": ["@tanstack/react-router@1.116.0", "", { "dependencies": { "@tanstack/history": "1.115.0", "@tanstack/react-store": "^0.7.0", "@tanstack/router-core": "1.115.3", "jsesc": "^3.1.0", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-ZBAg5Q6zJf0mnP9DYPiaaQ/wLDH2ujCMi/2RllpH86VUkdkyvQQzpAyKoiYJ891wh9OPgj6W6tPrzB4qy5FpRA=="], + "@tanstack/react-router": ["@tanstack/react-router@1.121.34", "", { "dependencies": { "@tanstack/history": "1.121.34", "@tanstack/react-store": "^0.7.0", "@tanstack/router-core": "1.121.34", "jsesc": "^3.1.0", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-nQYUXh459/YX9tDOGUqBb8yCiUw4JjcCf1o9wtb9fMxy3hnP0iQNU2TeV1A1N4KCGKXV3ZzkhpBb6sJe3kd43Q=="], - "@tanstack/react-router-devtools": ["@tanstack/react-router-devtools@1.116.0", "", { "dependencies": { "@tanstack/router-devtools-core": "^1.115.3", "solid-js": "^1.9.5" }, "peerDependencies": { "@tanstack/react-router": "^1.116.0", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-PsJZWPjcmwZGe71kUvH4bI1ozkv1FgBuBEE0hTYlTCSJ3uG+qv3ndGEI+AiFyuF5OStrbfg0otW1OxeNq5vdGQ=="], + "@tanstack/react-router-devtools": ["@tanstack/react-router-devtools@1.121.34", "", { "dependencies": { "@tanstack/router-devtools-core": "^1.121.34" }, "peerDependencies": { "@tanstack/react-router": "^1.121.34", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-rqbgqTT5QaxeEX5oEI3K/9kDRRhCq3daszjZIWEWa9KKA8Fo4cE9F5OIQeXKdFu7QrIlHurlE7HB2uLkjgVviw=="], - "@tanstack/react-start": ["@tanstack/react-start@1.116.1", "", { "dependencies": { "@tanstack/react-start-client": "^1.116.0", "@tanstack/react-start-config": "^1.116.1", "@tanstack/react-start-router-manifest": "^1.115.3", "@tanstack/react-start-server": "^1.116.0", "@tanstack/start-api-routes": "^1.115.3", "@tanstack/start-server-functions-client": "^1.115.3", "@tanstack/start-server-functions-handler": "^1.115.3", "@tanstack/start-server-functions-server": "^1.115.0", "@tanstack/start-server-functions-ssr": "^1.115.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0", "vite": "^6.0.0" } }, "sha512-D8nf/OsFWzqYQefjxpVhpVh6sXnDCTzLOHmOJFDnrDOyRz15qAQUu5Vd85xDyF/FQR2nU8vX5sgjjrpceoS9nw=="], + "@tanstack/react-start": ["@tanstack/react-start@1.121.34", "", { "dependencies": { "@tanstack/react-start-client": "1.121.34", "@tanstack/react-start-plugin": "1.121.34", "@tanstack/react-start-server": "1.121.34", "@tanstack/start-server-functions-client": "1.121.34", "@tanstack/start-server-functions-server": "1.121.31" }, "peerDependencies": { "@vitejs/plugin-react": ">=4.3.4", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0", "vite": ">=6.0.0" } }, "sha512-RtCqttI1zIkZxs9ezqH5bTTGGcN1z8ShkQldYjCdtIE7G02LHwBQicuduuUr86F8Z5SXbYoWydWI/J08++leWQ=="], - "@tanstack/react-start-client": ["@tanstack/react-start-client@1.121.19", "", { "dependencies": { "@tanstack/react-router": "^1.121.19", "@tanstack/router-core": "^1.121.19", "@tanstack/start-client-core": "^1.121.19", "cookie-es": "^1.2.2", "jsesc": "^3.1.0", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-2ZS67w3exGS5rAIKrD8Tg3ua78xt7gxt8uyVAx7yZsuzPj/TDCE4SZIDI0ZNMPo17uBMdoYsSFFSFyjQeeScgg=="], + "@tanstack/react-start-client": ["@tanstack/react-start-client@1.121.34", "", { "dependencies": { "@tanstack/react-router": "1.121.34", "@tanstack/router-core": "1.121.34", "@tanstack/start-client-core": "1.121.34", "cookie-es": "^1.2.2", "jsesc": "^3.1.0", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-jmxu1SoHcEotbnkDmlnA7dEWf02n7gmrwfxM4SkKpzdIdP3P3l0IqKhkD6Rwu7T8lssdWuzfgw1hanhJ+KVZOw=="], "@tanstack/react-start-config": ["@tanstack/react-start-config@1.120.20", "", { "dependencies": { "@tanstack/react-start-plugin": "^1.120.17", "@tanstack/router-core": "^1.120.19", "@tanstack/router-generator": "^1.120.20", "@tanstack/router-plugin": "^1.120.20", "@tanstack/server-functions-plugin": "^1.120.17", "@tanstack/start-server-functions-handler": "^1.120.19", "@vitejs/plugin-react": "^4.3.4", "import-meta-resolve": "^4.1.0", "nitropack": "^2.10.4", "ofetch": "^1.4.1", "vinxi": "0.5.3", "vite": "^6.1.0", "zod": "^3.24.2" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-wJ+1hMoCUUo9gcgN9tj+vjXJA1M7ZBa2/tWLuuxGMna+DMcol6Ycx0AcQ88D34/j6fNLOJgPhQAQWYPDBQIPow=="], - "@tanstack/react-start-plugin": ["@tanstack/react-start-plugin@1.121.19", "", { "dependencies": { "@tanstack/router-utils": "^1.121.19", "@tanstack/start-plugin-core": "^1.121.19", "zod": "^3.24.2" }, "peerDependencies": { "@vitejs/plugin-react": ">=4.3.4", "vite": ">=6.0.0" } }, "sha512-SevG+67QzqQWciyWoppHBnT/U5uuzXUnfefY4YJF/Qr1LzcOKE4QNWPwL/SG9y2RX78SxQVr9M//XImTlj6TlA=="], + "@tanstack/react-start-plugin": ["@tanstack/react-start-plugin@1.121.34", "", { "dependencies": { "@tanstack/start-plugin-core": "1.121.34", "zod": "^3.24.2" }, "peerDependencies": { "@vitejs/plugin-react": ">=4.3.4", "vite": ">=6.0.0" } }, "sha512-cb4PA4JC75PX6uDTbuz5vweLrAwXdvX/ys2jdPtoxJofz8LBGSFvVqSWxRvk2asNV5KAO8KTcOVUOC8aentcGw=="], "@tanstack/react-start-router-manifest": ["@tanstack/react-start-router-manifest@1.120.19", "", { "dependencies": { "@tanstack/router-core": "^1.120.19", "tiny-invariant": "^1.3.3", "vinxi": "0.5.3" } }, "sha512-z+4YL6shTtsHjk32yaIemQwgkx6FcqwPBYfeNt7Co2eOpWrvsoo/Fe9869/oIY2sPyhiWDs1rDb3e0qnAy8Cag=="], - "@tanstack/react-start-server": ["@tanstack/react-start-server@1.121.19", "", { "dependencies": { "@tanstack/history": "^1.121.19", "@tanstack/react-router": "^1.121.19", "@tanstack/router-core": "^1.121.19", "@tanstack/start-client-core": "^1.121.19", "@tanstack/start-server-core": "^1.121.19", "h3": "1.13.0", "isbot": "^5.1.22", "jsesc": "^3.1.0", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3", "unctx": "^2.4.1" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-6h1CT4RkcSmM7x6F9Cf6z0teVFmBBVreb192QW5XDXD6lbwy2wMf2h3Un2Zc/zlbHP6oB1LS7tci3XzGUj/6Eg=="], + "@tanstack/react-start-server": ["@tanstack/react-start-server@1.121.34", "", { "dependencies": { "@tanstack/history": "^1.121.34", "@tanstack/react-router": "^1.121.34", "@tanstack/router-core": "^1.121.34", "@tanstack/start-client-core": "1.121.34", "@tanstack/start-server-core": "1.121.34", "h3": "1.13.0", "isbot": "^5.1.22" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-ZjUzm24clALpQlrbPvJejuqQUScdmfqV05o3b9D35gnpLe2ME6zEKFgBMSYgJJ+6AAfIf7lw0B10hMT9vSbSyQ=="], "@tanstack/react-store": ["@tanstack/react-store@0.7.1", "", { "dependencies": { "@tanstack/store": "0.7.1", "use-sync-external-store": "^1.5.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-qUTEKdId6QPWGiWyKAPf/gkN29scEsz6EUSJ0C3HgLMgaqTAyBsQ2sMCfGVcqb+kkhEXAdjleCgH6LAPD6f2sA=="], - "@tanstack/router-core": ["@tanstack/router-core@1.115.3", "", { "dependencies": { "@tanstack/history": "1.115.0", "@tanstack/store": "^0.7.0", "tiny-invariant": "^1.3.3" } }, "sha512-gynHs72LHVg05fuJTwZZYhDL4VNEAK0sXz7IqiBv7a3qsYeEmIZsGaFr9sVjTkuF1kbrFBdJd5JYutzBh9Uuhw=="], + "@tanstack/router-core": ["@tanstack/router-core@1.121.34", "", { "dependencies": { "@tanstack/history": "1.121.34", "@tanstack/store": "^0.7.0", "tiny-invariant": "^1.3.3" } }, "sha512-CRH9dC8uLfFOKUGTbtOcMPv+weNVt2xs+me34KLX0Yja2yHG99oAUCBwamXsVQPpfjLFPYeJuKyo98+Mg+Ppeg=="], - "@tanstack/router-devtools-core": ["@tanstack/router-devtools-core@1.121.19", "", { "dependencies": { "clsx": "^2.1.1", "goober": "^2.1.16", "solid-js": "^1.9.5" }, "peerDependencies": { "@tanstack/router-core": "^1.121.19", "csstype": "^3.0.10", "tiny-invariant": "^1.3.3" }, "optionalPeers": ["csstype"] }, "sha512-isXd3EOuUmq/paR+Gj2gfkY8+PxEVVNX0QyQbxrYTfAVbbhSwQOYjdqTKnweoEPzplZCieyvJcVcF3fW/XIV6g=="], + "@tanstack/router-devtools-core": ["@tanstack/router-devtools-core@1.121.34", "", { "dependencies": { "clsx": "^2.1.1", "goober": "^2.1.16", "solid-js": "^1.9.5" }, "peerDependencies": { "@tanstack/router-core": "^1.121.34", "csstype": "^3.0.10", "tiny-invariant": "^1.3.3" }, "optionalPeers": ["csstype"] }, "sha512-WAFYxJ7qViKxqkFmf+VsrtMT4TfYqdfWTBRhVU/6qi0k/+7TO2EHjl8/aGBhg6q0/IwO9wyGvcbDhJxm0DwWag=="], - "@tanstack/router-generator": ["@tanstack/router-generator@1.121.19", "", { "dependencies": { "@tanstack/router-core": "^1.121.19", "@tanstack/router-utils": "^1.121.19", "@tanstack/virtual-file-routes": "^1.121.19", "prettier": "^3.5.0", "recast": "^0.23.11", "source-map": "^0.7.4", "tsx": "^4.19.2", "zod": "^3.24.2" } }, "sha512-5E7us5WZMQu6LlgfOZFK7fQb2Z7AuRAMKJRcBxPR0mRo/LiXBAdsS2jIy0YHwzWO+6aGreyyCvCBJQhDRCoE/w=="], + "@tanstack/router-generator": ["@tanstack/router-generator@1.121.34", "", { "dependencies": { "@tanstack/router-core": "^1.121.34", "@tanstack/router-utils": "^1.121.21", "@tanstack/virtual-file-routes": "^1.121.21", "prettier": "^3.5.0", "recast": "^0.23.11", "source-map": "^0.7.4", "tsx": "^4.19.2", "zod": "^3.24.2" } }, "sha512-JmxlhK8f7LIxHV8BAHikeiYGfwM9p5nxbEMpujNgTmC0dBwSyes+Zm0DzEL0EotVXZy+CyI/9bVa7z+9nWvqlA=="], - "@tanstack/router-plugin": ["@tanstack/router-plugin@1.121.19", "", { "dependencies": { "@babel/core": "^7.26.8", "@babel/plugin-syntax-jsx": "^7.25.9", "@babel/plugin-syntax-typescript": "^7.25.9", "@babel/template": "^7.26.8", "@babel/traverse": "^7.26.8", "@babel/types": "^7.26.8", "@tanstack/router-core": "^1.121.19", "@tanstack/router-generator": "^1.121.19", "@tanstack/router-utils": "^1.121.19", "@tanstack/virtual-file-routes": "^1.121.19", "babel-dead-code-elimination": "^1.0.10", "chokidar": "^3.6.0", "unplugin": "^2.1.2", "zod": "^3.24.2" }, "peerDependencies": { "@rsbuild/core": ">=1.0.2", "@tanstack/react-router": "^1.121.19", "vite": ">=5.0.0 || >=6.0.0", "vite-plugin-solid": "^2.11.2", "webpack": ">=5.92.0" }, "optionalPeers": ["@rsbuild/core", "@tanstack/react-router", "vite", "vite-plugin-solid", "webpack"] }, "sha512-Hl7snQR+typaYnZzBkbxRhiesvFiiL9H8Lxqeh84hM6MirN05pMREcK1OZBoFD8B2bUOvRVZhNzRcXf2uaqGEQ=="], + "@tanstack/router-plugin": ["@tanstack/router-plugin@1.121.34", "", { "dependencies": { "@babel/core": "^7.26.8", "@babel/plugin-syntax-jsx": "^7.25.9", "@babel/plugin-syntax-typescript": "^7.25.9", "@babel/template": "^7.26.8", "@babel/traverse": "^7.26.8", "@babel/types": "^7.26.8", "@tanstack/router-core": "^1.121.34", "@tanstack/router-generator": "^1.121.34", "@tanstack/router-utils": "^1.121.21", "@tanstack/virtual-file-routes": "^1.121.21", "babel-dead-code-elimination": "^1.0.10", "chokidar": "^3.6.0", "unplugin": "^2.1.2", "zod": "^3.24.2" }, "peerDependencies": { "@rsbuild/core": ">=1.0.2", "@tanstack/react-router": "^1.121.34", "vite": ">=5.0.0 || >=6.0.0", "vite-plugin-solid": "^2.11.2", "webpack": ">=5.92.0" }, "optionalPeers": ["@rsbuild/core", "@tanstack/react-router", "vite", "vite-plugin-solid", "webpack"] }, "sha512-ZmX/tkdd/ZKLdr17ewKJTTBGkXQDeOfQKSCuuEW5IjiNfWjT5gx8rQDvcYUSRcZdpUZ0LvDBxJUI74oHQ3sAiw=="], - "@tanstack/router-utils": ["@tanstack/router-utils@1.121.19", "", { "dependencies": { "@babel/core": "^7.27.4", "@babel/generator": "^7.27.5", "@babel/parser": "^7.27.5", "@babel/preset-typescript": "^7.27.1", "ansis": "^4.1.0", "diff": "^8.0.2" } }, "sha512-7cBeXvrtHTpQSLNfBE4c47GYd6xmlxaBng2j0sUYWXlm0+uEzMaSfnrVOlqI1ZWy6/nivOEy86js6E79ziPjzw=="], + "@tanstack/router-utils": ["@tanstack/router-utils@1.121.21", "", { "dependencies": { "@babel/core": "^7.27.4", "@babel/generator": "^7.27.5", "@babel/parser": "^7.27.5", "@babel/preset-typescript": "^7.27.1", "ansis": "^4.1.0", "diff": "^8.0.2" } }, "sha512-u7ubq1xPBtNiU7Fm+EOWlVWdgFLzuKOa1thhqdscVn8R4dNMUd1VoOjZ6AKmLw201VaUhFtlX+u0pjzI6szX7A=="], - "@tanstack/server-functions-plugin": ["@tanstack/server-functions-plugin@1.121.19", "", { "dependencies": { "@babel/code-frame": "7.26.2", "@babel/core": "^7.26.8", "@babel/plugin-syntax-jsx": "^7.25.9", "@babel/plugin-syntax-typescript": "^7.25.9", "@babel/template": "^7.26.8", "@babel/traverse": "^7.26.8", "@babel/types": "^7.26.8", "@tanstack/directive-functions-plugin": "1.121.19", "babel-dead-code-elimination": "^1.0.9", "tiny-invariant": "^1.3.3" } }, "sha512-QedgKlEA4uXGA7MV7Uy6nji9q6ySQ6QfjSxnNf8jeS0mdm/4MLmvDVBYVrjT+wksDDr9RRGrBo1LK0DToDlSdg=="], + "@tanstack/server-functions-plugin": ["@tanstack/server-functions-plugin@1.121.31", "", { "dependencies": { "@babel/code-frame": "7.26.2", "@babel/core": "^7.26.8", "@babel/plugin-syntax-jsx": "^7.25.9", "@babel/plugin-syntax-typescript": "^7.25.9", "@babel/template": "^7.26.8", "@babel/traverse": "^7.26.8", "@babel/types": "^7.26.8", "@tanstack/directive-functions-plugin": "1.121.31", "babel-dead-code-elimination": "^1.0.9", "tiny-invariant": "^1.3.3" } }, "sha512-K5SKY/CgWAOlPhduhaJxAcVxlVYQa7cCQJZITbc87/Y06bB2Iau0olG1mE3M0KqphRpMnbXl7hrDxHwg65L3sg=="], "@tanstack/start-api-routes": ["@tanstack/start-api-routes@1.120.19", "", { "dependencies": { "@tanstack/router-core": "^1.120.19", "@tanstack/start-server-core": "^1.120.19", "vinxi": "0.5.3" } }, "sha512-zvMI9Rfwsm3CCLTLqdvUfteDRMdPKTOO05O3L8vp49BrYYsLrT0OplhounzdRMgGMnKd4qCXUC9Pj4UOUOodTw=="], - "@tanstack/start-client-core": ["@tanstack/start-client-core@1.121.19", "", { "dependencies": { "@tanstack/router-core": "^1.121.19", "cookie-es": "^1.2.2", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" } }, "sha512-NCjZdgCvMvFxanx1cpdsAL8kn9ulvlaJwY8kYER5kL09B88Ow32JgSr98DH3LzEzr1VBP1/jPF893mMre+fe/A=="], + "@tanstack/start-client-core": ["@tanstack/start-client-core@1.121.34", "", { "dependencies": { "@tanstack/router-core": "1.121.34", "cookie-es": "^1.2.2", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" } }, "sha512-YDTMFPucF06LAtfM40qhQcWf1YbO8KDRvXuWZbVdKQDgD4CTOpmlwYSsAf2yaCPInCZF0ukRZixq3lWgu8JKUw=="], - "@tanstack/start-plugin-core": ["@tanstack/start-plugin-core@1.121.19", "", { "dependencies": { "@babel/code-frame": "7.26.2", "@babel/core": "^7.26.8", "@babel/types": "^7.26.8", "@tanstack/router-core": "^1.121.19", "@tanstack/router-generator": "^1.121.19", "@tanstack/router-plugin": "^1.121.19", "@tanstack/router-utils": "^1.121.19", "@tanstack/server-functions-plugin": "^1.121.19", "@tanstack/start-server-core": "^1.121.19", "@types/babel__code-frame": "^7.0.6", "@types/babel__core": "^7.20.5", "babel-dead-code-elimination": "^1.0.9", "cheerio": "^1.0.0", "h3": "1.13.0", "nitropack": "^2.11.12", "pathe": "^2.0.3", "ufo": "^1.5.4", "xmlbuilder2": "^3.1.1", "zod": "^3.24.2" }, "peerDependencies": { "vite": ">=6.0.0" } }, "sha512-KGlN4xLURDxmD+Z6mvJqm1MES9xWOOdgKSaZW2Y+olKX3PIr8yH3Z/qX2Qmyog6TVGxP9HUUDDMzz6P72vmxTw=="], + "@tanstack/start-plugin-core": ["@tanstack/start-plugin-core@1.121.34", "", { "dependencies": { "@babel/code-frame": "7.26.2", "@babel/core": "^7.26.8", "@babel/types": "^7.26.8", "@tanstack/router-core": "1.121.34", "@tanstack/router-generator": "1.121.34", "@tanstack/router-plugin": "1.121.34", "@tanstack/router-utils": "1.121.21", "@tanstack/server-functions-plugin": "1.121.31", "@tanstack/start-server-core": "1.121.34", "@types/babel__code-frame": "^7.0.6", "@types/babel__core": "^7.20.5", "babel-dead-code-elimination": "^1.0.9", "cheerio": "^1.0.0", "h3": "1.13.0", "nitropack": "^2.11.12", "pathe": "^2.0.3", "ufo": "^1.5.4", "xmlbuilder2": "^3.1.1", "zod": "^3.24.2" }, "peerDependencies": { "vite": ">=6.0.0" } }, "sha512-4E6uUneY/N2W0gckm+OTMN2LRoLonfT351bIDI6z+jl4ZcKXzb8+XD2fBej1psAKpH+ejttQnSr56UeYmqOosQ=="], - "@tanstack/start-server-core": ["@tanstack/start-server-core@1.121.19", "", { "dependencies": { "@tanstack/history": "^1.121.19", "@tanstack/router-core": "^1.121.19", "@tanstack/start-client-core": "^1.121.19", "h3": "1.13.0", "isbot": "^5.1.22", "jsesc": "^3.1.0", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3", "unctx": "^2.4.1" } }, "sha512-K2Lxi6MR9QEJgA8+HWBIe/Mw/gaCGaufpjuN/XugkQW9UvY+0S0rSar1WEUIdunTePyqdIn0tKlFpxG+uwsjuw=="], + "@tanstack/start-server-core": ["@tanstack/start-server-core@1.121.34", "", { "dependencies": { "@tanstack/history": "1.121.34", "@tanstack/router-core": "1.121.34", "@tanstack/start-client-core": "1.121.34", "h3": "1.13.0", "isbot": "^5.1.22", "jsesc": "^3.1.0", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3", "unctx": "^2.4.1" } }, "sha512-KWn2QqfanIOo/I+ZSDobKT9pFo9+KSlGm/8uCpYYEJ8qVqHb4UEUzznxasy3pswk+OsX9FcTobyUmNF7usSwhQ=="], - "@tanstack/start-server-functions-client": ["@tanstack/start-server-functions-client@1.121.19", "", { "dependencies": { "@tanstack/server-functions-plugin": "^1.121.19", "@tanstack/start-server-functions-fetcher": "^1.121.19" } }, "sha512-wiceXGof0goX/rNI7EOOXp5t/WINqLVm3Gl+jSyPJ23xRTRGA1wouqXs0neG19DANCNCdGkTrkR/3QPlm7mM7g=="], + "@tanstack/start-server-functions-client": ["@tanstack/start-server-functions-client@1.121.34", "", { "dependencies": { "@tanstack/server-functions-plugin": "1.121.31", "@tanstack/start-server-functions-fetcher": "1.121.34" } }, "sha512-xPiL9Z+E7ajTeaw30ZfeTZGd2Wb3TEg5yG+NiE+NuVmj9/fOSQ4nyrGE0/fdFeD6RyNyzrpUUcpSaa0sYGbgQg=="], - "@tanstack/start-server-functions-fetcher": ["@tanstack/start-server-functions-fetcher@1.121.19", "", { "dependencies": { "@tanstack/router-core": "^1.121.19", "@tanstack/start-client-core": "^1.121.19" } }, "sha512-Fzhv8eTsnJQXElYq6T2orLdguLM6DZAjwl6h9bqCvx+wps9X4vd6q+WHUorLxF+xcrkMjfQkeMkDlS8UgPrc4g=="], + "@tanstack/start-server-functions-fetcher": ["@tanstack/start-server-functions-fetcher@1.121.34", "", { "dependencies": { "@tanstack/router-core": "1.121.34", "@tanstack/start-client-core": "1.121.34" } }, "sha512-fjRvSUOMXauVA1MrOoNt94yIXd93oJuKt/jbiwJfesz4MiFnFVetqmLUb0ruCCRUXrYGPiupI/AO99lRhcfKnw=="], "@tanstack/start-server-functions-handler": ["@tanstack/start-server-functions-handler@1.120.19", "", { "dependencies": { "@tanstack/router-core": "^1.120.19", "@tanstack/start-client-core": "^1.120.19", "@tanstack/start-server-core": "^1.120.19", "tiny-invariant": "^1.3.3" } }, "sha512-Ow8HkNieoqHumD3QK4YUDIhzBtFX9mMEDrxFYtbVBgxP1C9Rm/YDuwnUNP49q1tTOZ22Bs4wSDjBXvu+OgSSfA=="], - "@tanstack/start-server-functions-server": ["@tanstack/start-server-functions-server@1.121.19", "", { "dependencies": { "@tanstack/server-functions-plugin": "^1.121.19", "tiny-invariant": "^1.3.3" } }, "sha512-pTzngvgCe/IoaoVLixh5ITfzemJdeW97RStbkxpcwFFh+G7pwvZoLSpPju4BsCxsh1cGh01K9GAAOTdX9aQIDg=="], + "@tanstack/start-server-functions-server": ["@tanstack/start-server-functions-server@1.121.31", "", { "dependencies": { "@tanstack/server-functions-plugin": "1.121.31", "tiny-invariant": "^1.3.3" } }, "sha512-1EwlX7egLAEvqAQXM55Bn5aJJ1l0gEVn7316ftkXBFzwZiPi2NobsgbpLqQcyJ6y6KUeYKnOhHVqFaRRFHILUg=="], "@tanstack/start-server-functions-ssr": ["@tanstack/start-server-functions-ssr@1.120.19", "", { "dependencies": { "@tanstack/server-functions-plugin": "^1.120.17", "@tanstack/start-client-core": "^1.120.19", "@tanstack/start-server-core": "^1.120.19", "@tanstack/start-server-functions-fetcher": "^1.120.19", "tiny-invariant": "^1.3.3" } }, "sha512-D4HGvJXWvVUssgkLDtdSJTFfWuT+nVv9GauPfVQTtMUUy+NbExNkFWKvF+XvCS81lBqnCKL7VrWqZMXiod0gTA=="], "@tanstack/store": ["@tanstack/store@0.7.1", "", {}, "sha512-PjUQKXEXhLYj2X5/6c1Xn/0/qKY0IVFxTJweopRfF26xfjVyb14yALydJrHupDh3/d+1WKmfEgZPBVCmDkzzwg=="], - "@tanstack/virtual-file-routes": ["@tanstack/virtual-file-routes@1.121.19", "", {}, "sha512-AuMbsG+FEFH2aP2SmdyH0nU6Dz4udt7YdULiQFs30tXdnCz1wDysU5w2TqjSEuEZ2ZSxoux2l6wJVQzzfqDhXw=="], + "@tanstack/virtual-file-routes": ["@tanstack/virtual-file-routes@1.121.21", "", {}, "sha512-3nuYsTyaq6ZN7jRZ9z6Gj3GXZqBOqOT0yzd/WZ33ZFfv4yVNIvsa5Lw+M1j3sgyEAxKMqGu/FaNi7FCjr3yOdw=="], "@tootallnate/quickjs-emscripten": ["@tootallnate/quickjs-emscripten@0.23.0", "", {}, "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA=="], + "@trpc/server": ["@trpc/server@11.4.2", "", { "peerDependencies": { "typescript": ">=5.7.2" } }, "sha512-THyq/V5bSFDHeWEAk6LqHF0IVTGk6voGwWsFEipzRRKOWWMIZINCsKZ4cISG6kWO2X9jBfMWv/S2o9hnC0zQ0w=="], + "@trysound/sax": ["@trysound/sax@0.2.0", "", {}, "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA=="], "@ts-morph/common": ["@ts-morph/common@0.26.1", "", { "dependencies": { "fast-glob": "^3.3.2", "minimatch": "^9.0.4", "path-browserify": "^1.0.1" } }, "sha512-Sn28TGl/4cFpcM+jwsH1wLncYq3FtN/BIpem+HOygfBWPT5pAeS5dB4VFVzV8FbnOKHpDLZmvAl4AjPEev5idA=="], @@ -1342,7 +1530,7 @@ "@types/braces": ["@types/braces@3.0.5", "", {}, "sha512-SQFof9H+LXeWNz8wDe7oN5zu7ket0qwMu5vZubW4GCJ8Kkeh6nBWUz87+KTz/G3Kqsrp0j/W253XJb3KMEeg3w=="], - "@types/bun": ["@types/bun@1.2.16", "", { "dependencies": { "bun-types": "1.2.16" } }, "sha512-1aCZJ/6nSiViw339RsaNhkNoEloLaPzZhxMOYEa7OzRzO41IGg5n/7I43/ZIAW/c+Q6cT12Vf7fOZOoVIzb5BQ=="], + "@types/bun": ["@types/bun@1.2.17", "", { "dependencies": { "bun-types": "1.2.17" } }, "sha512-l/BYs/JYt+cXA/0+wUhulYJB6a6p//GTPiJ7nV+QHa8iiId4HZmnu/3J/SowP5g0rTiERY2kfGKXEK5Ehltx4Q=="], "@types/chai": ["@types/chai@5.2.2", "", { "dependencies": { "@types/deep-eql": "*" } }, "sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg=="], @@ -1394,6 +1582,8 @@ "@types/normalize-package-data": ["@types/normalize-package-data@2.4.4", "", {}, "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA=="], + "@types/omelette": ["@types/omelette@0.4.5", "", {}, "sha512-zUCJpVRwfMcZfkxSCGp73mgd3/xesvPz5tQJIORlfP/zkYEyp9KUfF7IP3RRjyZR3DwxkPs96/IFf70GmYZYHQ=="], + "@types/parse-path": ["@types/parse-path@7.1.0", "", { "dependencies": { "parse-path": "*" } }, "sha512-EULJ8LApcVEPbrfND0cRQqutIOdiIgJ1Mgrhpy755r14xMohPTEpkV/k28SJvuOs9bHRFW8x+KeDAEPiGQPB9Q=="], "@types/react": ["@types/react@19.1.8", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g=="], @@ -1446,27 +1636,27 @@ "@vinxi/listhen": ["@vinxi/listhen@1.5.6", "", { "dependencies": { "@parcel/watcher": "^2.3.0", "@parcel/watcher-wasm": "2.3.0", "citty": "^0.1.5", "clipboardy": "^4.0.0", "consola": "^3.2.3", "defu": "^6.1.4", "get-port-please": "^3.1.2", "h3": "^1.10.0", "http-shutdown": "^1.2.2", "jiti": "^1.21.0", "mlly": "^1.5.0", "node-forge": "^1.3.1", "pathe": "^1.1.2", "std-env": "^3.7.0", "ufo": "^1.3.2", "untun": "^0.1.3", "uqr": "^0.1.2" }, "bin": { "listen": "bin/listhen.mjs", "listhen": "bin/listhen.mjs" } }, "sha512-WSN1z931BtasZJlgPp704zJFnQFRg7yzSjkm3MzAWQYe4uXFXlFr1hc5Ac2zae5/HDOz5x1/zDM5Cb54vTCnWw=="], - "@vitejs/plugin-react": ["@vitejs/plugin-react@4.5.2", "", { "dependencies": { "@babel/core": "^7.27.4", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.11", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" } }, "sha512-QNVT3/Lxx99nMQWJWF7K4N6apUEuT0KlZA3mx/mVaoGj3smm/8rc8ezz15J1pcbcjDK0V15rpHetVfya08r76Q=="], + "@vitejs/plugin-react": ["@vitejs/plugin-react@4.6.0", "", { "dependencies": { "@babel/core": "^7.27.4", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.19", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" } }, "sha512-5Kgff+m8e2PB+9j51eGHEpn5kUzRKH2Ry0qGoe8ItJg7pqnkPrYPkDQZGgGmTa0EGarHrkjLvOdU3b1fzI8otQ=="], "@vitejs/plugin-vue": ["@vitejs/plugin-vue@5.2.4", "", { "peerDependencies": { "vite": "^5.0.0 || ^6.0.0", "vue": "^3.2.25" } }, "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA=="], "@vitejs/plugin-vue-jsx": ["@vitejs/plugin-vue-jsx@4.2.0", "", { "dependencies": { "@babel/core": "^7.27.1", "@babel/plugin-transform-typescript": "^7.27.1", "@rolldown/pluginutils": "^1.0.0-beta.9", "@vue/babel-plugin-jsx": "^1.4.0" }, "peerDependencies": { "vite": "^5.0.0 || ^6.0.0", "vue": "^3.0.0" } }, "sha512-DSTrmrdLp+0LDNF77fqrKfx7X0ErRbOcUAgJL/HbSesqQwoUvUQ4uYQqaex+rovqgGcoPqVk+AwUh3v9CuiYIw=="], - "@vitest/expect": ["@vitest/expect@3.2.3", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.3", "@vitest/utils": "3.2.3", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-W2RH2TPWVHA1o7UmaFKISPvdicFJH+mjykctJFoAkUw+SPTJTGjUNdKscFBrqM7IPnCVu6zihtKYa7TkZS1dkQ=="], + "@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], - "@vitest/mocker": ["@vitest/mocker@3.2.3", "", { "dependencies": { "@vitest/spy": "3.2.3", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-cP6fIun+Zx8he4rbWvi+Oya6goKQDZK+Yq4hhlggwQBbrlOQ4qtZ+G4nxB6ZnzI9lyIb+JnvyiJnPC2AGbKSPA=="], + "@vitest/mocker": ["@vitest/mocker@3.2.4", "", { "dependencies": { "@vitest/spy": "3.2.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ=="], - "@vitest/pretty-format": ["@vitest/pretty-format@3.2.3", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-yFglXGkr9hW/yEXngO+IKMhP0jxyFw2/qys/CK4fFUZnSltD+MU7dVYGrH8rvPcK/O6feXQA+EU33gjaBBbAng=="], + "@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="], - "@vitest/runner": ["@vitest/runner@3.2.3", "", { "dependencies": { "@vitest/utils": "3.2.3", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-83HWYisT3IpMaU9LN+VN+/nLHVBCSIUKJzGxC5RWUOsK1h3USg7ojL+UXQR3b4o4UBIWCYdD2fxuzM7PQQ1u8w=="], + "@vitest/runner": ["@vitest/runner@3.2.4", "", { "dependencies": { "@vitest/utils": "3.2.4", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ=="], - "@vitest/snapshot": ["@vitest/snapshot@3.2.3", "", { "dependencies": { "@vitest/pretty-format": "3.2.3", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-9gIVWx2+tysDqUmmM1L0hwadyumqssOL1r8KJipwLx5JVYyxvVRfxvMq7DaWbZZsCqZnu/dZedaZQh4iYTtneA=="], + "@vitest/snapshot": ["@vitest/snapshot@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ=="], - "@vitest/spy": ["@vitest/spy@3.2.3", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-JHu9Wl+7bf6FEejTCREy+DmgWe+rQKbK+y32C/k5f4TBIAlijhJbRBIRIOCEpVevgRsCQR2iHRUH2/qKVM/plw=="], + "@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], - "@vitest/ui": ["@vitest/ui@3.2.3", "", { "dependencies": { "@vitest/utils": "3.2.3", "fflate": "^0.8.2", "flatted": "^3.3.3", "pathe": "^2.0.3", "sirv": "^3.0.1", "tinyglobby": "^0.2.14", "tinyrainbow": "^2.0.0" }, "peerDependencies": { "vitest": "3.2.3" } }, "sha512-9aR2tY/WT7GRHGEH/9sSIipJqeA21Eh3C6xmiOVmfyBCFmezUSUFLalpaSmRHlRzWCKQU10yz3AHhKuYcdnZGQ=="], + "@vitest/ui": ["@vitest/ui@3.2.4", "", { "dependencies": { "@vitest/utils": "3.2.4", "fflate": "^0.8.2", "flatted": "^3.3.3", "pathe": "^2.0.3", "sirv": "^3.0.1", "tinyglobby": "^0.2.14", "tinyrainbow": "^2.0.0" }, "peerDependencies": { "vitest": "3.2.4" } }, "sha512-hGISOaP18plkzbWEcP/QvtRW1xDXF2+96HbEX6byqQhAUbiS5oH6/9JwW+QsQCIYON2bI6QZBF+2PvOmrRZ9wA=="], - "@vitest/utils": ["@vitest/utils@3.2.3", "", { "dependencies": { "@vitest/pretty-format": "3.2.3", "loupe": "^3.1.3", "tinyrainbow": "^2.0.0" } }, "sha512-4zFBCU5Pf+4Z6v+rwnZ1HU1yzOKKvDkMXZrymE2PBlbjKJRlrOxbvpfPSvJTGRIwGoahaOGvp+kbCoxifhzJ1Q=="], + "@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], "@volar/kit": ["@volar/kit@2.4.14", "", { "dependencies": { "@volar/language-service": "2.4.14", "@volar/typescript": "2.4.14", "typesafe-path": "^0.2.2", "vscode-languageserver-textdocument": "^1.0.11", "vscode-uri": "^3.0.8" }, "peerDependencies": { "typescript": "*" } }, "sha512-kBcmHjEodtmYGJELHePZd2JdeYm4ZGOd9F/pQ1YETYIzAwy4Z491EkJ1nRSo/GTxwKt0XYwYA/dHSEgXecVHRA=="], @@ -1492,13 +1682,13 @@ "@vue/babel-plugin-resolve-type": ["@vue/babel-plugin-resolve-type@1.4.0", "", { "dependencies": { "@babel/code-frame": "^7.26.2", "@babel/helper-module-imports": "^7.25.9", "@babel/helper-plugin-utils": "^7.26.5", "@babel/parser": "^7.26.9", "@vue/compiler-sfc": "^3.5.13" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "sha512-4xqDRRbQQEWHQyjlYSgZsWj44KfiF6D+ktCuXyZ8EnVDYV3pztmXJDf1HveAjUAXxAnR8daCQT51RneWWxtTyQ=="], - "@vue/compiler-core": ["@vue/compiler-core@3.5.16", "", { "dependencies": { "@babel/parser": "^7.27.2", "@vue/shared": "3.5.16", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-AOQS2eaQOaaZQoL1u+2rCJIKDruNXVBZSiUD3chnUrsoX5ZTQMaCvXlWNIfxBJuU15r1o7+mpo5223KVtIhAgQ=="], + "@vue/compiler-core": ["@vue/compiler-core@3.5.17", "", { "dependencies": { "@babel/parser": "^7.27.5", "@vue/shared": "3.5.17", "entities": "^4.5.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-Xe+AittLbAyV0pabcN7cP7/BenRBNcteM4aSDCtRvGw0d9OL+HG1u/XHLY/kt1q4fyMeZYXyIYrsHuPSiDPosA=="], - "@vue/compiler-dom": ["@vue/compiler-dom@3.5.16", "", { "dependencies": { "@vue/compiler-core": "3.5.16", "@vue/shared": "3.5.16" } }, "sha512-SSJIhBr/teipXiXjmWOVWLnxjNGo65Oj/8wTEQz0nqwQeP75jWZ0n4sF24Zxoht1cuJoWopwj0J0exYwCJ0dCQ=="], + "@vue/compiler-dom": ["@vue/compiler-dom@3.5.17", "", { "dependencies": { "@vue/compiler-core": "3.5.17", "@vue/shared": "3.5.17" } }, "sha512-+2UgfLKoaNLhgfhV5Ihnk6wB4ljyW1/7wUIog2puUqajiC29Lp5R/IKDdkebh9jTbTogTbsgB+OY9cEWzG95JQ=="], - "@vue/compiler-sfc": ["@vue/compiler-sfc@3.5.16", "", { "dependencies": { "@babel/parser": "^7.27.2", "@vue/compiler-core": "3.5.16", "@vue/compiler-dom": "3.5.16", "@vue/compiler-ssr": "3.5.16", "@vue/shared": "3.5.16", "estree-walker": "^2.0.2", "magic-string": "^0.30.17", "postcss": "^8.5.3", "source-map-js": "^1.2.1" } }, "sha512-rQR6VSFNpiinDy/DVUE0vHoIDUF++6p910cgcZoaAUm3POxgNOOdS/xgoll3rNdKYTYPnnbARDCZOyZ+QSe6Pw=="], + "@vue/compiler-sfc": ["@vue/compiler-sfc@3.5.17", "", { "dependencies": { "@babel/parser": "^7.27.5", "@vue/compiler-core": "3.5.17", "@vue/compiler-dom": "3.5.17", "@vue/compiler-ssr": "3.5.17", "@vue/shared": "3.5.17", "estree-walker": "^2.0.2", "magic-string": "^0.30.17", "postcss": "^8.5.6", "source-map-js": "^1.2.1" } }, "sha512-rQQxbRJMgTqwRugtjw0cnyQv9cP4/4BxWfTdRBkqsTfLOHWykLzbOc3C4GGzAmdMDxhzU/1Ija5bTjMVrddqww=="], - "@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.16", "", { "dependencies": { "@vue/compiler-dom": "3.5.16", "@vue/shared": "3.5.16" } }, "sha512-d2V7kfxbdsjrDSGlJE7my1ZzCXViEcqN6w14DOsDrUCHEA6vbnVCpRFfrc4ryCP/lCKzX2eS1YtnLE/BuC9f/A=="], + "@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.17", "", { "dependencies": { "@vue/compiler-dom": "3.5.17", "@vue/shared": "3.5.17" } }, "sha512-hkDbA0Q20ZzGgpj5uZjb9rBzQtIHLS78mMilwrlpWk2Ep37DYntUz0PonQ6kr113vfOEdM+zTBuJDaceNIW0tQ=="], "@vue/devtools-api": ["@vue/devtools-api@7.7.7", "", { "dependencies": { "@vue/devtools-kit": "^7.7.7" } }, "sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg=="], @@ -1508,15 +1698,15 @@ "@vue/devtools-shared": ["@vue/devtools-shared@7.7.7", "", { "dependencies": { "rfdc": "^1.4.1" } }, "sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw=="], - "@vue/reactivity": ["@vue/reactivity@3.5.16", "", { "dependencies": { "@vue/shared": "3.5.16" } }, "sha512-FG5Q5ee/kxhIm1p2bykPpPwqiUBV3kFySsHEQha5BJvjXdZTUfmya7wP7zC39dFuZAcf/PD5S4Lni55vGLMhvA=="], + "@vue/reactivity": ["@vue/reactivity@3.5.17", "", { "dependencies": { "@vue/shared": "3.5.17" } }, "sha512-l/rmw2STIscWi7SNJp708FK4Kofs97zc/5aEPQh4bOsReD/8ICuBcEmS7KGwDj5ODQLYWVN2lNibKJL1z5b+Lw=="], - "@vue/runtime-core": ["@vue/runtime-core@3.5.16", "", { "dependencies": { "@vue/reactivity": "3.5.16", "@vue/shared": "3.5.16" } }, "sha512-bw5Ykq6+JFHYxrQa7Tjr+VSzw7Dj4ldR/udyBZbq73fCdJmyy5MPIFR9IX/M5Qs+TtTjuyUTCnmK3lWWwpAcFQ=="], + "@vue/runtime-core": ["@vue/runtime-core@3.5.17", "", { "dependencies": { "@vue/reactivity": "3.5.17", "@vue/shared": "3.5.17" } }, "sha512-QQLXa20dHg1R0ri4bjKeGFKEkJA7MMBxrKo2G+gJikmumRS7PTD4BOU9FKrDQWMKowz7frJJGqBffYMgQYS96Q=="], - "@vue/runtime-dom": ["@vue/runtime-dom@3.5.16", "", { "dependencies": { "@vue/reactivity": "3.5.16", "@vue/runtime-core": "3.5.16", "@vue/shared": "3.5.16", "csstype": "^3.1.3" } }, "sha512-T1qqYJsG2xMGhImRUV9y/RseB9d0eCYZQ4CWca9ztCuiPj/XWNNN+lkNBuzVbia5z4/cgxdL28NoQCvC0Xcfww=="], + "@vue/runtime-dom": ["@vue/runtime-dom@3.5.17", "", { "dependencies": { "@vue/reactivity": "3.5.17", "@vue/runtime-core": "3.5.17", "@vue/shared": "3.5.17", "csstype": "^3.1.3" } }, "sha512-8El0M60TcwZ1QMz4/os2MdlQECgGoVHPuLnQBU3m9h3gdNRW9xRmI8iLS4t/22OQlOE6aJvNNlBiCzPHur4H9g=="], - "@vue/server-renderer": ["@vue/server-renderer@3.5.16", "", { "dependencies": { "@vue/compiler-ssr": "3.5.16", "@vue/shared": "3.5.16" }, "peerDependencies": { "vue": "3.5.16" } }, "sha512-BrX0qLiv/WugguGsnQUJiYOE0Fe5mZTwi6b7X/ybGB0vfrPH9z0gD/Y6WOR1sGCgX4gc25L1RYS5eYQKDMoNIg=="], + "@vue/server-renderer": ["@vue/server-renderer@3.5.17", "", { "dependencies": { "@vue/compiler-ssr": "3.5.17", "@vue/shared": "3.5.17" }, "peerDependencies": { "vue": "3.5.17" } }, "sha512-BOHhm8HalujY6lmC3DbqF6uXN/K00uWiEeF22LfEsm9Q93XeJ/plHTepGwf6tqFcF7GA5oGSSAAUock3VvzaCA=="], - "@vue/shared": ["@vue/shared@3.5.16", "", {}, "sha512-c/0fWy3Jw6Z8L9FmTyYfkpM5zklnqqa9+a6dz3DvONRKW2NEbh46BP0FHuLFSWi2TnQEtp91Z6zOWNrU6QiyPg=="], + "@vue/shared": ["@vue/shared@3.5.17", "", {}, "sha512-CabR+UN630VnsJO/jHWYBC1YVXyMq94KKp6iF5MQgZJs5I8cmjw6oVMO1oDbtBkENSHSSn/UadWlW/OAgdmKrg=="], "@vueuse/core": ["@vueuse/core@12.8.2", "", { "dependencies": { "@types/web-bluetooth": "^0.0.21", "@vueuse/metadata": "12.8.2", "@vueuse/shared": "12.8.2", "vue": "^3.5.13" } }, "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ=="], @@ -1604,11 +1794,11 @@ "alchemy-web": ["alchemy-web@workspace:alchemy-web"], - "algoliasearch": ["algoliasearch@5.27.0", "", { "dependencies": { "@algolia/client-abtesting": "5.27.0", "@algolia/client-analytics": "5.27.0", "@algolia/client-common": "5.27.0", "@algolia/client-insights": "5.27.0", "@algolia/client-personalization": "5.27.0", "@algolia/client-query-suggestions": "5.27.0", "@algolia/client-search": "5.27.0", "@algolia/ingestion": "1.27.0", "@algolia/monitoring": "1.27.0", "@algolia/recommend": "5.27.0", "@algolia/requester-browser-xhr": "5.27.0", "@algolia/requester-fetch": "5.27.0", "@algolia/requester-node-http": "5.27.0" } }, "sha512-2PvAgvxxJzA3+dB+ERfS2JPdvUsxNf89Cc2GF5iCcFupTULOwmbfinvqrC4Qj9nHJJDNf494NqEN/1f9177ZTQ=="], + "algoliasearch": ["algoliasearch@5.29.0", "", { "dependencies": { "@algolia/client-abtesting": "5.29.0", "@algolia/client-analytics": "5.29.0", "@algolia/client-common": "5.29.0", "@algolia/client-insights": "5.29.0", "@algolia/client-personalization": "5.29.0", "@algolia/client-query-suggestions": "5.29.0", "@algolia/client-search": "5.29.0", "@algolia/ingestion": "1.29.0", "@algolia/monitoring": "1.29.0", "@algolia/recommend": "5.29.0", "@algolia/requester-browser-xhr": "5.29.0", "@algolia/requester-fetch": "5.29.0", "@algolia/requester-node-http": "5.29.0" } }, "sha512-E2l6AlTWGznM2e7vEE6T6hzObvEyXukxMOlBmVlMyixZyK1umuO/CiVc6sDBbzVH0oEviCE5IfVY1oZBmccYPQ=="], "ansi-align": ["ansi-align@3.0.1", "", { "dependencies": { "string-width": "^4.1.0" } }, "sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w=="], - "ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="], + "ansi-escapes": ["ansi-escapes@7.0.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw=="], "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], @@ -1648,6 +1838,8 @@ "as-table": ["as-table@1.0.55", "", { "dependencies": { "printable-characters": "^1.0.42" } }, "sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ=="], + "asn1js": ["asn1js@3.0.6", "", { "dependencies": { "pvtsutils": "^1.3.6", "pvutils": "^1.1.3", "tslib": "^2.8.1" } }, "sha512-UOCGPYbl0tv8+006qks/dTgV9ajs97X2p0FAbyS2iyCRrmLSRolDaHdp+v/CLgnzHc3fVB+CwYiUmei7ndFcgA=="], + "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], "assign-symbols": ["assign-symbols@1.0.0", "", {}, "sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw=="], @@ -1660,7 +1852,7 @@ "ast-walker-scope": ["ast-walker-scope@0.6.2", "", { "dependencies": { "@babel/parser": "^7.25.3", "ast-kit": "^1.0.1" } }, "sha512-1UWOyC50xI3QZkRuDj6PqDtpm1oHWtYs+NQGwqL/2R11eN3Q81PHAHPM0SWW3BNQm53UDwS//Jv8L4CCVLM1bQ=="], - "astro": ["astro@5.9.3", "", { "dependencies": { "@astrojs/compiler": "^2.12.2", "@astrojs/internal-helpers": "0.6.1", "@astrojs/markdown-remark": "6.3.2", "@astrojs/telemetry": "3.3.0", "@capsizecss/unpack": "^2.4.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.1.4", "acorn": "^8.14.1", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "boxen": "8.0.1", "ci-info": "^4.2.0", "clsx": "^2.1.1", "common-ancestor-path": "^1.0.1", "cookie": "^1.0.2", "cssesc": "^3.0.0", "debug": "^4.4.0", "deterministic-object-hash": "^2.0.2", "devalue": "^5.1.1", "diff": "^5.2.0", "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^1.6.0", "esbuild": "^0.25.0", "estree-walker": "^3.0.3", "flattie": "^1.1.1", "fontace": "~0.3.0", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.1.1", "import-meta-resolve": "^4.1.0", "js-yaml": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.17", "magicast": "^0.3.5", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "p-limit": "^6.2.0", "p-queue": "^8.1.0", "package-manager-detector": "^1.1.0", "picomatch": "^4.0.2", "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.7.1", "shiki": "^3.2.1", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.12", "tsconfck": "^3.1.5", "ultrahtml": "^1.6.0", "unifont": "~0.5.0", "unist-util-visit": "^5.0.0", "unstorage": "^1.15.0", "vfile": "^6.0.3", "vite": "^6.3.4", "vitefu": "^1.0.6", "xxhash-wasm": "^1.1.0", "yargs-parser": "^21.1.1", "yocto-spinner": "^0.2.1", "zod": "^3.24.2", "zod-to-json-schema": "^3.24.5", "zod-to-ts": "^1.2.0" }, "optionalDependencies": { "sharp": "^0.33.3" }, "bin": { "astro": "astro.js" } }, "sha512-VReZrpUa/3rfeiVvsQ1A2M3ujDPI+pDGIYOMtXPEZwut8tZoEyealXXLjitgCsJ+3dunKGZbg4Eak6i+r0vniw=="], + "astro": ["astro@workspace:alchemy/templates/astro"], "astro-project": ["astro-project@workspace:examples/cloudflare-astro"], @@ -1748,7 +1940,7 @@ "builtin-modules": ["builtin-modules@3.3.0", "", {}, "sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw=="], - "bun-types": ["bun-types@1.2.16", "", { "dependencies": { "@types/node": "*" } }, "sha512-ciXLrHV4PXax9vHvUrkvun9VPVGOVwbbbBF/Ev1cXz12lyEZMoJpIJABOfPcN9gDJRaiKF9MVbSygLg4NXu3/A=="], + "bun-types": ["bun-types@1.2.17", "", { "dependencies": { "@types/node": "*" } }, "sha512-ElC7ItwT3SCQwYZDYoAH+q6KT4Fxjl8DtZ6qDulUFBmXA8YB4xo+l54J9ZJN+k2pphfn9vk7kfubeSd5QfTVJQ=="], "bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="], @@ -1774,7 +1966,7 @@ "caniuse-api": ["caniuse-api@3.0.0", "", { "dependencies": { "browserslist": "^4.0.0", "caniuse-lite": "^1.0.0", "lodash.memoize": "^4.1.2", "lodash.uniq": "^4.5.0" } }, "sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw=="], - "caniuse-lite": ["caniuse-lite@1.0.30001723", "", {}, "sha512-1R/elMjtehrFejxwmexeXAtae5UO9iSyFn6G/I806CYC/BLyyBk1EPhrKBkWhy6wM6Xnm47dSJQec+tLJ39WHw=="], + "caniuse-lite": ["caniuse-lite@1.0.30001724", "", {}, "sha512-WqJo7p0TbHDOythNTqYujmaJTvtYRZrjpP8TCvH6Vb9CYJerJNKamKzIWOM4BkQatWj9H2lYulpdAQNBe7QhNA=="], "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], @@ -1786,7 +1978,7 @@ "changelogen": ["changelogen@0.5.7", "", { "dependencies": { "c12": "^1.11.2", "colorette": "^2.0.20", "consola": "^3.2.3", "convert-gitmoji": "^0.1.5", "mri": "^1.2.0", "node-fetch-native": "^1.6.4", "ofetch": "^1.3.4", "open": "^10.1.0", "pathe": "^1.1.2", "pkg-types": "^1.2.0", "scule": "^1.3.0", "semver": "^7.6.3", "std-env": "^3.7.0", "yaml": "^2.5.1" }, "bin": { "changelogen": "dist/cli.mjs" } }, "sha512-cTZXBcJMl3pudE40WENOakXkcVtrbBpbkmSkM20NdRiUqa4+VYRdXdEsgQ0BNQ6JBE2YymTNWtPKVF7UCTN5+g=="], - "changelogithub": ["changelogithub@13.15.0", "", { "dependencies": { "ansis": "^4.1.0", "c12": "^3.0.4", "cac": "^6.7.14", "changelogen": "0.5.7", "convert-gitmoji": "^0.1.5", "execa": "^9.6.0", "ofetch": "^1.4.1", "semver": "^7.7.2" }, "bin": { "changelogithub": "cli.mjs" } }, "sha512-qPQ896hrTKXYHzFTH1c1O4KRfk4IwxOp22fKBUBGYqcB13z8B6bNoViSX50Z6SbmcQjHLHwyOMfACJVn74x5LA=="], + "changelogithub": ["changelogithub@13.16.0", "", { "dependencies": { "ansis": "^4.1.0", "c12": "^3.0.4", "cac": "^6.7.14", "changelogen": "0.5.7", "convert-gitmoji": "^0.1.5", "execa": "^9.6.0", "ofetch": "^1.4.1", "semver": "^7.7.2", "tinyglobby": "^0.2.14" }, "bin": { "changelogithub": "cli.mjs" } }, "sha512-O+OiuYG/JgUGENXt6M+eyjLBtGYwUPL5SqKU/9sngZS14fJ3LCIU7xPdWJoyzjAixGYwjuVLC1tZFbboxZ7zbQ=="], "character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="], @@ -1794,8 +1986,6 @@ "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="], - "chardet": ["chardet@0.7.0", "", {}, "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA=="], - "check-error": ["check-error@2.1.1", "", {}, "sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw=="], "cheerio": ["cheerio@1.1.0", "", { "dependencies": { "cheerio-select": "^2.1.0", "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", "encoding-sniffer": "^0.2.0", "htmlparser2": "^10.0.0", "parse5": "^7.3.0", "parse5-htmlparser2-tree-adapter": "^7.1.0", "parse5-parser-stream": "^7.1.2", "undici": "^7.10.0", "whatwg-mimetype": "^4.0.0" } }, "sha512-+0hMx9eYhJvWbgpKV9hN7jg0JcwydpopZE4hgi+KvQtByZXPp04NiCWU0LzcAbP63abZckIHkTQaXVF52mX3xQ=="], @@ -1824,15 +2014,15 @@ "cli-truncate": ["cli-truncate@4.0.0", "", { "dependencies": { "slice-ansi": "^5.0.0", "string-width": "^7.0.0" } }, "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA=="], - "cli-width": ["cli-width@4.1.0", "", {}, "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ=="], - "clipboardy": ["clipboardy@4.0.0", "", { "dependencies": { "execa": "^8.0.1", "is-wsl": "^3.1.0", "is64bit": "^2.0.0" } }, "sha512-5mOlNS0mhX0707P2I0aZ2V/cmHUEO/fL7VFLqszkhUsxt7RwnmrInf/eEQKlf5GzvYeHIjT+Ov1HRfNmymlG0w=="], "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], "clone": ["clone@2.1.2", "", {}, "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w=="], - "cloudflare": ["cloudflare@4.4.0", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }, "sha512-LOuA4ogy0VZqcO341rFHoDRKzy9EDcWUVaYsp6QjuZnshfsAHRkXanHQBMLH4SrGw/AoPulnFUQ3BcdAO5xd5Q=="], + "cloudflare": ["cloudflare@4.4.1", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }, "sha512-wrtQ9WMflnfRcmdQZf/XfVVkeucgwzzYeqFDfgbNdADTaexsPwrtt3etzUvPGvVUeEk9kOPfNkl8MSzObxrIsg=="], + + "cloudflare-container": ["cloudflare-container@workspace:examples/cloudflare-container"], "cloudflare-react-router": ["cloudflare-react-router@workspace:examples/cloudflare-react-router"], @@ -1842,6 +2032,8 @@ "cloudflare-worker-bootstrap": ["cloudflare-worker-bootstrap@workspace:examples/cloudflare-worker-bootstrap"], + "cloudflare-worker-simple": ["cloudflare-worker-simple@workspace:examples/cloudflare-worker-simple"], + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], "cluster-key-slot": ["cluster-key-slot@1.1.2", "", {}, "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA=="], @@ -2034,7 +2226,9 @@ "dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="], - "dofs": ["dofs@0.0.1", "", { "peerDependencies": { "hono": "^4.7.10" } }, "sha512-vNfYmLREQNQRE0R+ZiUuoW4VkxLZm/M6PHxAlTE5YDwrDIYdSTA0rfZ3Rgp7g2Tkvv5VI9xTt7pUtdMD46eM1Q=="], + "docker": ["docker@workspace:examples/docker"], + + "dofs": ["dofs@0.1.0", "", { "dependencies": { "commander": "^14.0.0", "neofuse": "^0.0.1-rc.3" }, "peerDependencies": { "hono": "^4.7.11" }, "bin": { "dofs": "dist/cli/index.js" } }, "sha512-aF4SM2w1/DCjbZy3oTlRVbksp5TCPkSEETwslOQpdbnkMMyrOQ1Gk4C+LOgKAZRUdovS3qyLUVoKUtdrp+GldQ=="], "dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="], @@ -2062,7 +2256,7 @@ "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], - "electron-to-chromium": ["electron-to-chromium@1.5.168", "", {}, "sha512-RUNQmFLNIWVW6+z32EJQ5+qx8ci6RGvdtDC0Ls+F89wz6I2AthpXF0w0DIrn2jpLX0/PU9ZCo+Qp7bg/EckJmA=="], + "electron-to-chromium": ["electron-to-chromium@1.5.171", "", {}, "sha512-scWpzXEJEMrGJa4Y6m/tVotb0WuvNmasv3wWVzUAeCgKU0ToFOhUW6Z+xWnRQANMYGxN4ngJXIThgBJOqzVPCQ=="], "emmet": ["emmet@2.4.11", "", { "dependencies": { "@emmetio/abbreviation": "^2.3.3", "@emmetio/css-abbreviation": "^2.1.8" } }, "sha512-23QPJB3moh/U9sT4rQzGgeyyGIrcM+GH5uVYg2C6wZIxAIJq7Ng3QLT79tl8FUwDXhyq9SusfknOrofAKqvgyQ=="], @@ -2164,14 +2358,12 @@ "express": ["express@4.21.2", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "1.20.3", "content-disposition": "0.5.4", "content-type": "~1.0.4", "cookie": "0.7.1", "cookie-signature": "1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "1.3.1", "fresh": "0.5.2", "http-errors": "2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "0.1.12", "proxy-addr": "~2.0.7", "qs": "6.13.0", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "0.19.0", "serve-static": "1.16.2", "setprototypeof": "1.2.0", "statuses": "2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA=="], - "exsolve": ["exsolve@1.0.5", "", {}, "sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg=="], + "exsolve": ["exsolve@1.0.7", "", {}, "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw=="], "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], "extend-shallow": ["extend-shallow@3.0.2", "", { "dependencies": { "assign-symbols": "^1.0.0", "is-extendable": "^1.0.1" } }, "sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q=="], - "external-editor": ["external-editor@3.1.0", "", { "dependencies": { "chardet": "^0.7.0", "iconv-lite": "^0.4.24", "tmp": "^0.0.33" } }, "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew=="], - "externality": ["externality@1.0.2", "", { "dependencies": { "enhanced-resolve": "^5.14.1", "mlly": "^1.3.0", "pathe": "^1.1.1", "ufo": "^1.1.2" } }, "sha512-LyExtJWKxtgVzmgtEHyQtLFpw1KFhQphF9nTG8TpAIVkiI/xQ3FJh75tRFLYl4hkn7BNIIdLJInuDAavX35pMw=="], "extglob": ["extglob@0.3.2", "", { "dependencies": { "is-extglob": "^1.0.0" } }, "sha512-1FOj1LOwn42TMrruOHGt18HemVnbwAmAak7krWk+wa93KXxGbK+2jpezm+ytJYDaBX0/SPLZFHKM7m+tKobWGg=="], @@ -2192,7 +2384,7 @@ "fast-levenshtein": ["fast-levenshtein@2.0.6", "", {}, "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw=="], - "fast-npm-meta": ["fast-npm-meta@0.4.3", "", {}, "sha512-eUzR/uVx61fqlHBjG/eQx5mQs7SQObehMTTdq8FAkdCB4KuZSQ6DiZMIrAq4kcibB3WFLQ9c4dT26Vwkix1RKg=="], + "fast-npm-meta": ["fast-npm-meta@0.4.4", "", {}, "sha512-cq8EVW3jpX1U3dO1AYanz2BJ6n9ITQgCwE1xjNwI5jO2a9erE369OZNO8Wt/Wbw8YHhCD/dimH9BxRsY+6DinA=="], "fast-uri": ["fast-uri@3.0.6", "", {}, "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw=="], @@ -2378,7 +2570,7 @@ "hastscript": ["hastscript@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w=="], - "hono": ["hono@4.7.11", "", {}, "sha512-rv0JMwC0KALbbmwJDEnxvQCeJh+xbS3KEWW5PC9cMJ08Ur9xgatI0HmtgYZfOdOSOeYsp5LO2cOhdI8cLEbDEQ=="], + "hono": ["hono@4.8.3", "", {}, "sha512-jYZ6ZtfWjzBdh8H/0CIFfCBHaFL75k+KMzaM177hrWWm2TWL39YMYaJgB74uK/niRc866NMlH9B8uCvIo284WQ=="], "hookable": ["hookable@5.5.3", "", {}, "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="], @@ -2516,7 +2708,7 @@ "isbot": ["isbot@5.1.28", "", {}, "sha512-qrOp4g3xj8YNse4biorv6O5ZShwsJM0trsoda4y7j/Su7ZtTTfVXFzbKkpgcSoDrHS8FcTuUwcU04YimZlZOxw=="], - "isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="], + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], "isobject": ["isobject@3.0.1", "", {}, "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg=="], @@ -2574,6 +2766,8 @@ "kuler": ["kuler@2.0.0", "", {}, "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A=="], + "ky": ["ky@1.7.5", "", {}, "sha512-HzhziW6sc5m0pwi5M196+7cEBtbt0lCYi67wNsiwMUmz833wloE0gbzJPWKs1gliFKQb34huItDQX97LyOdPdA=="], + "lambda-local": ["lambda-local@2.2.0", "", { "dependencies": { "commander": "^10.0.1", "dotenv": "^16.3.1", "winston": "^3.10.0" }, "bin": { "lambda-local": "build/cli.js" } }, "sha512-bPcgpIXbHnVGfI/omZIlgucDqlf4LrsunwoKue5JdZeGybt8L6KyJz2Zu19ffuZwIwLj2NAI2ZyaqNT6/cetcg=="], "launch-editor": ["launch-editor@2.10.0", "", { "dependencies": { "picocolors": "^1.0.0", "shell-quote": "^1.8.1" } }, "sha512-D7dBRJo/qcGX9xlvt/6wUYzQxjh5G1RvZPgPv8vi4KRU99DVQL/oW7tnVOCCTm2HGeo3C5HvGE5Yrh6UBoZ0vA=="], @@ -2794,7 +2988,7 @@ "mimic-function": ["mimic-function@5.0.1", "", {}, "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA=="], - "miniflare": ["miniflare@4.20250604.1", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "^5.28.5", "workerd": "1.20250604.0", "ws": "8.18.0", "youch": "3.3.4", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-HJQ9YhH0F0fI73Vsdy3PNVau+PfHZYK7trI5WJEcbfl5HzqhMU0DRNtA/G5EXQgiumkjrmbW4Zh1DVTtsqICPg=="], + "miniflare": ["miniflare@4.20250617.4", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "^5.28.5", "workerd": "1.20250617.0", "ws": "8.18.0", "youch": "3.3.4", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-IAoApFKxOJlaaFkym5ETstVX3qWzVt3xyqCDj6vSSTgEH3zxZJ5417jZGg8iQfMHosKCcQH1doPPqqnOZm/yrw=="], "minimatch": ["minimatch@9.0.5", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow=="], @@ -2830,8 +3024,6 @@ "mustache": ["mustache@4.2.0", "", { "bin": { "mustache": "bin/mustache" } }, "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ=="], - "mute-stream": ["mute-stream@2.0.0", "", {}, "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA=="], - "mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="], "nan": ["nan@2.22.2", "", {}, "sha512-DANghxFkS1plDdRsX0X9pm0Z6SJNN6gBdtXfanwoZ8hooC5gosGFSBGRYHUVPz1asKA/kMRqDRdHrluZ61SpBQ=="], @@ -2840,6 +3032,8 @@ "nanomatch": ["nanomatch@1.2.13", "", { "dependencies": { "arr-diff": "^4.0.0", "array-unique": "^0.3.2", "define-property": "^2.0.2", "extend-shallow": "^3.0.2", "fragment-cache": "^0.2.1", "is-windows": "^1.0.2", "kind-of": "^6.0.2", "object.pick": "^1.3.0", "regex-not": "^1.0.0", "snapdragon": "^0.8.1", "to-regex": "^3.0.1" } }, "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA=="], + "nanoresource": ["nanoresource@1.3.0", "", { "dependencies": { "inherits": "^2.0.4" } }, "sha512-OI5dswqipmlYfyL3k/YMm7mbERlh4Bd1KuKdMHpeoVD1iVxqxaTMKleB4qaA2mbQZ6/zMNSxCXv9M9P/YbqTuQ=="], + "nanotar": ["nanotar@0.2.0", "", {}, "sha512-9ca1h0Xjvo9bEkE4UOxgAzLV0jHKe6LMaxo37ND2DAhhAtd0j8pR1Wxz+/goMrZO8AEZTWCmyaOsFI/W5AdpCQ=="], "natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="], @@ -2848,12 +3042,16 @@ "neo-async": ["neo-async@2.6.2", "", {}, "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="], + "neofuse": ["neofuse@0.0.1-rc.3", "", { "dependencies": { "nanoresource": "^1.3.0", "node-gyp-build": "^4.8.4" } }, "sha512-X5N4y+rk0z1XhCig16OpbgSfeYOqHtGhkjNvvAYms2jtjunVpzTNhM8iv2OamQlBtQQgrTYaQMbVHrBb5+6bfw=="], + "neotraverse": ["neotraverse@0.6.18", "", {}, "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA=="], "netlify": ["netlify@13.3.5", "", { "dependencies": { "@netlify/open-api": "^2.37.0", "lodash-es": "^4.17.21", "micro-api-client": "^3.3.0", "node-fetch": "^3.0.0", "p-wait-for": "^5.0.0", "qs": "^6.9.6" } }, "sha512-Nc3loyVASW59W+8fLDZT1lncpG7llffyZ2o0UQLx/Fr20i7P8oP+lE7+TEcFvXj9IUWU6LjB9P3BH+iFGyp+mg=="], "netmask": ["netmask@2.0.2", "", {}, "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg=="], + "nitro-cloudflare-dev": ["nitro-cloudflare-dev@0.2.2", "", { "dependencies": { "consola": "^3.4.0", "mlly": "^1.7.4", "pkg-types": "^2.1.0" } }, "sha512-aZfNTVdgXPQeAmXW0Tw8hm3usAHr4qVG4Bg3WhHBGeZYuXr9OyT04Ztb+STkMzhyaXvfMHViAaPUPg06iAYqag=="], + "nitropack": ["nitropack@2.11.12", "", { "dependencies": { "@cloudflare/kv-asset-handler": "^0.4.0", "@netlify/functions": "^3.1.8", "@rollup/plugin-alias": "^5.1.1", "@rollup/plugin-commonjs": "^28.0.3", "@rollup/plugin-inject": "^5.0.5", "@rollup/plugin-json": "^6.1.0", "@rollup/plugin-node-resolve": "^16.0.1", "@rollup/plugin-replace": "^6.0.2", "@rollup/plugin-terser": "^0.4.4", "@vercel/nft": "^0.29.2", "archiver": "^7.0.1", "c12": "^3.0.3", "chokidar": "^4.0.3", "citty": "^0.1.6", "compatx": "^0.2.0", "confbox": "^0.2.2", "consola": "^3.4.2", "cookie-es": "^2.0.0", "croner": "^9.0.0", "crossws": "^0.3.5", "db0": "^0.3.2", "defu": "^6.1.4", "destr": "^2.0.5", "dot-prop": "^9.0.0", "esbuild": "^0.25.4", "escape-string-regexp": "^5.0.0", "etag": "^1.8.1", "exsolve": "^1.0.5", "globby": "^14.1.0", "gzip-size": "^7.0.0", "h3": "^1.15.3", "hookable": "^5.5.3", "httpxy": "^0.1.7", "ioredis": "^5.6.1", "jiti": "^2.4.2", "klona": "^2.0.6", "knitwork": "^1.2.0", "listhen": "^1.9.0", "magic-string": "^0.30.17", "magicast": "^0.3.5", "mime": "^4.0.7", "mlly": "^1.7.4", "node-fetch-native": "^1.6.6", "node-mock-http": "^1.0.0", "ofetch": "^1.4.1", "ohash": "^2.0.11", "pathe": "^2.0.3", "perfect-debounce": "^1.0.0", "pkg-types": "^2.1.0", "pretty-bytes": "^6.1.1", "radix3": "^1.1.2", "rollup": "^4.40.2", "rollup-plugin-visualizer": "^5.14.0", "scule": "^1.3.0", "semver": "^7.7.2", "serve-placeholder": "^2.0.2", "serve-static": "^2.2.0", "source-map": "^0.7.4", "std-env": "^3.9.0", "ufo": "^1.6.1", "ultrahtml": "^1.6.0", "uncrypto": "^0.1.3", "unctx": "^2.4.1", "unenv": "^2.0.0-rc.17", "unimport": "^5.0.1", "unplugin-utils": "^0.2.4", "unstorage": "^1.16.0", "untyped": "^2.0.0", "unwasm": "^0.3.9", "youch": "^4.1.0-beta.7", "youch-core": "^0.3.2" }, "peerDependencies": { "xml2js": "^0.6.2" }, "optionalPeers": ["xml2js"], "bin": { "nitro": "dist/cli/index.mjs", "nitropack": "dist/cli/index.mjs" } }, "sha512-e2AdQrEY1IVoNTdyjfEQV93xkqz4SQxAMR0xWF8mZUUHxMLm6S4nPzpscjksmT4OdUxl0N8/DCaGjKQ9ghdodA=="], "nlcst-to-string": ["nlcst-to-string@4.0.0", "", { "dependencies": { "@types/nlcst": "^2.0.0" } }, "sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA=="], @@ -2896,7 +3094,7 @@ "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], - "nuxt": ["nuxt@3.17.5", "", { "dependencies": { "@nuxt/cli": "^3.25.1", "@nuxt/devalue": "^2.0.2", "@nuxt/devtools": "^2.4.1", "@nuxt/kit": "3.17.5", "@nuxt/schema": "3.17.5", "@nuxt/telemetry": "^2.6.6", "@nuxt/vite-builder": "3.17.5", "@unhead/vue": "^2.0.10", "@vue/shared": "^3.5.16", "c12": "^3.0.4", "chokidar": "^4.0.3", "compatx": "^0.2.0", "consola": "^3.4.2", "cookie-es": "^2.0.0", "defu": "^6.1.4", "destr": "^2.0.5", "devalue": "^5.1.1", "errx": "^0.1.0", "esbuild": "^0.25.5", "escape-string-regexp": "^5.0.0", "estree-walker": "^3.0.3", "exsolve": "^1.0.5", "h3": "^1.15.3", "hookable": "^5.5.3", "ignore": "^7.0.5", "impound": "^1.0.0", "jiti": "^2.4.2", "klona": "^2.0.6", "knitwork": "^1.2.0", "magic-string": "^0.30.17", "mlly": "^1.7.4", "mocked-exports": "^0.1.1", "nanotar": "^0.2.0", "nitropack": "^2.11.12", "nypm": "^0.6.0", "ofetch": "^1.4.1", "ohash": "^2.0.11", "on-change": "^5.0.1", "oxc-parser": "^0.72.2", "pathe": "^2.0.3", "perfect-debounce": "^1.0.0", "pkg-types": "^2.1.0", "radix3": "^1.1.2", "scule": "^1.3.0", "semver": "^7.7.2", "std-env": "^3.9.0", "strip-literal": "^3.0.0", "tinyglobby": "0.2.14", "ufo": "^1.6.1", "ultrahtml": "^1.6.0", "uncrypto": "^0.1.3", "unctx": "^2.4.1", "unimport": "^5.0.1", "unplugin": "^2.3.5", "unplugin-vue-router": "^0.12.0", "unstorage": "^1.16.0", "untyped": "^2.0.0", "vue": "^3.5.16", "vue-bundle-renderer": "^2.1.1", "vue-devtools-stub": "^0.1.0", "vue-router": "^4.5.1" }, "peerDependencies": { "@parcel/watcher": "^2.1.0", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "optionalPeers": ["@parcel/watcher", "@types/node"], "bin": { "nuxi": "bin/nuxt.mjs", "nuxt": "bin/nuxt.mjs" } }, "sha512-HWTWpM1/RDcCt9DlnzrPcNvUmGqc62IhlZJvr7COSfnJq2lKYiBKIIesEaOF+57Qjw7TfLPc1DQVBNtxfKBxEw=="], + "nuxt": ["nuxt@workspace:alchemy/templates/nuxt"], "nuxt-pipeline": ["nuxt-pipeline@workspace:examples/cloudflare-nuxt-pipeline"], @@ -2946,8 +3144,6 @@ "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], - "os-tmpdir": ["os-tmpdir@1.0.2", "", {}, "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g=="], - "oxc-parser": ["oxc-parser@0.72.3", "", { "dependencies": { "@oxc-project/types": "^0.72.3" }, "optionalDependencies": { "@oxc-parser/binding-darwin-arm64": "0.72.3", "@oxc-parser/binding-darwin-x64": "0.72.3", "@oxc-parser/binding-freebsd-x64": "0.72.3", "@oxc-parser/binding-linux-arm-gnueabihf": "0.72.3", "@oxc-parser/binding-linux-arm-musleabihf": "0.72.3", "@oxc-parser/binding-linux-arm64-gnu": "0.72.3", "@oxc-parser/binding-linux-arm64-musl": "0.72.3", "@oxc-parser/binding-linux-riscv64-gnu": "0.72.3", "@oxc-parser/binding-linux-s390x-gnu": "0.72.3", "@oxc-parser/binding-linux-x64-gnu": "0.72.3", "@oxc-parser/binding-linux-x64-musl": "0.72.3", "@oxc-parser/binding-wasm32-wasi": "0.72.3", "@oxc-parser/binding-win32-arm64-msvc": "0.72.3", "@oxc-parser/binding-win32-x64-msvc": "0.72.3" } }, "sha512-JYQeJKDcUTTZ/uTdJ+fZBGFjAjkLD1h0p3Tf44ZYXRcoMk+57d81paNPFAAwzrzzqhZmkGvKKXDxwyhJXYZlpg=="], "p-event": ["p-event@6.0.1", "", { "dependencies": { "p-timeout": "^6.1.2" } }, "sha512-Q6Bekk5wpzW5qIyUP4gdMEujObYstZl6DMMOSenwBvV0BlE5LkDwkjs5yHbZmdCEq2o4RJx4tE1vwxFVf2FG1w=="], @@ -3118,7 +3314,9 @@ "preserve": ["preserve@0.2.0", "", {}, "sha512-s/46sYeylUfHNjI+sA/78FAHlmIuKqI9wNnzEOGehAlUUYeObv5C2mOinXBjyUyWmJ2SfcS2/ydApH4hTF4WXQ=="], - "prettier": ["prettier@3.5.3", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw=="], + "prettier": ["prettier@3.6.0", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-ujSB9uXHJKzM/2GBuE0hBOUgC77CN3Bnpqa+g80bkv3T3A93wL/xlzDATHhnhkzifz/UE2SNOvmbTz5hSkDlHw=="], + + "prettier-plugin-tailwindcss": ["prettier-plugin-tailwindcss@0.6.13", "", { "peerDependencies": { "@ianvs/prettier-plugin-sort-imports": "*", "@prettier/plugin-pug": "*", "@shopify/prettier-plugin-liquid": "*", "@trivago/prettier-plugin-sort-imports": "*", "@zackad/prettier-plugin-twig": "*", "prettier": "^3.0", "prettier-plugin-astro": "*", "prettier-plugin-css-order": "*", "prettier-plugin-import-sort": "*", "prettier-plugin-jsdoc": "*", "prettier-plugin-marko": "*", "prettier-plugin-multiline-arrays": "*", "prettier-plugin-organize-attributes": "*", "prettier-plugin-organize-imports": "*", "prettier-plugin-sort-imports": "*", "prettier-plugin-style-order": "*", "prettier-plugin-svelte": "*" }, "optionalPeers": ["@ianvs/prettier-plugin-sort-imports", "@prettier/plugin-pug", "@shopify/prettier-plugin-liquid", "@trivago/prettier-plugin-sort-imports", "@zackad/prettier-plugin-twig", "prettier-plugin-astro", "prettier-plugin-css-order", "prettier-plugin-import-sort", "prettier-plugin-jsdoc", "prettier-plugin-marko", "prettier-plugin-multiline-arrays", "prettier-plugin-organize-attributes", "prettier-plugin-organize-imports", "prettier-plugin-sort-imports", "prettier-plugin-style-order", "prettier-plugin-svelte"] }, "sha512-uQ0asli1+ic8xrrSmIOaElDu0FacR4x69GynTh2oZjFY10JUt6EEumTQl5tB4fMeD6I1naKd+4rXQQ7esT2i1g=="], "pretty-bytes": ["pretty-bytes@6.1.1", "", {}, "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ=="], @@ -3126,6 +3324,8 @@ "printable-characters": ["printable-characters@1.0.42", "", {}, "sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ=="], + "prisma": ["prisma@6.8.2", "", { "dependencies": { "@prisma/config": "6.8.2", "@prisma/engines": "6.8.2" }, "peerDependencies": { "typescript": ">=5.1.0" }, "optionalPeers": ["typescript"], "bin": { "prisma": "build/index.js" } }, "sha512-JNricTXQxzDtRS7lCGGOB4g5DJ91eg3nozdubXze3LpcMl1oWwcFddrj++Up3jnRE6X/3gB/xz3V+ecBk/eEGA=="], + "prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="], "proc-log": ["proc-log@3.0.0", "", {}, "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A=="], @@ -3160,6 +3360,10 @@ "puppeteer-core": ["puppeteer-core@22.15.0", "", { "dependencies": { "@puppeteer/browsers": "2.3.0", "chromium-bidi": "0.6.3", "debug": "^4.3.6", "devtools-protocol": "0.0.1312386", "ws": "^8.18.0" } }, "sha512-cHArnywCiAAVXa3t4GGL2vttNxh7GqXtIYGym99egkNJ3oG//wL9LkvO4WE8W1TJe95t1F1ocu9X4xWaGsOKOA=="], + "pvtsutils": ["pvtsutils@1.3.6", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg=="], + + "pvutils": ["pvutils@1.1.3", "", {}, "sha512-pMpnA0qRdFp32b1sJl1wOJNxZLQ2cbQx+k6tjNtZ8CpvVhNqEPRgivZ2WOUev2YMajecdH7ctUPDvEe87nariQ=="], + "qs": ["qs@6.13.0", "", { "dependencies": { "side-channel": "^1.0.6" } }, "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg=="], "quansync": ["quansync@0.2.10", "", {}, "sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A=="], @@ -3188,7 +3392,7 @@ "react-refresh": ["react-refresh@0.14.2", "", {}, "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA=="], - "react-router": ["react-router@7.6.2", "", { "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" }, "optionalPeers": ["react-dom"] }, "sha512-U7Nv3y+bMimgWjhlT5CRdzHPu2/KVmqPwKUCChW8en5P3znxUqwlYFlbmyj8Rgp1SF6zs5X4+77kBVknkg6a0w=="], + "react-router": ["react-router@workspace:alchemy/templates/react-router"], "react-server-dom-webpack": ["react-server-dom-webpack@19.2.0-canary-39cad7af-20250411", "", { "dependencies": { "acorn-loose": "^8.3.0", "neo-async": "^2.6.1", "webpack-sources": "^3.2.0" }, "peerDependencies": { "react": "19.2.0-canary-39cad7af-20250411", "react-dom": "19.2.0-canary-39cad7af-20250411", "webpack": "^5.59.0" } }, "sha512-B6TOkq4VA+p1rsPsElTCwLEwWe5T+eseoXVzYKR1COR2R2TNjO2bQb+52tr5ZJ76mXnZc18wpUdafp1+Zb6piw=="], @@ -3286,7 +3490,7 @@ "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], - "rollup": ["rollup@4.43.0", "", { "dependencies": { "@types/estree": "1.0.7" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.43.0", "@rollup/rollup-android-arm64": "4.43.0", "@rollup/rollup-darwin-arm64": "4.43.0", "@rollup/rollup-darwin-x64": "4.43.0", "@rollup/rollup-freebsd-arm64": "4.43.0", "@rollup/rollup-freebsd-x64": "4.43.0", "@rollup/rollup-linux-arm-gnueabihf": "4.43.0", "@rollup/rollup-linux-arm-musleabihf": "4.43.0", "@rollup/rollup-linux-arm64-gnu": "4.43.0", "@rollup/rollup-linux-arm64-musl": "4.43.0", "@rollup/rollup-linux-loongarch64-gnu": "4.43.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.43.0", "@rollup/rollup-linux-riscv64-gnu": "4.43.0", "@rollup/rollup-linux-riscv64-musl": "4.43.0", "@rollup/rollup-linux-s390x-gnu": "4.43.0", "@rollup/rollup-linux-x64-gnu": "4.43.0", "@rollup/rollup-linux-x64-musl": "4.43.0", "@rollup/rollup-win32-arm64-msvc": "4.43.0", "@rollup/rollup-win32-ia32-msvc": "4.43.0", "@rollup/rollup-win32-x64-msvc": "4.43.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-wdN2Kd3Twh8MAEOEJZsuxuLKCsBEo4PVNLK6tQWAn10VhsVewQLzcucMgLolRlhFybGxfclbPeEYBaP6RvUFGg=="], + "rollup": ["rollup@4.44.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.44.0", "@rollup/rollup-android-arm64": "4.44.0", "@rollup/rollup-darwin-arm64": "4.44.0", "@rollup/rollup-darwin-x64": "4.44.0", "@rollup/rollup-freebsd-arm64": "4.44.0", "@rollup/rollup-freebsd-x64": "4.44.0", "@rollup/rollup-linux-arm-gnueabihf": "4.44.0", "@rollup/rollup-linux-arm-musleabihf": "4.44.0", "@rollup/rollup-linux-arm64-gnu": "4.44.0", "@rollup/rollup-linux-arm64-musl": "4.44.0", "@rollup/rollup-linux-loongarch64-gnu": "4.44.0", "@rollup/rollup-linux-powerpc64le-gnu": "4.44.0", "@rollup/rollup-linux-riscv64-gnu": "4.44.0", "@rollup/rollup-linux-riscv64-musl": "4.44.0", "@rollup/rollup-linux-s390x-gnu": "4.44.0", "@rollup/rollup-linux-x64-gnu": "4.44.0", "@rollup/rollup-linux-x64-musl": "4.44.0", "@rollup/rollup-win32-arm64-msvc": "4.44.0", "@rollup/rollup-win32-ia32-msvc": "4.44.0", "@rollup/rollup-win32-x64-msvc": "4.44.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-qHcdEzLCiktQIfwBq420pn2dP+30uzqYxv9ETm91wdt2R9AFcWfjNAmje4NWlnCIQ5RMTzVf0ZyisOKqHR6RwA=="], "rollup-plugin-inject": ["rollup-plugin-inject@3.0.2", "", { "dependencies": { "estree-walker": "^0.6.1", "magic-string": "^0.25.3", "rollup-pluginutils": "^2.8.1" } }, "sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w=="], @@ -3302,7 +3506,7 @@ "run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="], - "rwsdk": ["rwsdk@0.0.83", "", { "dependencies": { "@cloudflare/vite-plugin": "^1.1.0", "@cloudflare/workers-types": "^4.20250407.0", "@puppeteer/browsers": "^2.8.0", "@types/fs-extra": "^11.0.4", "@types/react": "^19.1.2", "@types/react-dom": "^19.1.2", "@types/react-is": "^19.0.0", "@vitejs/plugin-react": "^4.3.4", "debug": "^4.4.0", "enhanced-resolve": "^5.18.1", "es-module-lexer": "^1.5.4", "eventsource-parser": "^3.0.0", "execa": "^9.5.2", "fs-extra": "^11.3.0", "glob": "^11.0.1", "ignore": "^7.0.4", "jsonc-parser": "^3.3.1", "lodash": "^4.17.21", "magic-string": "^0.30.17", "miniflare": "^4.20250405.0", "picocolors": "^1.1.1", "puppeteer-core": "^22.8.1", "react": "19.2.0-canary-39cad7af-20250411", "react-dom": "19.2.0-canary-39cad7af-20250411", "react-is": "^19.0.0", "react-server-dom-webpack": "19.2.0-canary-39cad7af-20250411", "rsc-html-stream": "^0.0.6", "tmp-promise": "^3.0.3", "ts-morph": "^25.0.1", "unique-names-generator": "^4.7.1", "vibe-rules": "^0.2.31", "vite-tsconfig-paths": "^5.1.4", "wrangler": "^4.14.1" }, "peerDependencies": { "vite": "^6.2.6" }, "bin": { "rw-scripts": "bin/rw-scripts.mjs" } }, "sha512-Uwe8zou31gRKMvRnQ/Qt2VD+RhG4WS6eP8dBzFCw+d/6c87JhW7tx0jsk6WTN5hII7qytvKRR4cgAvHVHlwmKg=="], + "rwsdk": ["rwsdk@workspace:alchemy/templates/rwsdk"], "sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="], @@ -3488,9 +3692,11 @@ "supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="], - "svelte": ["svelte@5.34.3", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", "esrap": "^1.4.8", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-Y0QKP2rfWD+ARKe91c4JgZgc/nXa2BfOnVBUjYUMB819m7VyPszihkjdzXPIV0qlGRZYEukpgNq7hgbzTbopJw=="], + "svelte": ["svelte@5.34.7", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "@jridgewell/sourcemap-codec": "^1.5.0", "@sveltejs/acorn-typescript": "^1.0.5", "@types/estree": "^1.0.5", "acorn": "^8.12.1", "aria-query": "^5.3.1", "axobject-query": "^4.1.0", "clsx": "^2.1.1", "esm-env": "^1.2.1", "esrap": "^1.4.8", "is-reference": "^3.0.3", "locate-character": "^3.0.0", "magic-string": "^0.30.11", "zimmerframe": "^1.1.2" } }, "sha512-5PEg+QQKce4t1qiOtVUhUS3AQRTtxJyGBTpxLcNWnr0Ve8q4r06bMo0Gv8uhtCPWlztZHoi3Ye7elLhu+PCTMg=="], - "svelte-check": ["svelte-check@4.2.1", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-e49SU1RStvQhoipkQ/aonDhHnG3qxHSBtNfBRb9pxVXoa+N7qybAo32KgA9wEb2PCYFNaDg7bZCdhLD1vHpdYA=="], + "svelte-check": ["svelte-check@4.2.2", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "chokidar": "^4.0.1", "fdir": "^6.2.0", "picocolors": "^1.0.0", "sade": "^1.7.4" }, "peerDependencies": { "svelte": "^4.0.0 || ^5.0.0-next.0", "typescript": ">=5.0.0" }, "bin": { "svelte-check": "bin/svelte-check" } }, "sha512-1+31EOYZ7NKN0YDMKusav2hhEoA51GD9Ws6o//0SphMT0ve9mBTsTUEX7OmDMadUP3KjNHsSKtJrqdSaD8CrGQ=="], + + "sveltekit": ["sveltekit@workspace:alchemy/templates/sveltekit"], "svgo": ["svgo@3.3.2", "", { "dependencies": { "@trysound/sax": "0.2.0", "commander": "^7.2.0", "css-select": "^5.1.0", "css-tree": "^2.3.1", "css-what": "^6.1.0", "csso": "^5.0.5", "picocolors": "^1.0.0" }, "bin": "./bin/svgo" }, "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw=="], @@ -3504,10 +3710,12 @@ "tabbable": ["tabbable@6.2.0", "", {}, "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew=="], - "tailwind-merge": ["tailwind-merge@2.6.0", "", {}, "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA=="], + "tailwind-merge": ["tailwind-merge@3.3.1", "", {}, "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g=="], "tailwindcss": ["tailwindcss@4.1.10", "", {}, "sha512-P3nr6WkvKV/ONsTzj6Gb57sWPMX29EPNPopo7+FcpkQaNsrNpZ1pv8QmrYI2RqEKD7mlGqLnGovlcYnBK0IqUA=="], + "tanstack-start": ["tanstack-start@workspace:alchemy/templates/tanstack-start"], + "tanstack-start-example-basic": ["tanstack-start-example-basic@workspace:examples/cloudflare-tanstack-start"], "tapable": ["tapable@2.2.2", "", {}, "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg=="], @@ -3518,7 +3726,7 @@ "tar-stream": ["tar-stream@3.1.7", "", { "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", "streamx": "^2.15.0" } }, "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ=="], - "terser": ["terser@5.42.0", "", { "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.14.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" } }, "sha512-UYCvU9YQW2f/Vwl+P0GfhxJxbUGLwd+5QrrGgLajzWAtC/23AX0vcise32kkP7Eu0Wu9VlzzHAXkLObgjQfFlQ=="], + "terser": ["terser@5.43.1", "", { "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.14.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" } }, "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg=="], "terser-webpack-plugin": ["terser-webpack-plugin@5.3.14", "", { "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", "schema-utils": "^4.3.0", "serialize-javascript": "^6.0.2", "terser": "^5.31.1" }, "peerDependencies": { "webpack": "^5.1.0" } }, "sha512-vkZjpUjb6OMS7dhV+tILUW6BhpDR7P2L/aQSAv+Uwk+m8KATX9EccViHTJR2qDtACKPIYndLGCyl3FMo+r2LMw=="], @@ -3576,6 +3784,8 @@ "trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="], + "trpc-cli": ["trpc-cli@0.9.2", "", { "dependencies": { "@trpc/server": "^11.1.1", "@types/omelette": "^0.4.4", "commander": "^14.0.0", "picocolors": "^1.0.1", "zod": "^3.25.3", "zod-to-json-schema": "^3.23.0" }, "peerDependencies": { "@inquirer/prompts": "*", "omelette": "*" }, "optionalPeers": ["@inquirer/prompts", "omelette"], "bin": { "trpc-cli": "dist/bin.js" } }, "sha512-JCixLtLCC40Nd8n9i8yM6tr1modW4eT5u7JGJ/S6QAO09Ptds/jcZRXe5QvAiWG63sXNHG29VnEdw8rFIU0LuA=="], + "ts-api-utils": ["ts-api-utils@2.1.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ=="], "ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="], @@ -3598,11 +3808,11 @@ "typedoc": ["typedoc@0.28.5", "", { "dependencies": { "@gerrit0/mini-shiki": "^3.2.2", "lunr": "^2.3.9", "markdown-it": "^14.1.0", "minimatch": "^9.0.5", "yaml": "^2.7.1" }, "peerDependencies": { "typescript": "5.0.x || 5.1.x || 5.2.x || 5.3.x || 5.4.x || 5.5.x || 5.6.x || 5.7.x || 5.8.x" }, "bin": { "typedoc": "bin/typedoc" } }, "sha512-5PzUddaA9FbaarUzIsEc4wNXCiO4Ot3bJNeMF2qKpYlTmM9TTaSHQ7162w756ERCkXER/+o2purRG6YOAv6EMA=="], - "typedoc-plugin-markdown": ["typedoc-plugin-markdown@4.6.4", "", { "peerDependencies": { "typedoc": "0.28.x" } }, "sha512-AnbToFS1T1H+n40QbO2+i0wE6L+55rWnj7zxnM1r781+2gmhMF2dB6dzFpaylWLQYkbg4D1Y13sYnne/6qZwdw=="], + "typedoc-plugin-markdown": ["typedoc-plugin-markdown@4.7.0", "", { "peerDependencies": { "typedoc": "0.28.x" } }, "sha512-PitbnAps2vpcqK2gargKoiFXLWFttvwUbyns/E6zGIFG5Gz8ZQJGttHnYR9csOlcSjB/uyjd8tnoayrtsXG17w=="], "typesafe-path": ["typesafe-path@0.2.2", "", {}, "sha512-OJabfkAg1WLZSqJAJ0Z6Sdt3utnbzr/jh+NAHoyWHJe8CMSy79Gm085094M9nvTPy22KzTVn5Zq5mbapCI/hPA=="], - "typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], + "typescript": ["typescript@workspace:alchemy/templates/typescript"], "typescript-auto-import-cache": ["typescript-auto-import-cache@0.3.6", "", { "dependencies": { "semver": "^7.3.8" } }, "sha512-RpuHXrknHdVdK7wv/8ug3Fr0WNsNi5l5aB8MYYuXhq2UH5lnEB1htJ1smhtD5VeCsGr2p8mUDtd83LCQDFVgjQ=="], @@ -3730,10 +3940,12 @@ "vite-hot-client": ["vite-hot-client@2.0.4", "", { "peerDependencies": { "vite": "^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0" } }, "sha512-W9LOGAyGMrbGArYJN4LBCdOC5+Zwh7dHvOHC0KmGKkJhsOzaKbpo/jEjpPKVHIW0/jBWj8RZG0NUxfgA8BxgAg=="], - "vite-node": ["vite-node@3.2.3", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-gc8aAifGuDIpZHrPjuHyP4dpQmYXqWw7D1GmDnWeNWP654UEXzVfQ5IHPSK5HaHkwB/+p1atpYpSdw/2kOv8iQ=="], + "vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], "vite-plugin-checker": ["vite-plugin-checker@0.9.3", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "chokidar": "^4.0.3", "npm-run-path": "^6.0.0", "picocolors": "^1.1.1", "picomatch": "^4.0.2", "strip-ansi": "^7.1.0", "tiny-invariant": "^1.3.3", "tinyglobby": "^0.2.13", "vscode-uri": "^3.1.0" }, "peerDependencies": { "@biomejs/biome": ">=1.7", "eslint": ">=7", "meow": "^13.2.0", "optionator": "^0.9.4", "stylelint": ">=16", "typescript": "*", "vite": ">=2.0.0", "vls": "*", "vti": "*", "vue-tsc": "~2.2.10" }, "optionalPeers": ["@biomejs/biome", "eslint", "meow", "optionator", "stylelint", "typescript", "vls", "vti", "vue-tsc"] }, "sha512-Tf7QBjeBtG7q11zG0lvoF38/2AVUzzhMNu+Wk+mcsJ00Rk/FpJ4rmUviVJpzWkagbU13cGXvKpt7CMiqtxVTbQ=="], + "vite-plugin-devtools-json": ["vite-plugin-devtools-json@0.2.0", "", { "dependencies": { "uuid": "^11.1.0" }, "peerDependencies": { "vite": "^2.7.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" } }, "sha512-K7PoaWOEJECZ1n3VbhJXsUAX2PsO0xY7KFMM/Leh7tUev0M5zi+lz+vnVVdCK17IOK9Jp9rdzHXc08cnQirGbg=="], + "vite-plugin-inspect": ["vite-plugin-inspect@11.2.0", "", { "dependencies": { "ansis": "^3.17.0", "debug": "^4.4.1", "error-stack-parser-es": "^1.0.5", "ohash": "^2.0.11", "open": "^10.1.2", "perfect-debounce": "^1.0.0", "sirv": "^3.0.1", "unplugin-utils": "^0.2.4", "vite-dev-rpc": "^1.0.7" }, "peerDependencies": { "vite": "^6.0.0" } }, "sha512-hcCncl4YK20gcrx22cPF5mR+zfxsCmX6vUQKCyudgOZMYKVVGbrxVaL3zU62W0MVSVawtf5ZR4DrLRO+9fZVWQ=="], "vite-plugin-vue-tracer": ["vite-plugin-vue-tracer@0.1.4", "", { "dependencies": { "estree-walker": "^3.0.3", "exsolve": "^1.0.5", "magic-string": "^0.30.17", "pathe": "^2.0.3", "source-map-js": "^1.2.1" }, "peerDependencies": { "vite": "^6.0.0", "vue": "^3.5.0" } }, "sha512-o6tzfvwreQWg/S42vIPmSjXHj939p+a1gnl6VICpWgMtWqoVn21YlK4X63nZvQV/D0mmJe5CCtV/h0zaNdAL6g=="], @@ -3742,13 +3954,13 @@ "vite-tsconfig-paths": ["vite-tsconfig-paths@5.1.4", "", { "dependencies": { "debug": "^4.1.1", "globrex": "^0.1.2", "tsconfck": "^3.0.3" }, "peerDependencies": { "vite": "*" }, "optionalPeers": ["vite"] }, "sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w=="], - "vitefu": ["vitefu@1.0.6", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" }, "optionalPeers": ["vite"] }, "sha512-+Rex1GlappUyNN6UfwbVZne/9cYC4+R2XDk9xkNXBKMw6HQagdX9PgZ8V2v1WUSK1wfBLp7qbI1+XSNIlB1xmA=="], + "vitefu": ["vitefu@1.0.7", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0" }, "optionalPeers": ["vite"] }, "sha512-eRWXLBbJjW3X5z5P5IHcSm2yYbYRPb2kQuc+oqsbAl99WB5kVsPbiiox+cymo8twTzifA6itvhr2CmjnaZZp0Q=="], "vitepress": ["vitepress@1.6.3", "", { "dependencies": { "@docsearch/css": "3.8.2", "@docsearch/js": "3.8.2", "@iconify-json/simple-icons": "^1.2.21", "@shikijs/core": "^2.1.0", "@shikijs/transformers": "^2.1.0", "@shikijs/types": "^2.1.0", "@types/markdown-it": "^14.1.2", "@vitejs/plugin-vue": "^5.2.1", "@vue/devtools-api": "^7.7.0", "@vue/shared": "^3.5.13", "@vueuse/core": "^12.4.0", "@vueuse/integrations": "^12.4.0", "focus-trap": "^7.6.4", "mark.js": "8.11.1", "minisearch": "^7.1.1", "shiki": "^2.1.0", "vite": "^5.4.14", "vue": "^3.5.13" }, "peerDependencies": { "markdown-it-mathjax3": "^4", "postcss": "^8" }, "optionalPeers": ["markdown-it-mathjax3", "postcss"], "bin": { "vitepress": "bin/vitepress.js" } }, "sha512-fCkfdOk8yRZT8GD9BFqusW3+GggWYZ/rYncOfmgcDtP3ualNHCAg+Robxp2/6xfH1WwPHtGpPwv7mbA3qomtBw=="], "vitepress-plugin-group-icons": ["vitepress-plugin-group-icons@1.6.0", "", { "dependencies": { "@iconify-json/logos": "^1.2.4", "@iconify-json/vscode-icons": "^1.2.18", "@iconify/utils": "^2.3.0" }, "peerDependencies": { "markdown-it": ">=14", "vite": ">=3" } }, "sha512-+nxuVETpFkOYR5qHdvj3M5otWusJyS3ozEvVf1aQaE5Oz5e6NR0naYKTtH0Zf3Qss4vnhqaYt2Lq4jUTn9JVuA=="], - "vitest": ["vitest@3.2.3", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.3", "@vitest/mocker": "3.2.3", "@vitest/pretty-format": "^3.2.3", "@vitest/runner": "3.2.3", "@vitest/snapshot": "3.2.3", "@vitest/spy": "3.2.3", "@vitest/utils": "3.2.3", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.0", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.3", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.3", "@vitest/ui": "3.2.3", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-E6U2ZFXe3N/t4f5BwUaVCKRLHqUpk1CBWeMh78UT4VaTPH/2dyvH6ALl29JTovEPu9dVKr/K/J4PkXgrMbw4Ww=="], + "vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="], "volar-service-css": ["volar-service-css@0.0.62", "", { "dependencies": { "vscode-css-languageservice": "^6.3.0", "vscode-languageserver-textdocument": "^1.0.11", "vscode-uri": "^3.0.8" }, "peerDependencies": { "@volar/language-service": "~2.4.0" }, "optionalPeers": ["@volar/language-service"] }, "sha512-JwNyKsH3F8PuzZYuqPf+2e+4CTU8YoyUHEHVnoXNlrLe7wy9U3biomZ56llN69Ris7TTy/+DEX41yVxQpM4qvg=="], @@ -3784,7 +3996,7 @@ "vscode-uri": ["vscode-uri@3.1.0", "", {}, "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ=="], - "vue": ["vue@3.5.16", "", { "dependencies": { "@vue/compiler-dom": "3.5.16", "@vue/compiler-sfc": "3.5.16", "@vue/runtime-dom": "3.5.16", "@vue/server-renderer": "3.5.16", "@vue/shared": "3.5.16" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-rjOV2ecxMd5SiAmof2xzh2WxntRcigkX/He4YFJ6WdRvVUrbt6DxC1Iujh10XLl8xCDRDtGKMeO3D+pRQ1PP9w=="], + "vue": ["vue@3.5.17", "", { "dependencies": { "@vue/compiler-dom": "3.5.17", "@vue/compiler-sfc": "3.5.17", "@vue/runtime-dom": "3.5.17", "@vue/server-renderer": "3.5.17", "@vue/shared": "3.5.17" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-LbHV3xPN9BeljML+Xctq4lbz2lVHCR6DtbpTf5XIO6gugpXUN49j2QQPcMj086r9+AkJ0FfUT8xjulKKBkkr9g=="], "vue-bundle-renderer": ["vue-bundle-renderer@2.1.1", "", { "dependencies": { "ufo": "^1.5.4" } }, "sha512-+qALLI5cQncuetYOXp4yScwYvqh8c6SMXee3B+M7oTZxOgtESP0l4j/fXdEJoZ+EdMxkGWIj+aSEyjXkOdmd7g=="], @@ -3802,7 +4014,7 @@ "webpack": ["webpack@5.99.9", "", { "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "@webassemblyjs/ast": "^1.14.1", "@webassemblyjs/wasm-edit": "^1.14.1", "@webassemblyjs/wasm-parser": "^1.14.1", "acorn": "^8.14.0", "browserslist": "^4.24.0", "chrome-trace-event": "^1.0.2", "enhanced-resolve": "^5.17.1", "es-module-lexer": "^1.2.1", "eslint-scope": "5.1.1", "events": "^3.2.0", "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.2.11", "json-parse-even-better-errors": "^2.3.1", "loader-runner": "^4.2.0", "mime-types": "^2.1.27", "neo-async": "^2.6.2", "schema-utils": "^4.3.2", "tapable": "^2.1.1", "terser-webpack-plugin": "^5.3.11", "watchpack": "^2.4.1", "webpack-sources": "^3.2.3" }, "bin": { "webpack": "bin/webpack.js" } }, "sha512-brOPwM3JnmOa+7kd3NsmOUOwbDAj8FT9xDsG3IW0MgbN9yZV7Oi/s/+MNQ/EcSMqw7qfoRyXPoeEWT8zLVdVGg=="], - "webpack-sources": ["webpack-sources@3.3.2", "", {}, "sha512-ykKKus8lqlgXX/1WjudpIEjqsafjOTcOJqxnAbMLAu/KCsDCJ6GBtvscewvTkrn24HsnvFwrSCbenFrhtcCsAA=="], + "webpack-sources": ["webpack-sources@3.3.3", "", {}, "sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg=="], "webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="], @@ -3812,7 +4024,7 @@ "whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="], - "which": ["which@5.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ=="], + "which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], "which-pm-runs": ["which-pm-runs@1.1.0", "", {}, "sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA=="], @@ -3826,11 +4038,11 @@ "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], - "workerd": ["workerd@1.20250604.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20250604.0", "@cloudflare/workerd-darwin-arm64": "1.20250604.0", "@cloudflare/workerd-linux-64": "1.20250604.0", "@cloudflare/workerd-linux-arm64": "1.20250604.0", "@cloudflare/workerd-windows-64": "1.20250604.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-sHz9R1sxPpnyq3ptrI/5I96sYTMA2+Ljm75oJDbmEcZQwNyezpu9Emerzt3kzzjCJQqtdscGOidWv4RKGZXzAA=="], + "workerd": ["workerd@1.20250617.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20250617.0", "@cloudflare/workerd-darwin-arm64": "1.20250617.0", "@cloudflare/workerd-linux-64": "1.20250617.0", "@cloudflare/workerd-linux-arm64": "1.20250617.0", "@cloudflare/workerd-windows-64": "1.20250617.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-Uv6p0PYUHp/W/aWfUPLkZVAoAjapisM27JJlwcX9wCPTfCfnuegGOxFMvvlYpmNaX4YCwEdLCwuNn3xkpSkuZw=="], "worktop": ["worktop@0.8.0-next.18", "", { "dependencies": { "mrmime": "^2.0.0", "regexparam": "^3.0.0" } }, "sha512-+TvsA6VAVoMC3XDKR5MoC/qlLqDixEfOBysDEKnPIPou/NvoPWCAuXHXMsswwlvmEuvX56lQjvELLyLuzTKvRw=="], - "wrangler": ["wrangler@4.20.0", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.3.2", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "miniflare": "4.20250604.1", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.17", "workerd": "1.20250604.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20250604.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-gxMLaSnYp3VLdGPZu4fc/9UlB7PnSVwni25v32NM9szG2yTt+gx5RunWzmoLplplIfEMkBuV3wA47vccNu7zcA=="], + "wrangler": ["wrangler@4.20.5", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.3.3", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "miniflare": "4.20250617.3", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.17", "workerd": "1.20250617.0" }, "optionalDependencies": { "fsevents": "~2.3.2" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20250617.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-tmiyt2vBHszhdzJEDbCpFLU2RiV7/QzvGMV07Zaz4ptqiU2h/lTojyNJAugPpSFNiOuY+k0g3ENNTDQqoUkMFA=="], "wrap-ansi": ["wrap-ansi@9.0.0", "", { "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", "strip-ansi": "^7.1.0" } }, "sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q=="], @@ -3866,8 +4078,6 @@ "yoctocolors": ["yoctocolors@2.1.1", "", {}, "sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ=="], - "yoctocolors-cjs": ["yoctocolors-cjs@2.1.2", "", {}, "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA=="], - "youch": ["youch@3.3.4", "", { "dependencies": { "cookie": "^0.7.1", "mustache": "^4.2.0", "stacktracey": "^2.1.8" } }, "sha512-UeVBXie8cA35DS6+nBkls68xaBBXCye0CNznrhszZjTbRVnJKQuNsyLKBTTL4ln1o1rh2PKtv35twV7irj5SEg=="], "youch-core": ["youch-core@0.3.2", "", { "dependencies": { "@poppinss/exception": "^1.2.0", "error-stack-parser-es": "^1.0.5" } }, "sha512-fusrlIMLeRvTFYLUjJ9KzlGC3N+6MOPJ68HNj/yJv2nz7zq8t4HEviLms2gkdRPUS7F5rZ5n+pYx9r88m6IE1g=="], @@ -3876,7 +4086,7 @@ "zip-stream": ["zip-stream@6.0.1", "", { "dependencies": { "archiver-utils": "^5.0.0", "compress-commons": "^6.0.2", "readable-stream": "^4.0.0" } }, "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA=="], - "zod": ["zod@3.25.65", "", {}, "sha512-kMyE2qsXK1p+TAvO7zsf5wMFiCejU3obrUDs9bR1q5CBKykfvp7QhhXrycUylMoOow0iEUSyjLlZZdCsHwSldQ=="], + "zod": ["zod@3.25.67", "", {}, "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw=="], "zod-to-json-schema": ["zod-to-json-schema@3.24.5", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g=="], @@ -3900,7 +4110,7 @@ "@astrojs/check/chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], - "@astrojs/markdown-remark/shiki": ["shiki@3.6.0", "", { "dependencies": { "@shikijs/core": "3.6.0", "@shikijs/engine-javascript": "3.6.0", "@shikijs/engine-oniguruma": "3.6.0", "@shikijs/langs": "3.6.0", "@shikijs/themes": "3.6.0", "@shikijs/types": "3.6.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-tKn/Y0MGBTffQoklaATXmTqDU02zx8NYBGQ+F6gy87/YjKbizcLd+Cybh/0ZtOBX9r1NEnAy/GTRDKtOsc1L9w=="], + "@astrojs/markdown-remark/shiki": ["shiki@3.7.0", "", { "dependencies": { "@shikijs/core": "3.7.0", "@shikijs/engine-javascript": "3.7.0", "@shikijs/engine-oniguruma": "3.7.0", "@shikijs/langs": "3.7.0", "@shikijs/themes": "3.7.0", "@shikijs/types": "3.7.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-ZcI4UT9n6N2pDuM2n3Jbk0sR4Swzq43nLPgS/4h0E3B/NrFn2HKElrDtceSf8Zx/OWYOo7G1SAtBLypCp+YXqg=="], "@aws-crypto/crc32/@aws-sdk/types": ["@aws-sdk/types@3.723.0", "", { "dependencies": { "@smithy/types": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-LmK3kwiMZG1y5g3LGihT9mNkeNOmwEyPk6HGcJqh0wOSV4QpWoKu2epyKE4MLQNUUlz2kOVbVbOrwmI6ZcteuA=="], @@ -3916,8 +4126,6 @@ "@aws-crypto/sha256-js/@aws-sdk/types": ["@aws-sdk/types@3.723.0", "", { "dependencies": { "@smithy/types": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-LmK3kwiMZG1y5g3LGihT9mNkeNOmwEyPk6HGcJqh0wOSV4QpWoKu2epyKE4MLQNUUlz2kOVbVbOrwmI6ZcteuA=="], - "@aws-crypto/util/@aws-sdk/types": ["@aws-sdk/types@3.723.0", "", { "dependencies": { "@smithy/types": "^4.0.0", "tslib": "^2.6.2" } }, "sha512-LmK3kwiMZG1y5g3LGihT9mNkeNOmwEyPk6HGcJqh0wOSV4QpWoKu2epyKE4MLQNUUlz2kOVbVbOrwmI6ZcteuA=="], - "@aws-crypto/util/@smithy/util-utf8": ["@smithy/util-utf8@2.3.0", "", { "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A=="], "@aws-sdk/client-s3/@aws-sdk/core": ["@aws-sdk/core@3.723.0", "", { "dependencies": { "@aws-sdk/types": "3.723.0", "@smithy/core": "^3.0.0", "@smithy/node-config-provider": "^4.0.0", "@smithy/property-provider": "^4.0.0", "@smithy/protocol-http": "^5.0.0", "@smithy/signature-v4": "^5.0.0", "@smithy/smithy-client": "^4.0.0", "@smithy/types": "^4.0.0", "@smithy/util-middleware": "^4.0.0", "fast-xml-parser": "4.4.1", "tslib": "^2.6.2" } }, "sha512-UraXNmvqj3vScSsTkjMwQkhei30BhXlW5WxX6JacMKVtl95c7z0qOXquTWeTalYkFfulfdirUhvSZrl+hcyqTw=="], @@ -4030,6 +4238,8 @@ "@cloudflare/unenv-preset/unenv": ["unenv@2.0.0-rc.17", "", { "dependencies": { "defu": "^6.1.4", "exsolve": "^1.0.4", "ohash": "^2.0.11", "pathe": "^2.0.3", "ufo": "^1.6.1" } }, "sha512-B06u0wXkEd+o5gOCMl/ZHl5cfpYbDZKAT+HWTL+Hws6jWu7dCiqBBXXXzMFcFVJb8D4ytAnYmxJA83uwOQRSsg=="], + "@cloudflare/vite-plugin/miniflare": ["miniflare@4.20250617.3", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "^5.28.5", "workerd": "1.20250617.0", "ws": "8.18.0", "youch": "3.3.4", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-j+LZycT11UdlVeNdaqD0XdNnYnqAL+wXmboz+tNPFgTq6zhD489Ujj3BfSDyEHDCA9UFBLbkc5ByGWBh+pYZ5Q=="], + "@cloudflare/vite-plugin/unenv": ["unenv@2.0.0-rc.17", "", { "dependencies": { "defu": "^6.1.4", "exsolve": "^1.0.4", "ohash": "^2.0.11", "pathe": "^2.0.3", "ufo": "^1.6.1" } }, "sha512-B06u0wXkEd+o5gOCMl/ZHl5cfpYbDZKAT+HWTL+Hws6jWu7dCiqBBXXXzMFcFVJb8D4ytAnYmxJA83uwOQRSsg=="], "@cloudflare/vite-plugin/ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="], @@ -4054,12 +4264,10 @@ "@eslint/plugin-kit/@eslint/core": ["@eslint/core@0.15.0", "", { "dependencies": { "@types/json-schema": "^7.0.15" } }, "sha512-b7ePw78tEWWkpgZCDYkbqDOP8dmM6qe+AOC6iuJqlq1R/0ahMAeH3qynpnqKFGkMltrp44ohV4ubGyvLX28tzw=="], - "@gerrit0/mini-shiki/@shikijs/types": ["@shikijs/types@3.6.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-cLWFiToxYu0aAzJqhXTQsFiJRTFDAGl93IrMSBNaGSzs7ixkLfdG6pH11HipuWFGW5vyx4X47W8HDQ7eSrmBUg=="], + "@gerrit0/mini-shiki/@shikijs/types": ["@shikijs/types@3.7.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-MGaLeaRlSWpnP0XSAum3kP3a8vtcTsITqoEPYdt3lQG3YCdQH4DnEhodkYcNMcU0uW0RffhoD1O3e0vG5eSBBg=="], "@humanfs/node/@humanwhocodes/retry": ["@humanwhocodes/retry@0.3.1", "", {}, "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA=="], - "@inquirer/core/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="], - "@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], "@isaacs/cliui/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], @@ -4100,6 +4308,8 @@ "@nuxt/devtools/execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="], + "@nuxt/devtools/which": ["which@5.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ=="], + "@nuxt/devtools/ws": ["ws@8.18.2", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ=="], "@nuxt/devtools-kit/execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="], @@ -4120,6 +4330,8 @@ "@poppinss/dumper/supports-color": ["supports-color@10.0.0", "", {}, "sha512-HRVVSbCCMbj7/kdWF9Q+bbckjBHLtHMEoJWlkmYzzdwhYMkjkOwubLM6t7NbWKjgKamGDrWL1++KrjUO1t9oAQ=="], + "@prisma/adapter-d1/@cloudflare/workers-types": ["@cloudflare/workers-types@4.20250214.0", "", {}, "sha512-+M8oOFVbyXT5GeJrYLWMUGyPf5wGB4+k59PPqdedtOig7NjZ5r4S79wMdaZ/EV5IV8JPtZBSNjTKpDnNmfxjaQ=="], + "@react-router/dev/chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], "@react-router/dev/fs-extra": ["fs-extra@10.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ=="], @@ -4136,6 +4348,8 @@ "@redwoodjs/starter-drizzle/@types/node": ["@types/node@22.15.32", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-3jigKqgSjsH6gYZv2nEsqdXfZqIFGAV36XYYjf9KGZ3PSG+IhLecqPnI310RvjutyMwifE2hhhNEklOUrvx/wA=="], + "@redwoodjs/starter-drizzle/rwsdk": ["rwsdk@0.0.83", "", { "dependencies": { "@cloudflare/vite-plugin": "^1.1.0", "@cloudflare/workers-types": "^4.20250407.0", "@puppeteer/browsers": "^2.8.0", "@types/fs-extra": "^11.0.4", "@types/react": "^19.1.2", "@types/react-dom": "^19.1.2", "@types/react-is": "^19.0.0", "@vitejs/plugin-react": "^4.3.4", "debug": "^4.4.0", "enhanced-resolve": "^5.18.1", "es-module-lexer": "^1.5.4", "eventsource-parser": "^3.0.0", "execa": "^9.5.2", "fs-extra": "^11.3.0", "glob": "^11.0.1", "ignore": "^7.0.4", "jsonc-parser": "^3.3.1", "lodash": "^4.17.21", "magic-string": "^0.30.17", "miniflare": "^4.20250405.0", "picocolors": "^1.1.1", "puppeteer-core": "^22.8.1", "react": "19.2.0-canary-39cad7af-20250411", "react-dom": "19.2.0-canary-39cad7af-20250411", "react-is": "^19.0.0", "react-server-dom-webpack": "19.2.0-canary-39cad7af-20250411", "rsc-html-stream": "^0.0.6", "tmp-promise": "^3.0.3", "ts-morph": "^25.0.1", "unique-names-generator": "^4.7.1", "vibe-rules": "^0.2.31", "vite-tsconfig-paths": "^5.1.4", "wrangler": "^4.14.1" }, "peerDependencies": { "vite": "^6.2.6" }, "bin": { "rw-scripts": "bin/rw-scripts.mjs" } }, "sha512-Uwe8zou31gRKMvRnQ/Qt2VD+RhG4WS6eP8dBzFCw+d/6c87JhW7tx0jsk6WTN5hII7qytvKRR4cgAvHVHlwmKg=="], + "@rollup/plugin-commonjs/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], "@rollup/plugin-commonjs/is-reference": ["is-reference@1.2.1", "", { "dependencies": { "@types/estree": "*" } }, "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ=="], @@ -4146,11 +4360,11 @@ "@shikijs/core/@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw=="], - "@shikijs/engine-oniguruma/@shikijs/types": ["@shikijs/types@3.6.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-cLWFiToxYu0aAzJqhXTQsFiJRTFDAGl93IrMSBNaGSzs7ixkLfdG6pH11HipuWFGW5vyx4X47W8HDQ7eSrmBUg=="], + "@shikijs/engine-oniguruma/@shikijs/types": ["@shikijs/types@3.7.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-MGaLeaRlSWpnP0XSAum3kP3a8vtcTsITqoEPYdt3lQG3YCdQH4DnEhodkYcNMcU0uW0RffhoD1O3e0vG5eSBBg=="], - "@shikijs/langs/@shikijs/types": ["@shikijs/types@3.6.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-cLWFiToxYu0aAzJqhXTQsFiJRTFDAGl93IrMSBNaGSzs7ixkLfdG6pH11HipuWFGW5vyx4X47W8HDQ7eSrmBUg=="], + "@shikijs/langs/@shikijs/types": ["@shikijs/types@3.7.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-MGaLeaRlSWpnP0XSAum3kP3a8vtcTsITqoEPYdt3lQG3YCdQH4DnEhodkYcNMcU0uW0RffhoD1O3e0vG5eSBBg=="], - "@shikijs/themes/@shikijs/types": ["@shikijs/types@3.6.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-cLWFiToxYu0aAzJqhXTQsFiJRTFDAGl93IrMSBNaGSzs7ixkLfdG6pH11HipuWFGW5vyx4X47W8HDQ7eSrmBUg=="], + "@shikijs/themes/@shikijs/types": ["@shikijs/types@3.7.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-MGaLeaRlSWpnP0XSAum3kP3a8vtcTsITqoEPYdt3lQG3YCdQH4DnEhodkYcNMcU0uW0RffhoD1O3e0vG5eSBBg=="], "@sveltejs/kit/acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], @@ -4172,62 +4386,26 @@ "@tanstack/react-router/jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], - "@tanstack/react-start-client/@tanstack/react-router": ["@tanstack/react-router@1.121.19", "", { "dependencies": { "@tanstack/history": "1.121.19", "@tanstack/react-store": "^0.7.0", "@tanstack/router-core": "1.121.19", "jsesc": "^3.1.0", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-g7De/6IX3QWxuk0eXgw/AIIVkxGhf2OV7abZjj70NOgwooq3HyXim8SzvX4gQ3H7QUEWyAw32fjBg29cq3Ep4w=="], - - "@tanstack/react-start-client/@tanstack/router-core": ["@tanstack/router-core@1.121.19", "", { "dependencies": { "@tanstack/history": "1.121.19", "@tanstack/store": "^0.7.0", "tiny-invariant": "^1.3.3" } }, "sha512-yIvaf+5vTsq4k5XMJzAMqL0JKtSczFN94m1/snfgOSq3rPmBCi1vivCTdCR7krNvlJfieykn+i8CYBCUhife5A=="], - "@tanstack/react-start-client/cookie-es": ["cookie-es@1.2.2", "", {}, "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg=="], "@tanstack/react-start-client/jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], - "@tanstack/react-start-config/@tanstack/router-core": ["@tanstack/router-core@1.121.19", "", { "dependencies": { "@tanstack/history": "1.121.19", "@tanstack/store": "^0.7.0", "tiny-invariant": "^1.3.3" } }, "sha512-yIvaf+5vTsq4k5XMJzAMqL0JKtSczFN94m1/snfgOSq3rPmBCi1vivCTdCR7krNvlJfieykn+i8CYBCUhife5A=="], - - "@tanstack/react-start-router-manifest/@tanstack/router-core": ["@tanstack/router-core@1.121.19", "", { "dependencies": { "@tanstack/history": "1.121.19", "@tanstack/store": "^0.7.0", "tiny-invariant": "^1.3.3" } }, "sha512-yIvaf+5vTsq4k5XMJzAMqL0JKtSczFN94m1/snfgOSq3rPmBCi1vivCTdCR7krNvlJfieykn+i8CYBCUhife5A=="], - - "@tanstack/react-start-server/@tanstack/history": ["@tanstack/history@1.121.19", "", {}, "sha512-+fskAbUbLEksyJBELUYwmPr6ne+PLgY/aThT/tUsNyPGXo+qvd4M5i7Inryh4VFE6sU1s3fMjbd59ykxU+h5lA=="], - - "@tanstack/react-start-server/@tanstack/react-router": ["@tanstack/react-router@1.121.19", "", { "dependencies": { "@tanstack/history": "1.121.19", "@tanstack/react-store": "^0.7.0", "@tanstack/router-core": "1.121.19", "jsesc": "^3.1.0", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-g7De/6IX3QWxuk0eXgw/AIIVkxGhf2OV7abZjj70NOgwooq3HyXim8SzvX4gQ3H7QUEWyAw32fjBg29cq3Ep4w=="], - - "@tanstack/react-start-server/@tanstack/router-core": ["@tanstack/router-core@1.121.19", "", { "dependencies": { "@tanstack/history": "1.121.19", "@tanstack/store": "^0.7.0", "tiny-invariant": "^1.3.3" } }, "sha512-yIvaf+5vTsq4k5XMJzAMqL0JKtSczFN94m1/snfgOSq3rPmBCi1vivCTdCR7krNvlJfieykn+i8CYBCUhife5A=="], - "@tanstack/react-start-server/h3": ["h3@1.13.0", "", { "dependencies": { "cookie-es": "^1.2.2", "crossws": ">=0.2.0 <0.4.0", "defu": "^6.1.4", "destr": "^2.0.3", "iron-webcrypto": "^1.2.1", "ohash": "^1.1.4", "radix3": "^1.1.2", "ufo": "^1.5.4", "uncrypto": "^0.1.3", "unenv": "^1.10.0" } }, "sha512-vFEAu/yf8UMUcB4s43OaDaigcqpQd14yanmOsn+NcRX3/guSKncyE2rOYhq8RIchgJrPSs/QiIddnTTR1ddiAg=="], - "@tanstack/react-start-server/jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], - - "@tanstack/router-devtools-core/@tanstack/router-core": ["@tanstack/router-core@1.121.19", "", { "dependencies": { "@tanstack/history": "1.121.19", "@tanstack/store": "^0.7.0", "tiny-invariant": "^1.3.3" } }, "sha512-yIvaf+5vTsq4k5XMJzAMqL0JKtSczFN94m1/snfgOSq3rPmBCi1vivCTdCR7krNvlJfieykn+i8CYBCUhife5A=="], - - "@tanstack/router-generator/@tanstack/router-core": ["@tanstack/router-core@1.121.19", "", { "dependencies": { "@tanstack/history": "1.121.19", "@tanstack/store": "^0.7.0", "tiny-invariant": "^1.3.3" } }, "sha512-yIvaf+5vTsq4k5XMJzAMqL0JKtSczFN94m1/snfgOSq3rPmBCi1vivCTdCR7krNvlJfieykn+i8CYBCUhife5A=="], - - "@tanstack/router-plugin/@tanstack/router-core": ["@tanstack/router-core@1.121.19", "", { "dependencies": { "@tanstack/history": "1.121.19", "@tanstack/store": "^0.7.0", "tiny-invariant": "^1.3.3" } }, "sha512-yIvaf+5vTsq4k5XMJzAMqL0JKtSczFN94m1/snfgOSq3rPmBCi1vivCTdCR7krNvlJfieykn+i8CYBCUhife5A=="], - "@tanstack/router-plugin/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], "@tanstack/server-functions-plugin/@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="], - "@tanstack/start-api-routes/@tanstack/router-core": ["@tanstack/router-core@1.121.19", "", { "dependencies": { "@tanstack/history": "1.121.19", "@tanstack/store": "^0.7.0", "tiny-invariant": "^1.3.3" } }, "sha512-yIvaf+5vTsq4k5XMJzAMqL0JKtSczFN94m1/snfgOSq3rPmBCi1vivCTdCR7krNvlJfieykn+i8CYBCUhife5A=="], - - "@tanstack/start-client-core/@tanstack/router-core": ["@tanstack/router-core@1.121.19", "", { "dependencies": { "@tanstack/history": "1.121.19", "@tanstack/store": "^0.7.0", "tiny-invariant": "^1.3.3" } }, "sha512-yIvaf+5vTsq4k5XMJzAMqL0JKtSczFN94m1/snfgOSq3rPmBCi1vivCTdCR7krNvlJfieykn+i8CYBCUhife5A=="], - "@tanstack/start-client-core/cookie-es": ["cookie-es@1.2.2", "", {}, "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg=="], "@tanstack/start-plugin-core/@babel/code-frame": ["@babel/code-frame@7.26.2", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", "js-tokens": "^4.0.0", "picocolors": "^1.0.0" } }, "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ=="], - "@tanstack/start-plugin-core/@tanstack/router-core": ["@tanstack/router-core@1.121.19", "", { "dependencies": { "@tanstack/history": "1.121.19", "@tanstack/store": "^0.7.0", "tiny-invariant": "^1.3.3" } }, "sha512-yIvaf+5vTsq4k5XMJzAMqL0JKtSczFN94m1/snfgOSq3rPmBCi1vivCTdCR7krNvlJfieykn+i8CYBCUhife5A=="], - "@tanstack/start-plugin-core/h3": ["h3@1.13.0", "", { "dependencies": { "cookie-es": "^1.2.2", "crossws": ">=0.2.0 <0.4.0", "defu": "^6.1.4", "destr": "^2.0.3", "iron-webcrypto": "^1.2.1", "ohash": "^1.1.4", "radix3": "^1.1.2", "ufo": "^1.5.4", "uncrypto": "^0.1.3", "unenv": "^1.10.0" } }, "sha512-vFEAu/yf8UMUcB4s43OaDaigcqpQd14yanmOsn+NcRX3/guSKncyE2rOYhq8RIchgJrPSs/QiIddnTTR1ddiAg=="], - "@tanstack/start-server-core/@tanstack/history": ["@tanstack/history@1.121.19", "", {}, "sha512-+fskAbUbLEksyJBELUYwmPr6ne+PLgY/aThT/tUsNyPGXo+qvd4M5i7Inryh4VFE6sU1s3fMjbd59ykxU+h5lA=="], - - "@tanstack/start-server-core/@tanstack/router-core": ["@tanstack/router-core@1.121.19", "", { "dependencies": { "@tanstack/history": "1.121.19", "@tanstack/store": "^0.7.0", "tiny-invariant": "^1.3.3" } }, "sha512-yIvaf+5vTsq4k5XMJzAMqL0JKtSczFN94m1/snfgOSq3rPmBCi1vivCTdCR7krNvlJfieykn+i8CYBCUhife5A=="], - "@tanstack/start-server-core/h3": ["h3@1.13.0", "", { "dependencies": { "cookie-es": "^1.2.2", "crossws": ">=0.2.0 <0.4.0", "defu": "^6.1.4", "destr": "^2.0.3", "iron-webcrypto": "^1.2.1", "ohash": "^1.1.4", "radix3": "^1.1.2", "ufo": "^1.5.4", "uncrypto": "^0.1.3", "unenv": "^1.10.0" } }, "sha512-vFEAu/yf8UMUcB4s43OaDaigcqpQd14yanmOsn+NcRX3/guSKncyE2rOYhq8RIchgJrPSs/QiIddnTTR1ddiAg=="], "@tanstack/start-server-core/jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], - "@tanstack/start-server-functions-fetcher/@tanstack/router-core": ["@tanstack/router-core@1.121.19", "", { "dependencies": { "@tanstack/history": "1.121.19", "@tanstack/store": "^0.7.0", "tiny-invariant": "^1.3.3" } }, "sha512-yIvaf+5vTsq4k5XMJzAMqL0JKtSczFN94m1/snfgOSq3rPmBCi1vivCTdCR7krNvlJfieykn+i8CYBCUhife5A=="], - - "@tanstack/start-server-functions-handler/@tanstack/router-core": ["@tanstack/router-core@1.121.19", "", { "dependencies": { "@tanstack/history": "1.121.19", "@tanstack/store": "^0.7.0", "tiny-invariant": "^1.3.3" } }, "sha512-yIvaf+5vTsq4k5XMJzAMqL0JKtSczFN94m1/snfgOSq3rPmBCi1vivCTdCR7krNvlJfieykn+i8CYBCUhife5A=="], - "@vercel/nft/acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], "@vercel/nft/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], @@ -4256,11 +4434,15 @@ "alchemy/@biomejs/biome": ["@biomejs/biome@1.9.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "1.9.4", "@biomejs/cli-darwin-x64": "1.9.4", "@biomejs/cli-linux-arm64": "1.9.4", "@biomejs/cli-linux-arm64-musl": "1.9.4", "@biomejs/cli-linux-x64": "1.9.4", "@biomejs/cli-linux-x64-musl": "1.9.4", "@biomejs/cli-win32-arm64": "1.9.4", "@biomejs/cli-win32-x64": "1.9.4" }, "bin": { "biome": "bin/biome" } }, "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog=="], + "alchemy/@types/node": ["@types/node@24.0.4", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ulyqAkrhnuNq9pB76DRBTkcS6YsmDALy6Ua63V8OhrOBgbcYt6IOdzpw5P1+dyRIyMerzLkeYWBeOXPpA9GMAA=="], + + "alchemy/alchemy": ["alchemy@0.37.2", "", { "dependencies": { "@aws-sdk/credential-providers": "^3.0.0", "@cloudflare/unenv-preset": "^2.3.1", "@cloudflare/workers-shared": "^0.17.5", "@iarna/toml": "^2.2.5", "@smithy/node-config-provider": "^4.0.0", "@swc/core": "^1.11.24", "aws4fetch": "^1.0.20", "diff": "^8.0.2", "esbuild": "^0.25.1", "fast-json-patch": "^3.1.1", "glob": "^10.0.0", "jszip": "^3.0.0", "kleur": "^4.1.5", "libsodium-wrappers": "^0.7.15", "turndown": "^7.0.0", "unenv": "2.0.0-rc.15", "yaml": "^2.0.0" }, "peerDependencies": { "@ai-sdk/openai": "^1.1.9", "@ai-sdk/openai-compatible": "^0.2.2", "@aws-sdk/client-dynamodb": "^3.0.0", "@aws-sdk/client-iam": "^3.0.0", "@aws-sdk/client-lambda": "^3.0.0", "@aws-sdk/client-s3": "^3.0.0", "@aws-sdk/client-sagemaker": "^3.0.0", "@aws-sdk/client-ses": "^3.0.0", "@aws-sdk/client-sesv2": "^3.0.0", "@aws-sdk/client-sqs": "^3.0.0", "@aws-sdk/client-ssm": "^3.0.0", "@aws-sdk/client-sts": "^3.0.0", "@octokit/rest": "^21.1.1", "ai": "^4.0.0", "arktype": "^2.0.0", "cloudflare": "^4.0.0", "dofs": "^0.0.1", "hono": "^4.0.0", "prettier": "^3.0.0", "stripe": "^17.0.0", "zod": "^3.0.0" }, "bin": { "alchemy": "bin/alchemy.mjs" } }, "sha512-n/ZauK3dXPU7INjzODKQN6VC/yOigTiDgGDmh2A+7YgJOlRv2fwoknzA/U93raHblZR5qywjhBe08sICWXjRrA=="], + "alchemy/braintrust": ["braintrust@0.0.201", "", { "dependencies": { "@ai-sdk/provider": "^1.0.1", "@braintrust/core": "0.0.86", "@next/env": "^14.2.3", "@vercel/functions": "^1.0.2", "ai": "^3.2.16", "argparse": "^2.0.1", "chalk": "^4.1.2", "cli-progress": "^3.12.0", "cors": "^2.8.5", "dotenv": "^16.4.5", "esbuild": "^0.25.3", "eventsource-parser": "^1.1.2", "express": "^4.21.2", "graceful-fs": "^4.2.11", "http-errors": "^2.0.0", "minimatch": "^9.0.3", "mustache": "^4.2.0", "pluralize": "^8.0.0", "simple-git": "^3.21.0", "slugify": "^1.6.6", "source-map": "^0.7.4", "uuid": "^9.0.1", "zod": "^3.22.4", "zod-to-json-schema": "^3.22.5" }, "bin": { "braintrust": "dist/cli.js" } }, "sha512-qH4esyOskiQ25OzbmlnPMVKEImDuFANEuYknNEsgS2f3M7t5SfbZxdelbYWcFPbUwQO3TR3XktszWcc3+oAZKg=="], - "alchemy/wrangler": ["wrangler@3.114.9", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.3.4", "@cloudflare/unenv-preset": "2.0.2", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@esbuild-plugins/node-modules-polyfill": "0.2.2", "blake3-wasm": "2.1.5", "esbuild": "0.17.19", "miniflare": "3.20250408.2", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.14", "workerd": "1.20250408.0" }, "optionalDependencies": { "fsevents": "~2.3.2", "sharp": "^0.33.5" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20250408.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-1e0gL+rxLF04kM9bW4sxoDGLXpJ1x53Rx1t18JuUm6F67qadKKPISyUAXuBeIQudWrCWEBXaTVnSdLHz0yBXbA=="], + "alchemy/typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], - "ansi-escapes/type-fest": ["type-fest@0.21.3", "", {}, "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w=="], + "alchemy/wrangler": ["wrangler@3.114.9", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.3.4", "@cloudflare/unenv-preset": "2.0.2", "@esbuild-plugins/node-globals-polyfill": "0.2.3", "@esbuild-plugins/node-modules-polyfill": "0.2.2", "blake3-wasm": "2.1.5", "esbuild": "0.17.19", "miniflare": "3.20250408.2", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.14", "workerd": "1.20250408.0" }, "optionalDependencies": { "fsevents": "~2.3.2", "sharp": "^0.33.5" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20250408.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-1e0gL+rxLF04kM9bW4sxoDGLXpJ1x53Rx1t18JuUm6F67qadKKPISyUAXuBeIQudWrCWEBXaTVnSdLHz0yBXbA=="], "anymatch/micromatch": ["micromatch@2.3.11", "", { "dependencies": { "arr-diff": "^2.0.0", "array-unique": "^0.2.1", "braces": "^1.8.2", "expand-brackets": "^0.1.4", "extglob": "^0.3.1", "filename-regex": "^2.0.0", "is-extglob": "^1.0.0", "is-glob": "^2.0.1", "kind-of": "^3.0.2", "normalize-path": "^2.0.1", "object.omit": "^2.0.0", "parse-glob": "^3.0.4", "regex-cache": "^0.4.2" } }, "sha512-LnU2XFEk9xxSJ6rfgAry/ty5qwUTyHYOBU0g4R6tIw5ljwgGIBmiKhRWLw5NpMOnrgUNcDJ4WMp8rl3sYVHLNA=="], @@ -4272,16 +4454,16 @@ "archiver-utils/readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="], - "astro/acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], - - "astro/diff": ["diff@5.2.0", "", {}, "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A=="], + "astro/astro": ["astro@5.10.0", "", { "dependencies": { "@astrojs/compiler": "^2.12.2", "@astrojs/internal-helpers": "0.6.1", "@astrojs/markdown-remark": "6.3.2", "@astrojs/telemetry": "3.3.0", "@capsizecss/unpack": "^2.4.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.1.4", "acorn": "^8.14.1", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "boxen": "8.0.1", "ci-info": "^4.2.0", "clsx": "^2.1.1", "common-ancestor-path": "^1.0.1", "cookie": "^1.0.2", "cssesc": "^3.0.0", "debug": "^4.4.0", "deterministic-object-hash": "^2.0.2", "devalue": "^5.1.1", "diff": "^5.2.0", "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^1.6.0", "esbuild": "^0.25.0", "estree-walker": "^3.0.3", "flattie": "^1.1.1", "fontace": "~0.3.0", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.1.1", "import-meta-resolve": "^4.1.0", "js-yaml": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.17", "magicast": "^0.3.5", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "p-limit": "^6.2.0", "p-queue": "^8.1.0", "package-manager-detector": "^1.1.0", "picomatch": "^4.0.2", "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.7.1", "shiki": "^3.2.1", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.12", "tsconfck": "^3.1.5", "ultrahtml": "^1.6.0", "unifont": "~0.5.0", "unist-util-visit": "^5.0.0", "unstorage": "^1.15.0", "vfile": "^6.0.3", "vite": "^6.3.4", "vitefu": "^1.0.6", "xxhash-wasm": "^1.1.0", "yargs-parser": "^21.1.1", "yocto-spinner": "^0.2.1", "zod": "^3.24.2", "zod-to-json-schema": "^3.24.5", "zod-to-ts": "^1.2.0" }, "optionalDependencies": { "sharp": "^0.33.3" }, "bin": { "astro": "astro.js" } }, "sha512-g/t54kVzQnFVijs+GbbbX/NBAFTl/3yNAEA/AQYq4FumLLVv7n4BIF+jKhcPGn9iFGyT1Cjvr7KB/qYyNvHEIg=="], - "astro/sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], + "astro/miniflare": ["miniflare@4.20250617.3", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "^5.28.5", "workerd": "1.20250617.0", "ws": "8.18.0", "youch": "3.3.4", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-j+LZycT11UdlVeNdaqD0XdNnYnqAL+wXmboz+tNPFgTq6zhD489Ujj3BfSDyEHDCA9UFBLbkc5ByGWBh+pYZ5Q=="], - "astro/shiki": ["shiki@3.6.0", "", { "dependencies": { "@shikijs/core": "3.6.0", "@shikijs/engine-javascript": "3.6.0", "@shikijs/engine-oniguruma": "3.6.0", "@shikijs/langs": "3.6.0", "@shikijs/themes": "3.6.0", "@shikijs/types": "3.6.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-tKn/Y0MGBTffQoklaATXmTqDU02zx8NYBGQ+F6gy87/YjKbizcLd+Cybh/0ZtOBX9r1NEnAy/GTRDKtOsc1L9w=="], + "astro/typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], "astro-project/@types/node": ["@types/node@22.15.32", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-3jigKqgSjsH6gYZv2nEsqdXfZqIFGAV36XYYjf9KGZ3PSG+IhLecqPnI310RvjutyMwifE2hhhNEklOUrvx/wA=="], + "astro-project/astro": ["astro@5.10.0", "", { "dependencies": { "@astrojs/compiler": "^2.12.2", "@astrojs/internal-helpers": "0.6.1", "@astrojs/markdown-remark": "6.3.2", "@astrojs/telemetry": "3.3.0", "@capsizecss/unpack": "^2.4.0", "@oslojs/encoding": "^1.1.0", "@rollup/pluginutils": "^5.1.4", "acorn": "^8.14.1", "aria-query": "^5.3.2", "axobject-query": "^4.1.0", "boxen": "8.0.1", "ci-info": "^4.2.0", "clsx": "^2.1.1", "common-ancestor-path": "^1.0.1", "cookie": "^1.0.2", "cssesc": "^3.0.0", "debug": "^4.4.0", "deterministic-object-hash": "^2.0.2", "devalue": "^5.1.1", "diff": "^5.2.0", "dlv": "^1.1.3", "dset": "^3.1.4", "es-module-lexer": "^1.6.0", "esbuild": "^0.25.0", "estree-walker": "^3.0.3", "flattie": "^1.1.1", "fontace": "~0.3.0", "github-slugger": "^2.0.0", "html-escaper": "3.0.3", "http-cache-semantics": "^4.1.1", "import-meta-resolve": "^4.1.0", "js-yaml": "^4.1.0", "kleur": "^4.1.5", "magic-string": "^0.30.17", "magicast": "^0.3.5", "mrmime": "^2.0.1", "neotraverse": "^0.6.18", "p-limit": "^6.2.0", "p-queue": "^8.1.0", "package-manager-detector": "^1.1.0", "picomatch": "^4.0.2", "prompts": "^2.4.2", "rehype": "^13.0.2", "semver": "^7.7.1", "shiki": "^3.2.1", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.12", "tsconfck": "^3.1.5", "ultrahtml": "^1.6.0", "unifont": "~0.5.0", "unist-util-visit": "^5.0.0", "unstorage": "^1.15.0", "vfile": "^6.0.3", "vite": "^6.3.4", "vitefu": "^1.0.6", "xxhash-wasm": "^1.1.0", "yargs-parser": "^21.1.1", "yocto-spinner": "^0.2.1", "zod": "^3.24.2", "zod-to-json-schema": "^3.24.5", "zod-to-ts": "^1.2.0" }, "optionalDependencies": { "sharp": "^0.33.3" }, "bin": { "astro": "astro.js" } }, "sha512-g/t54kVzQnFVijs+GbbbX/NBAFTl/3yNAEA/AQYq4FumLLVv7n4BIF+jKhcPGn9iFGyT1Cjvr7KB/qYyNvHEIg=="], + "base/define-property": ["define-property@1.0.0", "", { "dependencies": { "is-descriptor": "^1.0.0" } }, "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA=="], "body-parser/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], @@ -4320,10 +4502,22 @@ "cloudflare/@types/node": ["@types/node@18.19.112", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-i+Vukt9POdS/MBI7YrrkkI5fMfwFtOjphSmt4WXYLfwqsfr6z/HdCx7LqT9M7JktGob8WNgj8nFB4TbGNE4Cog=="], + "cloudflare-container/@types/node": ["@types/node@24.0.4", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-ulyqAkrhnuNq9pB76DRBTkcS6YsmDALy6Ua63V8OhrOBgbcYt6IOdzpw5P1+dyRIyMerzLkeYWBeOXPpA9GMAA=="], + + "cloudflare-container/typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], + "cloudflare-react-router/@types/node": ["@types/node@20.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-jJD50LtlD2dodAEO653i3YF04NWak6jN3ky+Ri3Em3mGR39/glWiboM/IePaRbgwSfqM1TpGXfAg8ohn/4dTgA=="], + "cloudflare-react-router/react-router": ["react-router@7.6.2", "", { "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" }, "optionalPeers": ["react-dom"] }, "sha512-U7Nv3y+bMimgWjhlT5CRdzHPu2/KVmqPwKUCChW8en5P3znxUqwlYFlbmyj8Rgp1SF6zs5X4+77kBVknkg6a0w=="], + + "cloudflare-react-router/typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], + + "cloudflare-sveltekit/typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], + "cloudflare-worker/braintrust": ["braintrust@0.0.201", "", { "dependencies": { "@ai-sdk/provider": "^1.0.1", "@braintrust/core": "0.0.86", "@next/env": "^14.2.3", "@vercel/functions": "^1.0.2", "ai": "^3.2.16", "argparse": "^2.0.1", "chalk": "^4.1.2", "cli-progress": "^3.12.0", "cors": "^2.8.5", "dotenv": "^16.4.5", "esbuild": "^0.25.3", "eventsource-parser": "^1.1.2", "express": "^4.21.2", "graceful-fs": "^4.2.11", "http-errors": "^2.0.0", "minimatch": "^9.0.3", "mustache": "^4.2.0", "pluralize": "^8.0.0", "simple-git": "^3.21.0", "slugify": "^1.6.6", "source-map": "^0.7.4", "uuid": "^9.0.1", "zod": "^3.22.4", "zod-to-json-schema": "^3.22.5" }, "bin": { "braintrust": "dist/cli.js" } }, "sha512-qH4esyOskiQ25OzbmlnPMVKEImDuFANEuYknNEsgS2f3M7t5SfbZxdelbYWcFPbUwQO3TR3XktszWcc3+oAZKg=="], + "cloudflare-worker-simple/typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], + "colorspace/color": ["color@3.2.1", "", { "dependencies": { "color-convert": "^1.9.3", "color-string": "^1.6.0" } }, "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA=="], "compress-commons/is-stream": ["is-stream@2.0.1", "", {}, "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg=="], @@ -4336,14 +4530,14 @@ "crc32-stream/readable-stream": ["readable-stream@4.7.0", "", { "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", "events": "^3.3.0", "process": "^0.11.10", "string_decoder": "^1.3.0" } }, "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg=="], - "cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], - "csso/css-tree": ["css-tree@2.2.1", "", { "dependencies": { "mdn-data": "2.0.28", "source-map-js": "^1.0.1" } }, "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA=="], "dax-sh/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], "degenerator/ast-types": ["ast-types@0.13.4", "", { "dependencies": { "tslib": "^2.0.1" } }, "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w=="], + "dofs/commander": ["commander@14.0.0", "", {}, "sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA=="], + "encoding-sniffer/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], "escodegen/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], @@ -4368,8 +4562,6 @@ "extend-shallow/is-extendable": ["is-extendable@1.0.1", "", { "dependencies": { "is-plain-object": "^2.0.4" } }, "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA=="], - "external-editor/tmp": ["tmp@0.0.33", "", { "dependencies": { "os-tmpdir": "~1.0.2" } }, "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw=="], - "externality/pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="], "extglob/is-extglob": ["is-extglob@1.0.0", "", {}, "sha512-7Q+VbVafe6x2T+Tu6NcOf6sRklazEPmBoB3IWk3WdGZM2iGUwU/Oe3Wtq5lSEkDTTlpp8yx+5t4pzO/i9Ty1ww=="], @@ -4420,8 +4612,6 @@ "listhen/pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="], - "log-update/ansi-escapes": ["ansi-escapes@7.0.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw=="], - "log-update/slice-ansi": ["slice-ansi@7.1.0", "", { "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" } }, "sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg=="], "log-update/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], @@ -4464,7 +4654,11 @@ "npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="], - "nuxt/chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], + "nuxt/nuxt": ["nuxt@3.17.5", "", { "dependencies": { "@nuxt/cli": "^3.25.1", "@nuxt/devalue": "^2.0.2", "@nuxt/devtools": "^2.4.1", "@nuxt/kit": "3.17.5", "@nuxt/schema": "3.17.5", "@nuxt/telemetry": "^2.6.6", "@nuxt/vite-builder": "3.17.5", "@unhead/vue": "^2.0.10", "@vue/shared": "^3.5.16", "c12": "^3.0.4", "chokidar": "^4.0.3", "compatx": "^0.2.0", "consola": "^3.4.2", "cookie-es": "^2.0.0", "defu": "^6.1.4", "destr": "^2.0.5", "devalue": "^5.1.1", "errx": "^0.1.0", "esbuild": "^0.25.5", "escape-string-regexp": "^5.0.0", "estree-walker": "^3.0.3", "exsolve": "^1.0.5", "h3": "^1.15.3", "hookable": "^5.5.3", "ignore": "^7.0.5", "impound": "^1.0.0", "jiti": "^2.4.2", "klona": "^2.0.6", "knitwork": "^1.2.0", "magic-string": "^0.30.17", "mlly": "^1.7.4", "mocked-exports": "^0.1.1", "nanotar": "^0.2.0", "nitropack": "^2.11.12", "nypm": "^0.6.0", "ofetch": "^1.4.1", "ohash": "^2.0.11", "on-change": "^5.0.1", "oxc-parser": "^0.72.2", "pathe": "^2.0.3", "perfect-debounce": "^1.0.0", "pkg-types": "^2.1.0", "radix3": "^1.1.2", "scule": "^1.3.0", "semver": "^7.7.2", "std-env": "^3.9.0", "strip-literal": "^3.0.0", "tinyglobby": "0.2.14", "ufo": "^1.6.1", "ultrahtml": "^1.6.0", "uncrypto": "^0.1.3", "unctx": "^2.4.1", "unimport": "^5.0.1", "unplugin": "^2.3.5", "unplugin-vue-router": "^0.12.0", "unstorage": "^1.16.0", "untyped": "^2.0.0", "vue": "^3.5.16", "vue-bundle-renderer": "^2.1.1", "vue-devtools-stub": "^0.1.0", "vue-router": "^4.5.1" }, "peerDependencies": { "@parcel/watcher": "^2.1.0", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "optionalPeers": ["@parcel/watcher", "@types/node"], "bin": { "nuxi": "bin/nuxt.mjs", "nuxt": "bin/nuxt.mjs" } }, "sha512-HWTWpM1/RDcCt9DlnzrPcNvUmGqc62IhlZJvr7COSfnJq2lKYiBKIIesEaOF+57Qjw7TfLPc1DQVBNtxfKBxEw=="], + + "nuxt/typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], + + "nuxt-pipeline/nuxt": ["nuxt@3.17.5", "", { "dependencies": { "@nuxt/cli": "^3.25.1", "@nuxt/devalue": "^2.0.2", "@nuxt/devtools": "^2.4.1", "@nuxt/kit": "3.17.5", "@nuxt/schema": "3.17.5", "@nuxt/telemetry": "^2.6.6", "@nuxt/vite-builder": "3.17.5", "@unhead/vue": "^2.0.10", "@vue/shared": "^3.5.16", "c12": "^3.0.4", "chokidar": "^4.0.3", "compatx": "^0.2.0", "consola": "^3.4.2", "cookie-es": "^2.0.0", "defu": "^6.1.4", "destr": "^2.0.5", "devalue": "^5.1.1", "errx": "^0.1.0", "esbuild": "^0.25.5", "escape-string-regexp": "^5.0.0", "estree-walker": "^3.0.3", "exsolve": "^1.0.5", "h3": "^1.15.3", "hookable": "^5.5.3", "ignore": "^7.0.5", "impound": "^1.0.0", "jiti": "^2.4.2", "klona": "^2.0.6", "knitwork": "^1.2.0", "magic-string": "^0.30.17", "mlly": "^1.7.4", "mocked-exports": "^0.1.1", "nanotar": "^0.2.0", "nitropack": "^2.11.12", "nypm": "^0.6.0", "ofetch": "^1.4.1", "ohash": "^2.0.11", "on-change": "^5.0.1", "oxc-parser": "^0.72.2", "pathe": "^2.0.3", "perfect-debounce": "^1.0.0", "pkg-types": "^2.1.0", "radix3": "^1.1.2", "scule": "^1.3.0", "semver": "^7.7.2", "std-env": "^3.9.0", "strip-literal": "^3.0.0", "tinyglobby": "0.2.14", "ufo": "^1.6.1", "ultrahtml": "^1.6.0", "uncrypto": "^0.1.3", "unctx": "^2.4.1", "unimport": "^5.0.1", "unplugin": "^2.3.5", "unplugin-vue-router": "^0.12.0", "unstorage": "^1.16.0", "untyped": "^2.0.0", "vue": "^3.5.16", "vue-bundle-renderer": "^2.1.1", "vue-devtools-stub": "^0.1.0", "vue-router": "^4.5.1" }, "peerDependencies": { "@parcel/watcher": "^2.1.0", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0" }, "optionalPeers": ["@parcel/watcher", "@types/node"], "bin": { "nuxi": "bin/nuxt.mjs", "nuxt": "bin/nuxt.mjs" } }, "sha512-HWTWpM1/RDcCt9DlnzrPcNvUmGqc62IhlZJvr7COSfnJq2lKYiBKIIesEaOF+57Qjw7TfLPc1DQVBNtxfKBxEw=="], "object-copy/define-property": ["define-property@0.2.5", "", { "dependencies": { "is-descriptor": "^0.1.0" } }, "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA=="], @@ -4490,6 +4684,8 @@ "precinct/commander": ["commander@12.1.0", "", {}, "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA=="], + "precinct/typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], + "prompts/kleur": ["kleur@3.0.3", "", {}, "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w=="], "proxy-agent/lru-cache": ["lru-cache@7.18.3", "", {}, "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="], @@ -4498,12 +4694,20 @@ "puppeteer-core/devtools-protocol": ["devtools-protocol@0.0.1312386", "", {}, "sha512-DPnhUXvmvKT2dFA/j7B+riVLUt9Q6RKJlcppojL5CoRywJJKLDYnRlw0gTFKfgDPHP5E04UoB71SxoJlVZy8FA=="], - "puppeteer-core/ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="], + "puppeteer-core/ws": ["ws@8.18.2", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ=="], "randomatic/is-number": ["is-number@4.0.0", "", {}, "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ=="], "randomatic/kind-of": ["kind-of@6.0.3", "", {}, "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw=="], + "react-router/@types/node": ["@types/node@20.19.1", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-jJD50LtlD2dodAEO653i3YF04NWak6jN3ky+Ri3Em3mGR39/glWiboM/IePaRbgwSfqM1TpGXfAg8ohn/4dTgA=="], + + "react-router/miniflare": ["miniflare@4.20250617.3", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "^5.28.5", "workerd": "1.20250617.0", "ws": "8.18.0", "youch": "3.3.4", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-j+LZycT11UdlVeNdaqD0XdNnYnqAL+wXmboz+tNPFgTq6zhD489Ujj3BfSDyEHDCA9UFBLbkc5ByGWBh+pYZ5Q=="], + + "react-router/react-router": ["react-router@7.6.2", "", { "dependencies": { "cookie": "^1.0.1", "set-cookie-parser": "^2.6.0" }, "peerDependencies": { "react": ">=18", "react-dom": ">=18" }, "optionalPeers": ["react-dom"] }, "sha512-U7Nv3y+bMimgWjhlT5CRdzHPu2/KVmqPwKUCChW8en5P3znxUqwlYFlbmyj8Rgp1SF6zs5X4+77kBVknkg6a0w=="], + + "react-router/typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], + "react-server-dom-webpack/react": ["react@19.2.0-canary-39cad7af-20250411", "", {}, "sha512-F6Iiuc7rXFtMZpCYM7rjJUSc8ee2xJV2+1s47xYR29U7e3Z5ztSplpEy559zeTEol1nHIxNTRvyFwVjmyEXnUA=="], "react-server-dom-webpack/react-dom": ["react-dom@19.2.0-canary-39cad7af-20250411", "", { "dependencies": { "scheduler": "0.27.0-canary-39cad7af-20250411" }, "peerDependencies": { "react": "19.2.0-canary-39cad7af-20250411" } }, "sha512-O9GNnsgW8BtodVQ8kQxUs3y6sy2LwzCLrVfMTgPWSCVmIMj7sb9Pgc9y4Tmzmzj/iaFeNNuZXbiLFsHvhslYww=="], @@ -4522,8 +4726,6 @@ "restore-cursor/onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], - "rollup/@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="], - "rollup-plugin-inject/estree-walker": ["estree-walker@0.6.1", "", {}, "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w=="], "rollup-plugin-inject/magic-string": ["magic-string@0.25.9", "", { "dependencies": { "sourcemap-codec": "^1.4.8" } }, "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ=="], @@ -4532,13 +4734,11 @@ "rollup-pluginutils/estree-walker": ["estree-walker@0.6.1", "", {}, "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w=="], - "rwsdk/eventsource-parser": ["eventsource-parser@3.0.2", "", {}, "sha512-6RxOBZ/cYgd8usLwsEl+EC09Au/9BcmCKYF2/xbml6DNczf7nv0MQb+7BA2F+li6//I+28VNlQR37XfQtcAJuA=="], - - "rwsdk/glob": ["glob@11.0.3", "", { "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", "minimatch": "^10.0.3", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA=="], + "rwsdk/@types/node": ["@types/node@22.15.32", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-3jigKqgSjsH6gYZv2nEsqdXfZqIFGAV36XYYjf9KGZ3PSG+IhLecqPnI310RvjutyMwifE2hhhNEklOUrvx/wA=="], - "rwsdk/react": ["react@19.2.0-canary-39cad7af-20250411", "", {}, "sha512-F6Iiuc7rXFtMZpCYM7rjJUSc8ee2xJV2+1s47xYR29U7e3Z5ztSplpEy559zeTEol1nHIxNTRvyFwVjmyEXnUA=="], + "rwsdk/rwsdk": ["rwsdk@0.0.89", "", { "dependencies": { "@cloudflare/vite-plugin": "0.0.0-1bae8618b", "@cloudflare/workers-types": "^4.20250407.0", "@puppeteer/browsers": "^2.8.0", "@types/fs-extra": "^11.0.4", "@types/react": "^19.1.2", "@types/react-dom": "^19.1.2", "@types/react-is": "^19.0.0", "@vitejs/plugin-react": "^4.3.4", "debug": "^4.4.0", "enhanced-resolve": "^5.18.1", "eventsource-parser": "^3.0.0", "execa": "^9.5.2", "fs-extra": "^11.3.0", "glob": "^11.0.1", "ignore": "^7.0.4", "jsonc-parser": "^3.3.1", "lodash": "^4.17.21", "magic-string": "^0.30.17", "miniflare": "^4.20250405.0", "picocolors": "^1.1.1", "puppeteer-core": "^22.8.1", "react": "19.2.0-canary-39cad7af-20250411", "react-dom": "19.2.0-canary-39cad7af-20250411", "react-is": "^19.0.0", "react-server-dom-webpack": "19.2.0-canary-39cad7af-20250411", "rsc-html-stream": "^0.0.6", "tmp-promise": "^3.0.3", "ts-morph": "^25.0.1", "unique-names-generator": "^4.7.1", "vibe-rules": "^0.2.31", "vite-tsconfig-paths": "^5.1.4", "wrangler": "^4.16.0" }, "peerDependencies": { "vite": "^6.2.6" }, "bin": { "rw-scripts": "bin/rw-scripts.mjs" } }, "sha512-xmkgfYgu+Z/H+uQ9TZhsxMdoB8eFetvJpi+kGAjc+RNuXom2IZDEa8Y+gnVyYf6zbErsxCpjmFyib2W92m3ddA=="], - "rwsdk/react-dom": ["react-dom@19.2.0-canary-39cad7af-20250411", "", { "dependencies": { "scheduler": "0.27.0-canary-39cad7af-20250411" }, "peerDependencies": { "react": "19.2.0-canary-39cad7af-20250411" } }, "sha512-O9GNnsgW8BtodVQ8kQxUs3y6sy2LwzCLrVfMTgPWSCVmIMj7sb9Pgc9y4Tmzmzj/iaFeNNuZXbiLFsHvhslYww=="], + "rwsdk/typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], "schema-utils/ajv": ["ajv@8.17.1", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g=="], @@ -4584,20 +4784,44 @@ "svelte-check/chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], + "sveltekit/typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], + "svgo/commander": ["commander@7.2.0", "", {}, "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw=="], "svgo/css-tree": ["css-tree@2.3.1", "", { "dependencies": { "mdn-data": "2.0.30", "source-map-js": "^1.0.1" } }, "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw=="], + "tanstack-start/@types/node": ["@types/node@22.15.32", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-3jigKqgSjsH6gYZv2nEsqdXfZqIFGAV36XYYjf9KGZ3PSG+IhLecqPnI310RvjutyMwifE2hhhNEklOUrvx/wA=="], + + "tanstack-start/miniflare": ["miniflare@4.20250617.3", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "^5.28.5", "workerd": "1.20250617.0", "ws": "8.18.0", "youch": "3.3.4", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-j+LZycT11UdlVeNdaqD0XdNnYnqAL+wXmboz+tNPFgTq6zhD489Ujj3BfSDyEHDCA9UFBLbkc5ByGWBh+pYZ5Q=="], + + "tanstack-start/typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], + + "tanstack-start-example-basic/@tanstack/react-router": ["@tanstack/react-router@1.116.0", "", { "dependencies": { "@tanstack/history": "1.115.0", "@tanstack/react-store": "^0.7.0", "@tanstack/router-core": "1.115.3", "jsesc": "^3.1.0", "tiny-invariant": "^1.3.3", "tiny-warning": "^1.0.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-ZBAg5Q6zJf0mnP9DYPiaaQ/wLDH2ujCMi/2RllpH86VUkdkyvQQzpAyKoiYJ891wh9OPgj6W6tPrzB4qy5FpRA=="], + + "tanstack-start-example-basic/@tanstack/react-router-devtools": ["@tanstack/react-router-devtools@1.116.0", "", { "dependencies": { "@tanstack/router-devtools-core": "^1.115.3", "solid-js": "^1.9.5" }, "peerDependencies": { "@tanstack/react-router": "^1.116.0", "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0" } }, "sha512-PsJZWPjcmwZGe71kUvH4bI1ozkv1FgBuBEE0hTYlTCSJ3uG+qv3ndGEI+AiFyuF5OStrbfg0otW1OxeNq5vdGQ=="], + + "tanstack-start-example-basic/@tanstack/react-start": ["@tanstack/react-start@1.116.1", "", { "dependencies": { "@tanstack/react-start-client": "^1.116.0", "@tanstack/react-start-config": "^1.116.1", "@tanstack/react-start-router-manifest": "^1.115.3", "@tanstack/react-start-server": "^1.116.0", "@tanstack/start-api-routes": "^1.115.3", "@tanstack/start-server-functions-client": "^1.115.3", "@tanstack/start-server-functions-handler": "^1.115.3", "@tanstack/start-server-functions-server": "^1.115.0", "@tanstack/start-server-functions-ssr": "^1.115.3" }, "peerDependencies": { "react": ">=18.0.0 || >=19.0.0", "react-dom": ">=18.0.0 || >=19.0.0", "vite": "^6.0.0" } }, "sha512-D8nf/OsFWzqYQefjxpVhpVh6sXnDCTzLOHmOJFDnrDOyRz15qAQUu5Vd85xDyF/FQR2nU8vX5sgjjrpceoS9nw=="], + "tanstack-start-example-basic/@types/node": ["@types/node@22.15.32", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-3jigKqgSjsH6gYZv2nEsqdXfZqIFGAV36XYYjf9KGZ3PSG+IhLecqPnI310RvjutyMwifE2hhhNEklOUrvx/wA=="], + "tanstack-start-example-basic/tailwind-merge": ["tailwind-merge@2.6.0", "", {}, "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA=="], + "tanstack-start-example-basic/tailwindcss": ["tailwindcss@3.4.17", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", "chokidar": "^3.6.0", "didyoumean": "^1.2.2", "dlv": "^1.1.3", "fast-glob": "^3.3.2", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", "jiti": "^1.21.6", "lilconfig": "^3.1.3", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", "picocolors": "^1.1.1", "postcss": "^8.4.47", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", "postcss-load-config": "^4.0.2", "postcss-nested": "^6.2.0", "postcss-selector-parser": "^6.1.2", "resolve": "^1.22.8", "sucrase": "^3.35.0" }, "bin": { "tailwind": "lib/cli.js", "tailwindcss": "lib/cli.js" } }, "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og=="], + "tanstack-start-example-basic/typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], + "tar/mkdirp": ["mkdirp@3.0.1", "", { "bin": { "mkdirp": "dist/cjs/src/bin.js" } }, "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg=="], "terser/acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], "terser/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + "trpc-cli/commander": ["commander@14.0.0", "", {}, "sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA=="], + + "typescript/miniflare": ["miniflare@4.20250617.3", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "^5.28.5", "workerd": "1.20250617.0", "ws": "8.18.0", "youch": "3.3.4", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-j+LZycT11UdlVeNdaqD0XdNnYnqAL+wXmboz+tNPFgTq6zhD489Ujj3BfSDyEHDCA9UFBLbkc5ByGWBh+pYZ5Q=="], + + "typescript/typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="], + "unctx/acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], "unicode-trie/pako": ["pako@0.2.9", "", {}, "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA=="], @@ -4644,6 +4868,8 @@ "vite-plugin-checker/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], + "vite-plugin-devtools-json/uuid": ["uuid@11.1.0", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A=="], + "vite-plugin-inspect/ansis": ["ansis@3.17.0", "", {}, "sha512-0qWUglt9JEqLFr3w1I1pbrChn1grhaiAR2ocX1PP/flRmxgtwTzPFFFnfIlD6aMOLQZgSuCRlidD70lvx8yhzg=="], "vite-project/@types/node": ["@types/node@22.15.32", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-3jigKqgSjsH6gYZv2nEsqdXfZqIFGAV36XYYjf9KGZ3PSG+IhLecqPnI310RvjutyMwifE2hhhNEklOUrvx/wA=="], @@ -4672,6 +4898,8 @@ "wrangler/esbuild": ["esbuild@0.25.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.4", "@esbuild/android-arm": "0.25.4", "@esbuild/android-arm64": "0.25.4", "@esbuild/android-x64": "0.25.4", "@esbuild/darwin-arm64": "0.25.4", "@esbuild/darwin-x64": "0.25.4", "@esbuild/freebsd-arm64": "0.25.4", "@esbuild/freebsd-x64": "0.25.4", "@esbuild/linux-arm": "0.25.4", "@esbuild/linux-arm64": "0.25.4", "@esbuild/linux-ia32": "0.25.4", "@esbuild/linux-loong64": "0.25.4", "@esbuild/linux-mips64el": "0.25.4", "@esbuild/linux-ppc64": "0.25.4", "@esbuild/linux-riscv64": "0.25.4", "@esbuild/linux-s390x": "0.25.4", "@esbuild/linux-x64": "0.25.4", "@esbuild/netbsd-arm64": "0.25.4", "@esbuild/netbsd-x64": "0.25.4", "@esbuild/openbsd-arm64": "0.25.4", "@esbuild/openbsd-x64": "0.25.4", "@esbuild/sunos-x64": "0.25.4", "@esbuild/win32-arm64": "0.25.4", "@esbuild/win32-ia32": "0.25.4", "@esbuild/win32-x64": "0.25.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q=="], + "wrangler/miniflare": ["miniflare@4.20250617.3", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "^5.28.5", "workerd": "1.20250617.0", "ws": "8.18.0", "youch": "3.3.4", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-j+LZycT11UdlVeNdaqD0XdNnYnqAL+wXmboz+tNPFgTq6zhD489Ujj3BfSDyEHDCA9UFBLbkc5ByGWBh+pYZ5Q=="], + "wrangler/unenv": ["unenv@2.0.0-rc.17", "", { "dependencies": { "defu": "^6.1.4", "exsolve": "^1.0.4", "ohash": "^2.0.11", "pathe": "^2.0.3", "ufo": "^1.6.1" } }, "sha512-B06u0wXkEd+o5gOCMl/ZHl5cfpYbDZKAT+HWTL+Hws6jWu7dCiqBBXXXzMFcFVJb8D4ytAnYmxJA83uwOQRSsg=="], "wrap-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], @@ -4712,11 +4940,11 @@ "@astrojs/check/chokidar/readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], - "@astrojs/markdown-remark/shiki/@shikijs/core": ["@shikijs/core@3.6.0", "", { "dependencies": { "@shikijs/types": "3.6.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-9By7Xb3olEX0o6UeJyPLI1PE1scC4d3wcVepvtv2xbuN9/IThYN4Wcwh24rcFeASzPam11MCq8yQpwwzCgSBRw=="], + "@astrojs/markdown-remark/shiki/@shikijs/core": ["@shikijs/core@3.7.0", "", { "dependencies": { "@shikijs/types": "3.7.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-yilc0S9HvTPyahHpcum8eonYrQtmGTU0lbtwxhA6jHv4Bm1cAdlPFRCJX4AHebkCm75aKTjjRAW+DezqD1b/cg=="], - "@astrojs/markdown-remark/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.6.0", "", { "dependencies": { "@shikijs/types": "3.6.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-7YnLhZG/TU05IHMG14QaLvTW/9WiK8SEYafceccHUSXs2Qr5vJibUwsDfXDLmRi0zHdzsxrGKpSX6hnqe0k8nA=="], + "@astrojs/markdown-remark/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.7.0", "", { "dependencies": { "@shikijs/types": "3.7.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-0t17s03Cbv+ZcUvv+y33GtX75WBLQELgNdVghnsdhTgU3hVcWcMsoP6Lb0nDTl95ZJfbP1mVMO0p3byVh3uuzA=="], - "@astrojs/markdown-remark/shiki/@shikijs/types": ["@shikijs/types@3.6.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-cLWFiToxYu0aAzJqhXTQsFiJRTFDAGl93IrMSBNaGSzs7ixkLfdG6pH11HipuWFGW5vyx4X47W8HDQ7eSrmBUg=="], + "@astrojs/markdown-remark/shiki/@shikijs/types": ["@shikijs/types@3.7.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-MGaLeaRlSWpnP0XSAum3kP3a8vtcTsITqoEPYdt3lQG3YCdQH4DnEhodkYcNMcU0uW0RffhoD1O3e0vG5eSBBg=="], "@aws-crypto/sha1-browser/@smithy/util-utf8/@smithy/util-buffer-from": ["@smithy/util-buffer-from@2.2.0", "", { "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" } }, "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA=="], @@ -4772,6 +5000,12 @@ "@cloudflare/puppeteer/debug/ms": ["ms@2.1.2", "", {}, "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="], + "@cloudflare/vite-plugin/miniflare/sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], + + "@cloudflare/vite-plugin/miniflare/zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="], + + "@deno/shim-deno/which/isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="], + "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.18.20", "", { "os": "android", "cpu": "arm" }, "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw=="], "@esbuild-kit/core-utils/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.18.20", "", { "os": "android", "cpu": "arm64" }, "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ=="], @@ -4848,10 +5082,6 @@ "@netlify/zip-it-and-ship-it/locate-path/p-locate": ["p-locate@6.0.0", "", { "dependencies": { "p-limit": "^4.0.0" } }, "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw=="], - "@npmcli/git/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], - - "@npmcli/promise-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], - "@nuxt/cli/chokidar/readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], "@nuxt/devtools-kit/execa/get-stream": ["get-stream@8.0.1", "", {}, "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA=="], @@ -4884,6 +5114,8 @@ "@nuxt/devtools/execa/strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="], + "@nuxt/devtools/which/isexe": ["isexe@3.1.1", "", {}, "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ=="], + "@octokit/plugin-paginate-rest/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="], "@octokit/plugin-rest-endpoint-methods/@octokit/types/@octokit/openapi-types": ["@octokit/openapi-types@24.2.0", "", {}, "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg=="], @@ -4892,15 +5124,17 @@ "@redwoodjs/starter-drizzle/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "@tanstack/directive-functions-plugin/@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], + "@redwoodjs/starter-drizzle/rwsdk/eventsource-parser": ["eventsource-parser@3.0.2", "", {}, "sha512-6RxOBZ/cYgd8usLwsEl+EC09Au/9BcmCKYF2/xbml6DNczf7nv0MQb+7BA2F+li6//I+28VNlQR37XfQtcAJuA=="], + + "@redwoodjs/starter-drizzle/rwsdk/glob": ["glob@11.0.3", "", { "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", "minimatch": "^10.0.3", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA=="], - "@tanstack/react-start-client/@tanstack/react-router/@tanstack/history": ["@tanstack/history@1.121.19", "", {}, "sha512-+fskAbUbLEksyJBELUYwmPr6ne+PLgY/aThT/tUsNyPGXo+qvd4M5i7Inryh4VFE6sU1s3fMjbd59ykxU+h5lA=="], + "@redwoodjs/starter-drizzle/rwsdk/miniflare": ["miniflare@4.20250617.3", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "^5.28.5", "workerd": "1.20250617.0", "ws": "8.18.0", "youch": "3.3.4", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-j+LZycT11UdlVeNdaqD0XdNnYnqAL+wXmboz+tNPFgTq6zhD489Ujj3BfSDyEHDCA9UFBLbkc5ByGWBh+pYZ5Q=="], - "@tanstack/react-start-client/@tanstack/router-core/@tanstack/history": ["@tanstack/history@1.121.19", "", {}, "sha512-+fskAbUbLEksyJBELUYwmPr6ne+PLgY/aThT/tUsNyPGXo+qvd4M5i7Inryh4VFE6sU1s3fMjbd59ykxU+h5lA=="], + "@redwoodjs/starter-drizzle/rwsdk/react": ["react@19.2.0-canary-39cad7af-20250411", "", {}, "sha512-F6Iiuc7rXFtMZpCYM7rjJUSc8ee2xJV2+1s47xYR29U7e3Z5ztSplpEy559zeTEol1nHIxNTRvyFwVjmyEXnUA=="], - "@tanstack/react-start-config/@tanstack/router-core/@tanstack/history": ["@tanstack/history@1.121.19", "", {}, "sha512-+fskAbUbLEksyJBELUYwmPr6ne+PLgY/aThT/tUsNyPGXo+qvd4M5i7Inryh4VFE6sU1s3fMjbd59ykxU+h5lA=="], + "@redwoodjs/starter-drizzle/rwsdk/react-dom": ["react-dom@19.2.0-canary-39cad7af-20250411", "", { "dependencies": { "scheduler": "0.27.0-canary-39cad7af-20250411" }, "peerDependencies": { "react": "19.2.0-canary-39cad7af-20250411" } }, "sha512-O9GNnsgW8BtodVQ8kQxUs3y6sy2LwzCLrVfMTgPWSCVmIMj7sb9Pgc9y4Tmzmzj/iaFeNNuZXbiLFsHvhslYww=="], - "@tanstack/react-start-router-manifest/@tanstack/router-core/@tanstack/history": ["@tanstack/history@1.121.19", "", {}, "sha512-+fskAbUbLEksyJBELUYwmPr6ne+PLgY/aThT/tUsNyPGXo+qvd4M5i7Inryh4VFE6sU1s3fMjbd59ykxU+h5lA=="], + "@tanstack/directive-functions-plugin/@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], "@tanstack/react-start-server/h3/cookie-es": ["cookie-es@1.2.2", "", {}, "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg=="], @@ -4908,12 +5142,6 @@ "@tanstack/react-start-server/h3/unenv": ["unenv@1.10.0", "", { "dependencies": { "consola": "^3.2.3", "defu": "^6.1.4", "mime": "^3.0.0", "node-fetch-native": "^1.6.4", "pathe": "^1.1.2" } }, "sha512-wY5bskBQFL9n3Eca5XnhH6KbUo/tfvkwm9OpcdCvLaeA7piBNbavbOKJySEwQ1V0RH6HvNlSAFRTpvTqgKRQXQ=="], - "@tanstack/router-devtools-core/@tanstack/router-core/@tanstack/history": ["@tanstack/history@1.121.19", "", {}, "sha512-+fskAbUbLEksyJBELUYwmPr6ne+PLgY/aThT/tUsNyPGXo+qvd4M5i7Inryh4VFE6sU1s3fMjbd59ykxU+h5lA=="], - - "@tanstack/router-generator/@tanstack/router-core/@tanstack/history": ["@tanstack/history@1.121.19", "", {}, "sha512-+fskAbUbLEksyJBELUYwmPr6ne+PLgY/aThT/tUsNyPGXo+qvd4M5i7Inryh4VFE6sU1s3fMjbd59ykxU+h5lA=="], - - "@tanstack/router-plugin/@tanstack/router-core/@tanstack/history": ["@tanstack/history@1.121.19", "", {}, "sha512-+fskAbUbLEksyJBELUYwmPr6ne+PLgY/aThT/tUsNyPGXo+qvd4M5i7Inryh4VFE6sU1s3fMjbd59ykxU+h5lA=="], - "@tanstack/router-plugin/chokidar/anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], "@tanstack/router-plugin/chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], @@ -4924,14 +5152,8 @@ "@tanstack/server-functions-plugin/@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], - "@tanstack/start-api-routes/@tanstack/router-core/@tanstack/history": ["@tanstack/history@1.121.19", "", {}, "sha512-+fskAbUbLEksyJBELUYwmPr6ne+PLgY/aThT/tUsNyPGXo+qvd4M5i7Inryh4VFE6sU1s3fMjbd59ykxU+h5lA=="], - - "@tanstack/start-client-core/@tanstack/router-core/@tanstack/history": ["@tanstack/history@1.121.19", "", {}, "sha512-+fskAbUbLEksyJBELUYwmPr6ne+PLgY/aThT/tUsNyPGXo+qvd4M5i7Inryh4VFE6sU1s3fMjbd59ykxU+h5lA=="], - "@tanstack/start-plugin-core/@babel/code-frame/js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], - "@tanstack/start-plugin-core/@tanstack/router-core/@tanstack/history": ["@tanstack/history@1.121.19", "", {}, "sha512-+fskAbUbLEksyJBELUYwmPr6ne+PLgY/aThT/tUsNyPGXo+qvd4M5i7Inryh4VFE6sU1s3fMjbd59ykxU+h5lA=="], - "@tanstack/start-plugin-core/h3/cookie-es": ["cookie-es@1.2.2", "", {}, "sha512-+W7VmiVINB+ywl1HGXJXmrqkOhpKrIiVZV6tQuV54ZyQC7MMuBt81Vc336GMLoHBq5hV/F9eXgt5Mnx0Rha5Fg=="], "@tanstack/start-plugin-core/h3/ohash": ["ohash@1.1.6", "", {}, "sha512-TBu7PtV8YkAZn0tSxobKY2n2aAQva936lhRrj6957aDaCf9IEtqsKbgMzXE/F/sjqYOwmrukeORHNLe5glk7Cg=="], @@ -4944,10 +5166,6 @@ "@tanstack/start-server-core/h3/unenv": ["unenv@1.10.0", "", { "dependencies": { "consola": "^3.2.3", "defu": "^6.1.4", "mime": "^3.0.0", "node-fetch-native": "^1.6.4", "pathe": "^1.1.2" } }, "sha512-wY5bskBQFL9n3Eca5XnhH6KbUo/tfvkwm9OpcdCvLaeA7piBNbavbOKJySEwQ1V0RH6HvNlSAFRTpvTqgKRQXQ=="], - "@tanstack/start-server-functions-fetcher/@tanstack/router-core/@tanstack/history": ["@tanstack/history@1.121.19", "", {}, "sha512-+fskAbUbLEksyJBELUYwmPr6ne+PLgY/aThT/tUsNyPGXo+qvd4M5i7Inryh4VFE6sU1s3fMjbd59ykxU+h5lA=="], - - "@tanstack/start-server-functions-handler/@tanstack/router-core/@tanstack/history": ["@tanstack/history@1.121.19", "", {}, "sha512-+fskAbUbLEksyJBELUYwmPr6ne+PLgY/aThT/tUsNyPGXo+qvd4M5i7Inryh4VFE6sU1s3fMjbd59ykxU+h5lA=="], - "ajv-formats/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], "ajv-keywords/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], @@ -4968,6 +5186,8 @@ "alchemy/@biomejs/biome/@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@1.9.4", "", { "os": "win32", "cpu": "x64" }, "sha512-8Y5wMhVIPaWe6jw2H+KlEm4wP/f7EW3810ZLmDlrEEy5KvBsb9ECEfu/kMWD484ijfQ8+nIi0giMgu9g1UAuuA=="], + "alchemy/alchemy/dofs": ["dofs@0.0.1", "", { "peerDependencies": { "hono": "^4.7.10" } }, "sha512-vNfYmLREQNQRE0R+ZiUuoW4VkxLZm/M6PHxAlTE5YDwrDIYdSTA0rfZ3Rgp7g2Tkvv5VI9xTt7pUtdMD46eM1Q=="], + "alchemy/braintrust/@braintrust/core": ["@braintrust/core@0.0.86", "", { "dependencies": { "@asteasolutions/zod-to-openapi": "^6.3.1", "uuid": "^9.0.1", "zod": "^3.22.4" } }, "sha512-kysTnKjizQl4EQGbLDK91Ao8UKMG111NgB+Stu88df9X2KHW5UpI63rShiucaVQt168fvr3UfRJbzfZ/t068bw=="], "alchemy/braintrust/ai": ["ai@3.4.33", "", { "dependencies": { "@ai-sdk/provider": "0.0.26", "@ai-sdk/provider-utils": "1.0.22", "@ai-sdk/react": "0.0.70", "@ai-sdk/solid": "0.0.54", "@ai-sdk/svelte": "0.0.57", "@ai-sdk/ui-utils": "0.0.50", "@ai-sdk/vue": "0.0.59", "@opentelemetry/api": "1.9.0", "eventsource-parser": "1.1.2", "json-schema": "^0.4.0", "jsondiffpatch": "0.6.0", "secure-json-parse": "^2.7.0", "zod-to-json-schema": "^3.23.3" }, "peerDependencies": { "openai": "^4.42.0", "react": "^18 || ^19 || ^19.0.0-rc", "sswr": "^2.1.0", "svelte": "^3.0.0 || ^4.0.0 || ^5.0.0", "zod": "^3.0.0" }, "optionalPeers": ["openai", "react", "sswr", "svelte", "zod"] }, "sha512-plBlrVZKwPoRTmM8+D1sJac9Bq8eaa2jiZlHLZIWekKWI1yMWYZvCCEezY9ASPwRhULYDJB2VhKOBUUeg3S5JQ=="], @@ -5004,49 +5224,27 @@ "astro-project/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "astro/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], - - "astro/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], - - "astro/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="], - - "astro/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="], - - "astro/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="], - - "astro/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="], - - "astro/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA=="], - - "astro/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="], - - "astro/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="], - - "astro/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="], - - "astro/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="], + "astro-project/astro/acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], - "astro/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="], + "astro-project/astro/diff": ["diff@5.2.0", "", {}, "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A=="], - "astro/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="], + "astro-project/astro/sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], - "astro/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="], + "astro-project/astro/shiki": ["shiki@3.7.0", "", { "dependencies": { "@shikijs/core": "3.7.0", "@shikijs/engine-javascript": "3.7.0", "@shikijs/engine-oniguruma": "3.7.0", "@shikijs/langs": "3.7.0", "@shikijs/themes": "3.7.0", "@shikijs/types": "3.7.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-ZcI4UT9n6N2pDuM2n3Jbk0sR4Swzq43nLPgS/4h0E3B/NrFn2HKElrDtceSf8Zx/OWYOo7G1SAtBLypCp+YXqg=="], - "astro/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="], + "astro/astro/acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], - "astro/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="], + "astro/astro/diff": ["diff@5.2.0", "", {}, "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A=="], - "astro/sharp/@img/sharp-wasm32": ["@img/sharp-wasm32@0.33.5", "", { "dependencies": { "@emnapi/runtime": "^1.2.0" }, "cpu": "none" }, "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg=="], + "astro/astro/sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], - "astro/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="], + "astro/astro/shiki": ["shiki@3.7.0", "", { "dependencies": { "@shikijs/core": "3.7.0", "@shikijs/engine-javascript": "3.7.0", "@shikijs/engine-oniguruma": "3.7.0", "@shikijs/langs": "3.7.0", "@shikijs/themes": "3.7.0", "@shikijs/types": "3.7.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-ZcI4UT9n6N2pDuM2n3Jbk0sR4Swzq43nLPgS/4h0E3B/NrFn2HKElrDtceSf8Zx/OWYOo7G1SAtBLypCp+YXqg=="], - "astro/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], + "astro/miniflare/sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], - "astro/shiki/@shikijs/core": ["@shikijs/core@3.6.0", "", { "dependencies": { "@shikijs/types": "3.6.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-9By7Xb3olEX0o6UeJyPLI1PE1scC4d3wcVepvtv2xbuN9/IThYN4Wcwh24rcFeASzPam11MCq8yQpwwzCgSBRw=="], + "astro/miniflare/ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="], - "astro/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.6.0", "", { "dependencies": { "@shikijs/types": "3.6.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-7YnLhZG/TU05IHMG14QaLvTW/9WiK8SEYafceccHUSXs2Qr5vJibUwsDfXDLmRi0zHdzsxrGKpSX6hnqe0k8nA=="], - - "astro/shiki/@shikijs/types": ["@shikijs/types@3.6.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-cLWFiToxYu0aAzJqhXTQsFiJRTFDAGl93IrMSBNaGSzs7ixkLfdG6pH11HipuWFGW5vyx4X47W8HDQ7eSrmBUg=="], + "astro/miniflare/zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="], "body-parser/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], @@ -5118,8 +5316,6 @@ "crc32-stream/readable-stream/string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="], - "cross-spawn/which/isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], - "csso/css-tree/mdn-data": ["mdn-data@2.0.28", "", {}, "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g=="], "eslint/minimatch/brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], @@ -5204,7 +5400,9 @@ "nitropack/serve-static/send": ["send@1.2.0", "", { "dependencies": { "debug": "^4.3.5", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.0", "mime-types": "^3.0.1", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.1" } }, "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw=="], - "nuxt/chokidar/readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], + "nuxt-pipeline/nuxt/chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], + + "nuxt/nuxt/chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], "object-copy/define-property/is-descriptor": ["is-descriptor@0.1.7", "", { "dependencies": { "is-accessor-descriptor": "^1.0.1", "is-data-descriptor": "^1.0.1" } }, "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg=="], @@ -5212,6 +5410,14 @@ "p-locate/p-limit/yocto-queue": ["yocto-queue@0.1.0", "", {}, "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q=="], + "react-router/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "react-router/miniflare/sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], + + "react-router/miniflare/ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="], + + "react-router/miniflare/zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="], + "react-server-dom-webpack/react-dom/scheduler": ["scheduler@0.27.0-canary-39cad7af-20250411", "", {}, "sha512-u+r6WyKk4zmirtBMZP8QZx0sTptiRxMMbGjf/3nOunE0mNnmkGFDcsG2UVDuOb5CyxvfbxgWE6W5JIyzTN4FXQ=="], "read-pkg/normalize-package-data/hosted-git-info": ["hosted-git-info@7.0.2", "", { "dependencies": { "lru-cache": "^10.0.1" } }, "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w=="], @@ -5232,13 +5438,19 @@ "rollup-plugin-visualizer/open/is-wsl": ["is-wsl@2.2.0", "", { "dependencies": { "is-docker": "^2.0.0" } }, "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww=="], - "rwsdk/glob/jackspeak": ["jackspeak@4.1.1", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" } }, "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ=="], + "rwsdk/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin": ["@cloudflare/vite-plugin@0.0.0-1bae8618b", "", { "dependencies": { "@cloudflare/unenv-preset": "2.3.2", "@mjackson/node-fetch-server": "^0.6.1", "@rollup/plugin-replace": "^6.0.1", "get-port": "^7.1.0", "miniflare": "0.0.0-1bae8618b", "picocolors": "^1.1.1", "tinyglobby": "^0.2.12", "unenv": "2.0.0-rc.17", "wrangler": "0.0.0-1bae8618b", "ws": "8.18.0" }, "peerDependencies": { "vite": "^6.1.0" } }, "sha512-VbPRyqmeLV2diKIEn8xTCVQYCFDlHD3OLbjJWH8YAb8alpiH9WaQs7T8EJubcxtn4FRUsxNgpolYFMV2NtbEEQ=="], - "rwsdk/glob/minimatch": ["minimatch@10.0.3", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw=="], + "rwsdk/rwsdk/eventsource-parser": ["eventsource-parser@3.0.2", "", {}, "sha512-6RxOBZ/cYgd8usLwsEl+EC09Au/9BcmCKYF2/xbml6DNczf7nv0MQb+7BA2F+li6//I+28VNlQR37XfQtcAJuA=="], - "rwsdk/glob/path-scurry": ["path-scurry@2.0.0", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg=="], + "rwsdk/rwsdk/glob": ["glob@11.0.3", "", { "dependencies": { "foreground-child": "^3.3.1", "jackspeak": "^4.1.1", "minimatch": "^10.0.3", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA=="], - "rwsdk/react-dom/scheduler": ["scheduler@0.27.0-canary-39cad7af-20250411", "", {}, "sha512-u+r6WyKk4zmirtBMZP8QZx0sTptiRxMMbGjf/3nOunE0mNnmkGFDcsG2UVDuOb5CyxvfbxgWE6W5JIyzTN4FXQ=="], + "rwsdk/rwsdk/miniflare": ["miniflare@4.20250617.3", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "^5.28.5", "workerd": "1.20250617.0", "ws": "8.18.0", "youch": "3.3.4", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-j+LZycT11UdlVeNdaqD0XdNnYnqAL+wXmboz+tNPFgTq6zhD489Ujj3BfSDyEHDCA9UFBLbkc5ByGWBh+pYZ5Q=="], + + "rwsdk/rwsdk/react": ["react@19.2.0-canary-39cad7af-20250411", "", {}, "sha512-F6Iiuc7rXFtMZpCYM7rjJUSc8ee2xJV2+1s47xYR29U7e3Z5ztSplpEy559zeTEol1nHIxNTRvyFwVjmyEXnUA=="], + + "rwsdk/rwsdk/react-dom": ["react-dom@19.2.0-canary-39cad7af-20250411", "", { "dependencies": { "scheduler": "0.27.0-canary-39cad7af-20250411" }, "peerDependencies": { "react": "19.2.0-canary-39cad7af-20250411" } }, "sha512-O9GNnsgW8BtodVQ8kQxUs3y6sy2LwzCLrVfMTgPWSCVmIMj7sb9Pgc9y4Tmzmzj/iaFeNNuZXbiLFsHvhslYww=="], "schema-utils/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], @@ -5254,17 +5466,37 @@ "svgo/css-tree/mdn-data": ["mdn-data@2.0.30", "", {}, "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA=="], + "tanstack-start-example-basic/@tanstack/react-router/@tanstack/history": ["@tanstack/history@1.115.0", "", {}, "sha512-K7JJNrRVvyjAVnbXOH2XLRhFXDkeP54Kt2P4FR1Kl2KDGlIbkua5VqZQD2rot3qaDrpufyUa63nuLai1kOLTsQ=="], + + "tanstack-start-example-basic/@tanstack/react-router/@tanstack/router-core": ["@tanstack/router-core@1.115.3", "", { "dependencies": { "@tanstack/history": "1.115.0", "@tanstack/store": "^0.7.0", "tiny-invariant": "^1.3.3" } }, "sha512-gynHs72LHVg05fuJTwZZYhDL4VNEAK0sXz7IqiBv7a3qsYeEmIZsGaFr9sVjTkuF1kbrFBdJd5JYutzBh9Uuhw=="], + + "tanstack-start-example-basic/@tanstack/react-router/jsesc": ["jsesc@3.1.0", "", { "bin": { "jsesc": "bin/jsesc" } }, "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA=="], + "tanstack-start-example-basic/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], "tanstack-start-example-basic/tailwindcss/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], "tanstack-start-example-basic/tailwindcss/jiti": ["jiti@1.21.7", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="], - "unplugin-vue-router/chokidar/readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], + "tanstack-start/@types/node/undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], - "unset-value/has-value/has-values": ["has-values@0.1.4", "", {}, "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ=="], + "tanstack-start/miniflare/sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], - "unset-value/has-value/isobject": ["isobject@2.1.0", "", { "dependencies": { "isarray": "1.0.0" } }, "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA=="], + "tanstack-start/miniflare/ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="], + + "tanstack-start/miniflare/zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="], + + "typescript/miniflare/sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], + + "typescript/miniflare/ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="], + + "typescript/miniflare/zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="], + + "unplugin-vue-router/chokidar/readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], + + "unset-value/has-value/has-values": ["has-values@0.1.4", "", {}, "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ=="], + + "unset-value/has-value/isobject": ["isobject@2.1.0", "", { "dependencies": { "isarray": "1.0.0" } }, "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA=="], "unstorage/anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], @@ -5412,6 +5644,12 @@ "wrangler/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.4", "", { "os": "win32", "cpu": "x64" }, "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ=="], + "wrangler/miniflare/sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], + + "wrangler/miniflare/ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="], + + "wrangler/miniflare/zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="], + "wrap-ansi/string-width/emoji-regex": ["emoji-regex@10.4.0", "", {}, "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw=="], "wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], @@ -5458,6 +5696,44 @@ "@cloudflare/puppeteer/@puppeteer/browsers/tar-fs/bare-path": ["bare-path@2.1.3", "", { "dependencies": { "bare-os": "^2.1.0" } }, "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA=="], + "@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], + + "@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], + + "@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="], + + "@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="], + + "@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="], + + "@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="], + + "@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA=="], + + "@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="], + + "@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="], + + "@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="], + + "@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="], + + "@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="], + + "@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="], + + "@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="], + + "@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="], + + "@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="], + + "@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-wasm32": ["@img/sharp-wasm32@0.33.5", "", { "dependencies": { "@emnapi/runtime": "^1.2.0" }, "cpu": "none" }, "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg=="], + + "@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="], + + "@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], + "@netlify/dev-utils/find-up/locate-path/p-locate": ["p-locate@6.0.0", "", { "dependencies": { "p-limit": "^4.0.0" } }, "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw=="], "@netlify/zip-it-and-ship-it/execa/npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="], @@ -5470,6 +5746,20 @@ "@nuxt/devtools/execa/npm-run-path/path-key": ["path-key@4.0.0", "", {}, "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ=="], + "@redwoodjs/starter-drizzle/rwsdk/glob/jackspeak": ["jackspeak@4.1.1", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" } }, "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ=="], + + "@redwoodjs/starter-drizzle/rwsdk/glob/minimatch": ["minimatch@10.0.3", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw=="], + + "@redwoodjs/starter-drizzle/rwsdk/glob/path-scurry": ["path-scurry@2.0.0", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg=="], + + "@redwoodjs/starter-drizzle/rwsdk/miniflare/sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], + + "@redwoodjs/starter-drizzle/rwsdk/miniflare/ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="], + + "@redwoodjs/starter-drizzle/rwsdk/miniflare/zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="], + + "@redwoodjs/starter-drizzle/rwsdk/react-dom/scheduler": ["scheduler@0.27.0-canary-39cad7af-20250411", "", {}, "sha512-u+r6WyKk4zmirtBMZP8QZx0sTptiRxMMbGjf/3nOunE0mNnmkGFDcsG2UVDuOb5CyxvfbxgWE6W5JIyzTN4FXQ=="], + "@tanstack/react-start-server/h3/unenv/pathe": ["pathe@1.1.2", "", {}, "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ=="], "@tanstack/router-plugin/chokidar/anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], @@ -5588,7 +5878,131 @@ "alchemy/wrangler/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20250408.0", "", { "os": "win32", "cpu": "x64" }, "sha512-nJ3RjMKGae2aF2rZ/CNeBvQPM+W5V1SUK0FYWG/uomyr7uQ2l4IayHna1ODg/OHHTEgIjwom0Mbn58iXb0WOcQ=="], - "astro/shiki/@shikijs/engine-javascript/oniguruma-to-es": ["oniguruma-to-es@4.3.3", "", { "dependencies": { "oniguruma-parser": "^0.12.1", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg=="], + "astro-project/astro/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], + + "astro-project/astro/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], + + "astro-project/astro/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="], + + "astro-project/astro/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="], + + "astro-project/astro/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="], + + "astro-project/astro/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="], + + "astro-project/astro/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA=="], + + "astro-project/astro/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="], + + "astro-project/astro/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="], + + "astro-project/astro/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="], + + "astro-project/astro/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="], + + "astro-project/astro/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="], + + "astro-project/astro/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="], + + "astro-project/astro/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="], + + "astro-project/astro/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="], + + "astro-project/astro/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="], + + "astro-project/astro/sharp/@img/sharp-wasm32": ["@img/sharp-wasm32@0.33.5", "", { "dependencies": { "@emnapi/runtime": "^1.2.0" }, "cpu": "none" }, "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg=="], + + "astro-project/astro/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="], + + "astro-project/astro/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], + + "astro-project/astro/shiki/@shikijs/core": ["@shikijs/core@3.7.0", "", { "dependencies": { "@shikijs/types": "3.7.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-yilc0S9HvTPyahHpcum8eonYrQtmGTU0lbtwxhA6jHv4Bm1cAdlPFRCJX4AHebkCm75aKTjjRAW+DezqD1b/cg=="], + + "astro-project/astro/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.7.0", "", { "dependencies": { "@shikijs/types": "3.7.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-0t17s03Cbv+ZcUvv+y33GtX75WBLQELgNdVghnsdhTgU3hVcWcMsoP6Lb0nDTl95ZJfbP1mVMO0p3byVh3uuzA=="], + + "astro-project/astro/shiki/@shikijs/types": ["@shikijs/types@3.7.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-MGaLeaRlSWpnP0XSAum3kP3a8vtcTsITqoEPYdt3lQG3YCdQH4DnEhodkYcNMcU0uW0RffhoD1O3e0vG5eSBBg=="], + + "astro/astro/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], + + "astro/astro/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], + + "astro/astro/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="], + + "astro/astro/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="], + + "astro/astro/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="], + + "astro/astro/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="], + + "astro/astro/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA=="], + + "astro/astro/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="], + + "astro/astro/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="], + + "astro/astro/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="], + + "astro/astro/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="], + + "astro/astro/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="], + + "astro/astro/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="], + + "astro/astro/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="], + + "astro/astro/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="], + + "astro/astro/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="], + + "astro/astro/sharp/@img/sharp-wasm32": ["@img/sharp-wasm32@0.33.5", "", { "dependencies": { "@emnapi/runtime": "^1.2.0" }, "cpu": "none" }, "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg=="], + + "astro/astro/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="], + + "astro/astro/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], + + "astro/astro/shiki/@shikijs/core": ["@shikijs/core@3.7.0", "", { "dependencies": { "@shikijs/types": "3.7.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-yilc0S9HvTPyahHpcum8eonYrQtmGTU0lbtwxhA6jHv4Bm1cAdlPFRCJX4AHebkCm75aKTjjRAW+DezqD1b/cg=="], + + "astro/astro/shiki/@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.7.0", "", { "dependencies": { "@shikijs/types": "3.7.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-0t17s03Cbv+ZcUvv+y33GtX75WBLQELgNdVghnsdhTgU3hVcWcMsoP6Lb0nDTl95ZJfbP1mVMO0p3byVh3uuzA=="], + + "astro/astro/shiki/@shikijs/types": ["@shikijs/types@3.7.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-MGaLeaRlSWpnP0XSAum3kP3a8vtcTsITqoEPYdt3lQG3YCdQH4DnEhodkYcNMcU0uW0RffhoD1O3e0vG5eSBBg=="], + + "astro/miniflare/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], + + "astro/miniflare/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], + + "astro/miniflare/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="], + + "astro/miniflare/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="], + + "astro/miniflare/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="], + + "astro/miniflare/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="], + + "astro/miniflare/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA=="], + + "astro/miniflare/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="], + + "astro/miniflare/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="], + + "astro/miniflare/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="], + + "astro/miniflare/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="], + + "astro/miniflare/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="], + + "astro/miniflare/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="], + + "astro/miniflare/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="], + + "astro/miniflare/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="], + + "astro/miniflare/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="], + + "astro/miniflare/sharp/@img/sharp-wasm32": ["@img/sharp-wasm32@0.33.5", "", { "dependencies": { "@emnapi/runtime": "^1.2.0" }, "cpu": "none" }, "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg=="], + + "astro/miniflare/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="], + + "astro/miniflare/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], "boxen/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], @@ -5634,6 +6048,48 @@ "nitropack/serve-static/send/mime-types": ["mime-types@3.0.1", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA=="], + "nuxt-pipeline/nuxt/chokidar/readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], + + "nuxt/nuxt/chokidar/readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], + + "react-router/miniflare/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], + + "react-router/miniflare/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], + + "react-router/miniflare/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="], + + "react-router/miniflare/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="], + + "react-router/miniflare/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="], + + "react-router/miniflare/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="], + + "react-router/miniflare/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA=="], + + "react-router/miniflare/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="], + + "react-router/miniflare/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="], + + "react-router/miniflare/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="], + + "react-router/miniflare/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="], + + "react-router/miniflare/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="], + + "react-router/miniflare/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="], + + "react-router/miniflare/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="], + + "react-router/miniflare/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="], + + "react-router/miniflare/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="], + + "react-router/miniflare/sharp/@img/sharp-wasm32": ["@img/sharp-wasm32@0.33.5", "", { "dependencies": { "@emnapi/runtime": "^1.2.0" }, "cpu": "none" }, "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg=="], + + "react-router/miniflare/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="], + + "react-router/miniflare/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], + "readdirp/micromatch/braces/extend-shallow": ["extend-shallow@2.0.1", "", { "dependencies": { "is-extendable": "^0.1.0" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="], "readdirp/micromatch/braces/fill-range": ["fill-range@4.0.0", "", { "dependencies": { "extend-shallow": "^2.0.1", "is-number": "^3.0.0", "repeat-string": "^1.6.1", "to-regex-range": "^2.1.0" } }, "sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ=="], @@ -5644,7 +6100,29 @@ "readdirp/micromatch/extglob/extend-shallow": ["extend-shallow@2.0.1", "", { "dependencies": { "is-extendable": "^0.1.0" } }, "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug=="], - "rwsdk/glob/path-scurry/lru-cache": ["lru-cache@11.1.0", "", {}, "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A=="], + "rwsdk/rwsdk/@cloudflare/vite-plugin/@cloudflare/unenv-preset": ["@cloudflare/unenv-preset@2.3.2", "", { "peerDependencies": { "unenv": "2.0.0-rc.17", "workerd": "^1.20250508.0" }, "optionalPeers": ["workerd"] }, "sha512-MtUgNl+QkQyhQvv5bbWP+BpBC1N0me4CHHuP2H4ktmOMKdB/6kkz/lo+zqiA4mEazb4y+1cwyNjVrQ2DWeE4mg=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/miniflare": ["miniflare@0.0.0-1bae8618b", "", { "dependencies": { "@cspotcode/source-map-support": "0.8.1", "acorn": "8.14.0", "acorn-walk": "8.3.2", "exit-hook": "2.2.1", "glob-to-regexp": "0.4.1", "sharp": "^0.33.5", "stoppable": "1.1.0", "undici": "^5.28.5", "workerd": "1.20250508.0", "ws": "8.18.0", "youch": "3.3.4", "zod": "3.22.3" }, "bin": { "miniflare": "bootstrap.js" } }, "sha512-FZnKE796uD3CNMIpKTzxHRwdqYIKm4jklpGWJvHJ93EFzjWnBAVdpbUb32pIQfLi2D20CFHAUO8Wv2N7JKdAIQ=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/unenv": ["unenv@2.0.0-rc.17", "", { "dependencies": { "defu": "^6.1.4", "exsolve": "^1.0.4", "ohash": "^2.0.11", "pathe": "^2.0.3", "ufo": "^1.6.1" } }, "sha512-B06u0wXkEd+o5gOCMl/ZHl5cfpYbDZKAT+HWTL+Hws6jWu7dCiqBBXXXzMFcFVJb8D4ytAnYmxJA83uwOQRSsg=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler": ["wrangler@0.0.0-1bae8618b", "", { "dependencies": { "@cloudflare/kv-asset-handler": "0.4.0", "@cloudflare/unenv-preset": "2.3.2", "blake3-wasm": "2.1.5", "esbuild": "0.25.4", "miniflare": "0.0.0-1bae8618b", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.17", "workerd": "1.20250508.0" }, "optionalDependencies": { "fsevents": "~2.3.2", "sharp": "^0.33.5" }, "peerDependencies": { "@cloudflare/workers-types": "^4.20250508.0" }, "optionalPeers": ["@cloudflare/workers-types"], "bin": { "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" } }, "sha512-bhoF0AzEgH135jr0jB6EHM275Zsgd0gPGkN+XPyRBPfl/JlkkfPVQRzDV3/X6yQ9/xFXPIiYFNzd7nkZNYcXvg=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="], + + "rwsdk/rwsdk/glob/jackspeak": ["jackspeak@4.1.1", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" } }, "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ=="], + + "rwsdk/rwsdk/glob/minimatch": ["minimatch@10.0.3", "", { "dependencies": { "@isaacs/brace-expansion": "^5.0.0" } }, "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw=="], + + "rwsdk/rwsdk/glob/path-scurry": ["path-scurry@2.0.0", "", { "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" } }, "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg=="], + + "rwsdk/rwsdk/miniflare/sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], + + "rwsdk/rwsdk/miniflare/ws": ["ws@8.18.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw=="], + + "rwsdk/rwsdk/miniflare/zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="], + + "rwsdk/rwsdk/react-dom/scheduler": ["scheduler@0.27.0-canary-39cad7af-20250411", "", {}, "sha512-u+r6WyKk4zmirtBMZP8QZx0sTptiRxMMbGjf/3nOunE0mNnmkGFDcsG2UVDuOb5CyxvfbxgWE6W5JIyzTN4FXQ=="], "tanstack-start-example-basic/tailwindcss/chokidar/anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], @@ -5654,6 +6132,82 @@ "tanstack-start-example-basic/tailwindcss/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="], + "tanstack-start/miniflare/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], + + "tanstack-start/miniflare/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], + + "tanstack-start/miniflare/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="], + + "tanstack-start/miniflare/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="], + + "tanstack-start/miniflare/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="], + + "tanstack-start/miniflare/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="], + + "tanstack-start/miniflare/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA=="], + + "tanstack-start/miniflare/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="], + + "tanstack-start/miniflare/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="], + + "tanstack-start/miniflare/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="], + + "tanstack-start/miniflare/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="], + + "tanstack-start/miniflare/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="], + + "tanstack-start/miniflare/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="], + + "tanstack-start/miniflare/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="], + + "tanstack-start/miniflare/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="], + + "tanstack-start/miniflare/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="], + + "tanstack-start/miniflare/sharp/@img/sharp-wasm32": ["@img/sharp-wasm32@0.33.5", "", { "dependencies": { "@emnapi/runtime": "^1.2.0" }, "cpu": "none" }, "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg=="], + + "tanstack-start/miniflare/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="], + + "tanstack-start/miniflare/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], + + "typescript/miniflare/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], + + "typescript/miniflare/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], + + "typescript/miniflare/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="], + + "typescript/miniflare/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="], + + "typescript/miniflare/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="], + + "typescript/miniflare/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="], + + "typescript/miniflare/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA=="], + + "typescript/miniflare/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="], + + "typescript/miniflare/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="], + + "typescript/miniflare/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="], + + "typescript/miniflare/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="], + + "typescript/miniflare/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="], + + "typescript/miniflare/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="], + + "typescript/miniflare/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="], + + "typescript/miniflare/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="], + + "typescript/miniflare/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="], + + "typescript/miniflare/sharp/@img/sharp-wasm32": ["@img/sharp-wasm32@0.33.5", "", { "dependencies": { "@emnapi/runtime": "^1.2.0" }, "cpu": "none" }, "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg=="], + + "typescript/miniflare/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="], + + "typescript/miniflare/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], + "vinxi/boxen/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], "vinxi/boxen/string-width/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="], @@ -5716,6 +6270,44 @@ "widest-line/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], + "wrangler/miniflare/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], + + "wrangler/miniflare/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], + + "wrangler/miniflare/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="], + + "wrangler/miniflare/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="], + + "wrangler/miniflare/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="], + + "wrangler/miniflare/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="], + + "wrangler/miniflare/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA=="], + + "wrangler/miniflare/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="], + + "wrangler/miniflare/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="], + + "wrangler/miniflare/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="], + + "wrangler/miniflare/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="], + + "wrangler/miniflare/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="], + + "wrangler/miniflare/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="], + + "wrangler/miniflare/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="], + + "wrangler/miniflare/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="], + + "wrangler/miniflare/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="], + + "wrangler/miniflare/sharp/@img/sharp-wasm32": ["@img/sharp-wasm32@0.33.5", "", { "dependencies": { "@emnapi/runtime": "^1.2.0" }, "cpu": "none" }, "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg=="], + + "wrangler/miniflare/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="], + + "wrangler/miniflare/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], + "xmlbuilder2/js-yaml/argparse/sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="], "yaml-language-server/vscode-languageserver/vscode-languageserver-protocol/vscode-jsonrpc": ["vscode-jsonrpc@6.0.0", "", {}, "sha512-wnJA4BnEjOSyFMvjZdpiOwhSq9uDoK8e/kpRJDTaMYzwlkrhG1fwDIZI94CLsLzlCK5cIbMMtFlJlfR57Lavmg=="], @@ -5728,6 +6320,50 @@ "@netlify/dev-utils/find-up/locate-path/p-locate/p-limit": ["p-limit@4.0.0", "", { "dependencies": { "yocto-queue": "^1.0.0" } }, "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ=="], + "@redwoodjs/starter-drizzle/rwsdk/glob/path-scurry/lru-cache": ["lru-cache@11.1.0", "", {}, "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A=="], + + "@redwoodjs/starter-drizzle/rwsdk/miniflare/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], + + "@redwoodjs/starter-drizzle/rwsdk/miniflare/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], + + "@redwoodjs/starter-drizzle/rwsdk/miniflare/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="], + + "@redwoodjs/starter-drizzle/rwsdk/miniflare/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="], + + "@redwoodjs/starter-drizzle/rwsdk/miniflare/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="], + + "@redwoodjs/starter-drizzle/rwsdk/miniflare/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="], + + "@redwoodjs/starter-drizzle/rwsdk/miniflare/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA=="], + + "@redwoodjs/starter-drizzle/rwsdk/miniflare/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="], + + "@redwoodjs/starter-drizzle/rwsdk/miniflare/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="], + + "@redwoodjs/starter-drizzle/rwsdk/miniflare/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="], + + "@redwoodjs/starter-drizzle/rwsdk/miniflare/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="], + + "@redwoodjs/starter-drizzle/rwsdk/miniflare/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="], + + "@redwoodjs/starter-drizzle/rwsdk/miniflare/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="], + + "@redwoodjs/starter-drizzle/rwsdk/miniflare/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="], + + "@redwoodjs/starter-drizzle/rwsdk/miniflare/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="], + + "@redwoodjs/starter-drizzle/rwsdk/miniflare/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="], + + "@redwoodjs/starter-drizzle/rwsdk/miniflare/sharp/@img/sharp-wasm32": ["@img/sharp-wasm32@0.33.5", "", { "dependencies": { "@emnapi/runtime": "^1.2.0" }, "cpu": "none" }, "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg=="], + + "@redwoodjs/starter-drizzle/rwsdk/miniflare/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="], + + "@redwoodjs/starter-drizzle/rwsdk/miniflare/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], + + "astro-project/astro/shiki/@shikijs/engine-javascript/oniguruma-to-es": ["oniguruma-to-es@4.3.3", "", { "dependencies": { "oniguruma-parser": "^0.12.1", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg=="], + + "astro/astro/shiki/@shikijs/engine-javascript/oniguruma-to-es": ["oniguruma-to-es@4.3.3", "", { "dependencies": { "oniguruma-parser": "^0.12.1", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg=="], + "changelogen/c12/chokidar/anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], "changelogen/c12/chokidar/is-binary-path/binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], @@ -5754,6 +6390,58 @@ "readdirp/micromatch/extglob/expand-brackets/define-property": ["define-property@0.2.5", "", { "dependencies": { "is-descriptor": "^0.1.0" } }, "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA=="], + "rwsdk/rwsdk/@cloudflare/vite-plugin/miniflare/sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/miniflare/workerd": ["workerd@1.20250508.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20250508.0", "@cloudflare/workerd-darwin-arm64": "1.20250508.0", "@cloudflare/workerd-linux-64": "1.20250508.0", "@cloudflare/workerd-linux-arm64": "1.20250508.0", "@cloudflare/workerd-windows-64": "1.20250508.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-ffLxe7dXSuGoA6jb3Qx2SClIV1aLHfJQ6RhGhzYHjQgv7dL6fdUOSIIGgzmu2mRKs+WFSujp6c8WgKquco6w3w=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/miniflare/zod": ["zod@3.22.3", "", {}, "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/esbuild": ["esbuild@0.25.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.4", "@esbuild/android-arm": "0.25.4", "@esbuild/android-arm64": "0.25.4", "@esbuild/android-x64": "0.25.4", "@esbuild/darwin-arm64": "0.25.4", "@esbuild/darwin-x64": "0.25.4", "@esbuild/freebsd-arm64": "0.25.4", "@esbuild/freebsd-x64": "0.25.4", "@esbuild/linux-arm": "0.25.4", "@esbuild/linux-arm64": "0.25.4", "@esbuild/linux-ia32": "0.25.4", "@esbuild/linux-loong64": "0.25.4", "@esbuild/linux-mips64el": "0.25.4", "@esbuild/linux-ppc64": "0.25.4", "@esbuild/linux-riscv64": "0.25.4", "@esbuild/linux-s390x": "0.25.4", "@esbuild/linux-x64": "0.25.4", "@esbuild/netbsd-arm64": "0.25.4", "@esbuild/netbsd-x64": "0.25.4", "@esbuild/openbsd-arm64": "0.25.4", "@esbuild/openbsd-x64": "0.25.4", "@esbuild/sunos-x64": "0.25.4", "@esbuild/win32-arm64": "0.25.4", "@esbuild/win32-ia32": "0.25.4", "@esbuild/win32-x64": "0.25.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8pgjLUcUjcgDg+2Q4NYXnPbo/vncAY4UmyaCm0jZevERqCHZIaWwdJHkf8XQtu4AxSKCdvrUbT0XUr1IdZzI8Q=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/sharp": ["sharp@0.33.5", "", { "dependencies": { "color": "^4.2.3", "detect-libc": "^2.0.3", "semver": "^7.6.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.33.5", "@img/sharp-darwin-x64": "0.33.5", "@img/sharp-libvips-darwin-arm64": "1.0.4", "@img/sharp-libvips-darwin-x64": "1.0.4", "@img/sharp-libvips-linux-arm": "1.0.5", "@img/sharp-libvips-linux-arm64": "1.0.4", "@img/sharp-libvips-linux-s390x": "1.0.4", "@img/sharp-libvips-linux-x64": "1.0.4", "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", "@img/sharp-libvips-linuxmusl-x64": "1.0.4", "@img/sharp-linux-arm": "0.33.5", "@img/sharp-linux-arm64": "0.33.5", "@img/sharp-linux-s390x": "0.33.5", "@img/sharp-linux-x64": "0.33.5", "@img/sharp-linuxmusl-arm64": "0.33.5", "@img/sharp-linuxmusl-x64": "0.33.5", "@img/sharp-wasm32": "0.33.5", "@img/sharp-win32-ia32": "0.33.5", "@img/sharp-win32-x64": "0.33.5" } }, "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/workerd": ["workerd@1.20250508.0", "", { "optionalDependencies": { "@cloudflare/workerd-darwin-64": "1.20250508.0", "@cloudflare/workerd-darwin-arm64": "1.20250508.0", "@cloudflare/workerd-linux-64": "1.20250508.0", "@cloudflare/workerd-linux-arm64": "1.20250508.0", "@cloudflare/workerd-windows-64": "1.20250508.0" }, "bin": { "workerd": "bin/workerd" } }, "sha512-ffLxe7dXSuGoA6jb3Qx2SClIV1aLHfJQ6RhGhzYHjQgv7dL6fdUOSIIGgzmu2mRKs+WFSujp6c8WgKquco6w3w=="], + + "rwsdk/rwsdk/glob/path-scurry/lru-cache": ["lru-cache@11.1.0", "", {}, "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A=="], + + "rwsdk/rwsdk/miniflare/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], + + "rwsdk/rwsdk/miniflare/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], + + "rwsdk/rwsdk/miniflare/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="], + + "rwsdk/rwsdk/miniflare/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="], + + "rwsdk/rwsdk/miniflare/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="], + + "rwsdk/rwsdk/miniflare/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="], + + "rwsdk/rwsdk/miniflare/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA=="], + + "rwsdk/rwsdk/miniflare/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="], + + "rwsdk/rwsdk/miniflare/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="], + + "rwsdk/rwsdk/miniflare/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="], + + "rwsdk/rwsdk/miniflare/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="], + + "rwsdk/rwsdk/miniflare/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="], + + "rwsdk/rwsdk/miniflare/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="], + + "rwsdk/rwsdk/miniflare/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="], + + "rwsdk/rwsdk/miniflare/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="], + + "rwsdk/rwsdk/miniflare/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="], + + "rwsdk/rwsdk/miniflare/sharp/@img/sharp-wasm32": ["@img/sharp-wasm32@0.33.5", "", { "dependencies": { "@emnapi/runtime": "^1.2.0" }, "cpu": "none" }, "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg=="], + + "rwsdk/rwsdk/miniflare/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="], + + "rwsdk/rwsdk/miniflare/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], + "tanstack-start-example-basic/tailwindcss/chokidar/anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], "tanstack-start-example-basic/tailwindcss/chokidar/is-binary-path/binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], @@ -5771,5 +6459,151 @@ "readdirp/micromatch/extglob/expand-brackets/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], "readdirp/micromatch/extglob/expand-brackets/define-property/is-descriptor": ["is-descriptor@0.1.7", "", { "dependencies": { "is-accessor-descriptor": "^1.0.1", "is-data-descriptor": "^1.0.1" } }, "sha512-C3grZTvObeN1xud4cRWl366OMXZTj0+HGyk4hvfpx4ZHt1Pb60ANSXqCK7pdOTeUQpRzECBSTphqvD7U+l22Eg=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-wasm32": ["@img/sharp-wasm32@0.33.5", "", { "dependencies": { "@emnapi/runtime": "^1.2.0" }, "cpu": "none" }, "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/miniflare/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/miniflare/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20250508.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-9x09MrA9Y5RQs3zqWvWns8xHgM2pVNXWpeJ+3hQYu4PrwPFZXtTD6b/iMmOnlYKzINlREq1RGeEybMFyWEUlUg=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/miniflare/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20250508.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-0Ili+nE2LLRzYue/yPc1pepSyNNg6LxR3/ng/rlQzVQUxPXIXldHFkJ/ynsYwQnAcf6OxasSi/kbTm6yvDoSAQ=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/miniflare/workerd/@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20250508.0", "", { "os": "linux", "cpu": "x64" }, "sha512-5saVrZ3uVwYxvBa7BaonXjeqB6X0YF3ak05qvBaWcmZ3FNmnarMm2W8842cnbhnckDVBpB/iDo51Sy6Y7y1jcw=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/miniflare/workerd/@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20250508.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-muQe1pkxRi3eaq1Q417xvfGd2SlktbLTzNhT5Yftsx8OecWrYuB8i4ttR6Nr5ER06bfEj0FqQjqJJhcp6wLLUQ=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/miniflare/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20250508.0", "", { "os": "win32", "cpu": "x64" }, "sha512-EJj8iTWFMqjgvZUxxNvzK7frA1JMFi3y/9eDIdZPL/OaQh3cmk5Lai5DCXsKYUxfooMBZWYTp53zOLrvuJI8VQ=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.4", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1VCICWypeQKhVbE9oW/sJaAmjLxhVqacdkvPLEjwlttjfwENRSClS8EjBz0KzRyFSCPDIkuXW34Je/vk7zdB7Q=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.25.4", "", { "os": "android", "cpu": "arm" }, "sha512-QNdQEps7DfFwE3hXiU4BZeOV68HHzYwGd0Nthhd3uCkkEKK7/R6MTgM0P7H7FAs5pU/DIWsviMmEGxEoxIZ+ZQ=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/esbuild/@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.4", "", { "os": "android", "cpu": "arm64" }, "sha512-bBy69pgfhMGtCnwpC/x5QhfxAz/cBgQ9enbtwjf6V9lnPI/hMyT9iWpR1arm0l3kttTr4L0KSLpKmLp/ilKS9A=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/esbuild/@esbuild/android-x64": ["@esbuild/android-x64@0.25.4", "", { "os": "android", "cpu": "x64" }, "sha512-TVhdVtQIFuVpIIR282btcGC2oGQoSfZfmBdTip2anCaVYcqWlZXGcdcKIUklfX2wj0JklNYgz39OBqh2cqXvcQ=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/esbuild/@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Y1giCfM4nlHDWEfSckMzeWNdQS31BQGs9/rouw6Ub91tkK79aIMTH3q9xHvzH8d0wDru5Ci0kWB8b3up/nl16g=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/esbuild/@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-CJsry8ZGM5VFVeyUYB3cdKpd/H69PYez4eJh1W/t38vzutdjEjtP7hB6eLKBoOdxcAlCtEYHzQ/PJ/oU9I4u0A=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/esbuild/@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.4", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-yYq+39NlTRzU2XmoPW4l5Ifpl9fqSk0nAJYM/V/WUGPEFfek1epLHJIkTQM6bBs1swApjO5nWgvr843g6TjxuQ=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/esbuild/@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-0FgvOJ6UUMflsHSPLzdfDnnBBVoCDtBTVyn/MrWloUNvq/5SFmh13l3dvgRPkDihRxb77Y17MbqbCAa2strMQQ=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/esbuild/@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.4", "", { "os": "linux", "cpu": "arm" }, "sha512-kro4c0P85GMfFYqW4TWOpvmF8rFShbWGnrLqlzp4X1TNWjRY3JMYUfDCtOxPKOIY8B0WC8HN51hGP4I4hz4AaQ=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/esbuild/@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-+89UsQTfXdmjIvZS6nUnOOLoXnkUTB9hR5QAeLrQdzOSWZvNSAXAtcRDHWtqAUtAmv7ZM1WPOOeSxDzzzMogiQ=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/esbuild/@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.4", "", { "os": "linux", "cpu": "ia32" }, "sha512-yTEjoapy8UP3rv8dB0ip3AfMpRbyhSN3+hY8mo/i4QXFeDxmiYbEKp3ZRjBKcOP862Ua4b1PDfwlvbuwY7hIGQ=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/esbuild/@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.4", "", { "os": "linux", "cpu": "none" }, "sha512-NeqqYkrcGzFwi6CGRGNMOjWGGSYOpqwCjS9fvaUlX5s3zwOtn1qwg1s2iE2svBe4Q/YOG1q6875lcAoQK/F4VA=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/esbuild/@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.4", "", { "os": "linux", "cpu": "none" }, "sha512-IcvTlF9dtLrfL/M8WgNI/qJYBENP3ekgsHbYUIzEzq5XJzzVEV/fXY9WFPfEEXmu3ck2qJP8LG/p3Q8f7Zc2Xg=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/esbuild/@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-HOy0aLTJTVtoTeGZh4HSXaO6M95qu4k5lJcH4gxv56iaycfz1S8GO/5Jh6X4Y1YiI0h7cRyLi+HixMR+88swag=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/esbuild/@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.4", "", { "os": "linux", "cpu": "none" }, "sha512-i8JUDAufpz9jOzo4yIShCTcXzS07vEgWzyX3NH2G7LEFVgrLEhjwL3ajFE4fZI3I4ZgiM7JH3GQ7ReObROvSUA=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/esbuild/@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-jFnu+6UbLlzIjPQpWCNh5QtrcNfMLjgIavnwPQAfoGx4q17ocOU9MsQ2QVvFxwQoWpZT8DvTLooTvmOQXkO51g=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/esbuild/@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.4", "", { "os": "linux", "cpu": "x64" }, "sha512-6e0cvXwzOnVWJHq+mskP8DNSrKBr1bULBvnFLpc1KY+d+irZSgZ02TGse5FsafKS5jg2e4pbvK6TPXaF/A6+CA=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/esbuild/@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.4", "", { "os": "none", "cpu": "arm64" }, "sha512-vUnkBYxZW4hL/ie91hSqaSNjulOnYXE1VSLusnvHg2u3jewJBz3YzB9+oCw8DABeVqZGg94t9tyZFoHma8gWZQ=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/esbuild/@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.4", "", { "os": "none", "cpu": "x64" }, "sha512-XAg8pIQn5CzhOB8odIcAm42QsOfa98SBeKUdo4xa8OvX8LbMZqEtgeWE9P/Wxt7MlG2QqvjGths+nq48TrUiKw=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/esbuild/@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.4", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-Ct2WcFEANlFDtp1nVAXSNBPDxyU+j7+tId//iHXU2f/lN5AmO4zLyhDcpR5Cz1r08mVxzt3Jpyt4PmXQ1O6+7A=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/esbuild/@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.4", "", { "os": "openbsd", "cpu": "x64" }, "sha512-xAGGhyOQ9Otm1Xu8NT1ifGLnA6M3sJxZ6ixylb+vIUVzvvd6GOALpwQrYrtlPouMqd/vSbgehz6HaVk4+7Afhw=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/esbuild/@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.4", "", { "os": "sunos", "cpu": "x64" }, "sha512-Mw+tzy4pp6wZEK0+Lwr76pWLjrtjmJyUB23tHKqEDP74R3q95luY/bXqXZeYl4NYlvwOqoRKlInQialgCKy67Q=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/esbuild/@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-AVUP428VQTSddguz9dO9ngb+E5aScyg7nOeJDrF1HPYu555gmza3bDGMPhmVXL8svDSoqPCsCPjb265yG/kLKQ=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/esbuild/@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-i1sW+1i+oWvQzSgfRcxxG2k4I9n3O9NRqy8U+uugaT2Dy7kLO9Y7wI72haOahxceMX8hZAzgGou1FhndRldxRg=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.4", "", { "os": "win32", "cpu": "x64" }, "sha512-nOT2vZNw6hJ+z43oP1SPea/G/6AbN6X+bGNhNuq8NtRHy4wsMhw765IKLNmnjek7GvjWBYQ8Q5VBoYTFg9y1UQ=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/sharp/@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.0.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/sharp/@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.0.4" }, "os": "darwin", "cpu": "x64" }, "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/sharp/@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/sharp/@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/sharp/@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.0.5", "", { "os": "linux", "cpu": "arm" }, "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/sharp/@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/sharp/@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.0.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/sharp/@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/sharp/@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/sharp/@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/sharp/@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.0.5" }, "os": "linux", "cpu": "arm" }, "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/sharp/@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/sharp/@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.0.4" }, "os": "linux", "cpu": "s390x" }, "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/sharp/@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/sharp/@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" }, "os": "linux", "cpu": "arm64" }, "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/sharp/@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.33.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.0.4" }, "os": "linux", "cpu": "x64" }, "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/sharp/@img/sharp-wasm32": ["@img/sharp-wasm32@0.33.5", "", { "dependencies": { "@emnapi/runtime": "^1.2.0" }, "cpu": "none" }, "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/sharp/@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.33.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/sharp/@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.33.5", "", { "os": "win32", "cpu": "x64" }, "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/workerd/@cloudflare/workerd-darwin-64": ["@cloudflare/workerd-darwin-64@1.20250508.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-9x09MrA9Y5RQs3zqWvWns8xHgM2pVNXWpeJ+3hQYu4PrwPFZXtTD6b/iMmOnlYKzINlREq1RGeEybMFyWEUlUg=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/workerd/@cloudflare/workerd-darwin-arm64": ["@cloudflare/workerd-darwin-arm64@1.20250508.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-0Ili+nE2LLRzYue/yPc1pepSyNNg6LxR3/ng/rlQzVQUxPXIXldHFkJ/ynsYwQnAcf6OxasSi/kbTm6yvDoSAQ=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/workerd/@cloudflare/workerd-linux-64": ["@cloudflare/workerd-linux-64@1.20250508.0", "", { "os": "linux", "cpu": "x64" }, "sha512-5saVrZ3uVwYxvBa7BaonXjeqB6X0YF3ak05qvBaWcmZ3FNmnarMm2W8842cnbhnckDVBpB/iDo51Sy6Y7y1jcw=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/workerd/@cloudflare/workerd-linux-arm64": ["@cloudflare/workerd-linux-arm64@1.20250508.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-muQe1pkxRi3eaq1Q417xvfGd2SlktbLTzNhT5Yftsx8OecWrYuB8i4ttR6Nr5ER06bfEj0FqQjqJJhcp6wLLUQ=="], + + "rwsdk/rwsdk/@cloudflare/vite-plugin/wrangler/workerd/@cloudflare/workerd-windows-64": ["@cloudflare/workerd-windows-64@1.20250508.0", "", { "os": "win32", "cpu": "x64" }, "sha512-EJj8iTWFMqjgvZUxxNvzK7frA1JMFi3y/9eDIdZPL/OaQh3cmk5Lai5DCXsKYUxfooMBZWYTp53zOLrvuJI8VQ=="], } } diff --git a/examples/cloudflare-container/.gitignore b/examples/cloudflare-container/.gitignore new file mode 100644 index 000000000..3bca80fbd --- /dev/null +++ b/examples/cloudflare-container/.gitignore @@ -0,0 +1 @@ +.alchemy/ \ No newline at end of file diff --git a/examples/cloudflare-container/Dockerfile b/examples/cloudflare-container/Dockerfile new file mode 100644 index 000000000..ee4c4c942 --- /dev/null +++ b/examples/cloudflare-container/Dockerfile @@ -0,0 +1,23 @@ +# syntax=docker/dockerfile:1 + +FROM golang:1.24-alpine AS build + +# Set destination for COPY +WORKDIR /app + +# Download any Go modules +COPY container_src/go.mod ./ +RUN go mod download + +# Copy container source code +COPY container_src/*.go ./ + +# Build +RUN CGO_ENABLED=0 GOOS=linux go build -o /server + +FROM scratch +COPY --from=build /server /server +EXPOSE 8080 + +# Run +CMD ["/server"] \ No newline at end of file diff --git a/examples/cloudflare-container/alchemy.run.ts b/examples/cloudflare-container/alchemy.run.ts new file mode 100644 index 000000000..d78f29f4d --- /dev/null +++ b/examples/cloudflare-container/alchemy.run.ts @@ -0,0 +1,26 @@ +/// + +import alchemy from "alchemy"; +import { Container, Worker } from "alchemy/cloudflare"; +import type { MyContainer } from "./src/worker.ts"; + +const app = await alchemy("cloudflare-container"); + +const container = await Container("test-container", { + className: "MyContainer", + build: { + context: import.meta.dirname, + dockerfile: "Dockerfile", + }, +}); + +export const worker = await Worker("test-worker", { + entrypoint: "src/worker.ts", + bindings: { + MY_CONTAINER: container, + }, +}); + +console.log(worker.url); + +await app.finalize(); diff --git a/examples/cloudflare-container/container_src/go.mod b/examples/cloudflare-container/container_src/go.mod new file mode 100644 index 000000000..5745f8d65 --- /dev/null +++ b/examples/cloudflare-container/container_src/go.mod @@ -0,0 +1,3 @@ +module server + +go 1.24.3 \ No newline at end of file diff --git a/examples/cloudflare-container/container_src/main.go b/examples/cloudflare-container/container_src/main.go new file mode 100644 index 000000000..167181620 --- /dev/null +++ b/examples/cloudflare-container/container_src/main.go @@ -0,0 +1,26 @@ +package main + +import ( + "fmt" + "log" + "net/http" + "os" +) + +func handler(w http.ResponseWriter, r *http.Request) { + message := os.Getenv("MESSAGE") + instanceId := os.Getenv("CLOUDFLARE_DEPLOYMENT_ID") + + fmt.Fprintf(w, "Hi, I'm a container and this is my message: \"%s\", my instance ID is: %s", message, instanceId) +} + +func errorHandler(w http.ResponseWriter, r *http.Request) { + panic("This is a panic") +} + +func main() { + http.HandleFunc("/", handler) + http.HandleFunc("/container", handler) + http.HandleFunc("/error", errorHandler) + log.Fatal(http.ListenAndServe(":8080", nil)) +} \ No newline at end of file diff --git a/examples/cloudflare-container/package.json b/examples/cloudflare-container/package.json new file mode 100644 index 000000000..7a019b639 --- /dev/null +++ b/examples/cloudflare-container/package.json @@ -0,0 +1,20 @@ +{ + "name": "cloudflare-container", + "version": "0.0.0", + "description": "Alchemy Typescript Project", + "type": "module", + "scripts": { + "build": "tsc -b", + "deploy": "bun run ./alchemy.run.ts", + "destroy": "bun run ./alchemy.run.ts --destroy", + "watch": "bun --watch ./alchemy.run.ts", + "dev": "bun --watch ./alchemy.run.ts --dev" + }, + "devDependencies": { + "@cloudflare/workers-types": "latest", + "@types/node": "^24.0.1", + "alchemy": "workspace:*", + "hono": "^4.8.3", + "typescript": "^5.8.3" + } +} diff --git a/examples/cloudflare-container/src/worker.ts b/examples/cloudflare-container/src/worker.ts new file mode 100644 index 000000000..c55eb6eef --- /dev/null +++ b/examples/cloudflare-container/src/worker.ts @@ -0,0 +1,70 @@ +import { Container, getContainer, getRandom } from "@cloudflare/containers"; +import { Hono } from "hono"; + +export class MyContainer extends Container { + // Port the container listens on (default: 8080) + defaultPort = 8080; + // Time before container sleeps due to inactivity (default: 30s) + sleepAfter = "2m"; + // Environment variables passed to the container + envVars = { + MESSAGE: "I was passed in via the container class!", + }; + + // Optional lifecycle hooks + override onStart() { + console.log("Container successfully started"); + } + + override onStop() { + console.log("Container successfully shut down"); + } + + override onError(error: unknown) { + console.log("Container error:", error); + } +} + +// Create Hono app with proper typing for Cloudflare Workers +const app = new Hono<{ + Bindings: { MY_CONTAINER: DurableObjectNamespace }; +}>(); + +// Home route with available endpoints +app.get("/", (c) => { + return c.text( + "Available endpoints:\n" + + "GET /container/ - Start a container for each ID with a 2m timeout\n" + + "GET /lb - Load balance requests over multiple containers\n" + + "GET /error - Start a container that errors (demonstrates error handling)\n" + + "GET /singleton - Get a single specific container instance", + ); +}); + +// Route requests to a specific container using the container ID +app.get("/container/:id", async (c) => { + const id = c.req.param("id"); + const containerId = c.env.MY_CONTAINER.idFromName(`/container/${id}`); + const container = c.env.MY_CONTAINER.get(containerId); + return await container.fetch(c.req.raw); +}); + +// Demonstrate error handling - this route forces a panic in the container +app.get("/error", async (c) => { + const container = getContainer(c.env.MY_CONTAINER, "error-test"); + return await container.fetch(c.req.raw); +}); + +// Load balance requests across multiple containers +app.get("/lb", async (c) => { + const container = await getRandom(c.env.MY_CONTAINER, 3); + return await container.fetch(c.req.raw); +}); + +// Get a single container instance (singleton pattern) +app.get("/singleton", async (c) => { + const container = getContainer(c.env.MY_CONTAINER); + return await container.fetch(c.req.raw); +}); + +export default app; diff --git a/examples/cloudflare-container/tsconfig.json b/examples/cloudflare-container/tsconfig.json new file mode 100644 index 000000000..feea997a5 --- /dev/null +++ b/examples/cloudflare-container/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "allowImportingTsExtensions": true, + "rewriteRelativeImportExtensions": true, + "types": ["@cloudflare/workers-types", "@types/node"] + }, + "include": ["src/**/*", "types/**/*", "alchemy.run.ts"], + "references": [ + { + "path": "../../alchemy/tsconfig.json" + } + ] +} diff --git a/examples/cloudflare-container/types/env.d.ts b/examples/cloudflare-container/types/env.d.ts new file mode 100644 index 000000000..f371005c3 --- /dev/null +++ b/examples/cloudflare-container/types/env.d.ts @@ -0,0 +1,16 @@ +// This file infers types for the cloudflare:workers environment from your Alchemy Worker. +// @see https://alchemy.run/docs/concepts/bindings.html#type-safe-bindings + +import type { worker } from "../alchemy.run.ts"; + +export type CloudflareEnv = typeof worker.Env; + +declare global { + type Env = CloudflareEnv; +} + +declare module "cloudflare:workers" { + namespace Cloudflare { + export interface Env extends CloudflareEnv {} + } +} diff --git a/examples/cloudflare-nuxt-pipeline/package.json b/examples/cloudflare-nuxt-pipeline/package.json index 3042e9829..414e2e307 100644 --- a/examples/cloudflare-nuxt-pipeline/package.json +++ b/examples/cloudflare-nuxt-pipeline/package.json @@ -15,7 +15,8 @@ "@cloudflare/workers-types": "^4.20250421.0", "alchemy": "workspace:*", "cloudflare": "^4.2.0", - "nuxt": "^3.16.2", + "nuxt": "^3.17.5", + "vite": "^6.0.0", "vue": "^3.5.13", "vue-router": "^4.5.0" } diff --git a/examples/cloudflare-redwood/alchemy.run.ts b/examples/cloudflare-redwood/alchemy.run.ts index 01d71bc4b..851665746 100644 --- a/examples/cloudflare-redwood/alchemy.run.ts +++ b/examples/cloudflare-redwood/alchemy.run.ts @@ -12,11 +12,13 @@ const app = await alchemy("cloudflare-redwood", { const database = await D1Database(`cloudflare-redwood-db${BRANCH_PREFIX}`, { migrationsDir: "drizzle", + adopt: true, }); export const website = await Redwood( `cloudflare-redwood-website${BRANCH_PREFIX}`, { + adopt: true, bindings: { DB: database, }, diff --git a/examples/cloudflare-vite/vite.config.ts b/examples/cloudflare-vite/vite.config.ts index 464dc6cc4..8e1250ccc 100644 --- a/examples/cloudflare-vite/vite.config.ts +++ b/examples/cloudflare-vite/vite.config.ts @@ -4,5 +4,14 @@ import { defineConfig } from "vite"; // https://vite.dev/config/ export default defineConfig({ - plugins: [react(), cloudflare()], + plugins: [ + react(), + cloudflare({ + persistState: process.env.ALCHEMY_CLOUDFLARE_PERSIST_PATH + ? { + path: process.env.ALCHEMY_CLOUDFLARE_PERSIST_PATH, + } + : undefined, + }), + ], }); diff --git a/examples/cloudflare-worker-simple/.gitignore b/examples/cloudflare-worker-simple/.gitignore new file mode 100644 index 000000000..3bca80fbd --- /dev/null +++ b/examples/cloudflare-worker-simple/.gitignore @@ -0,0 +1 @@ +.alchemy/ \ No newline at end of file diff --git a/examples/cloudflare-worker-simple/alchemy.run.ts b/examples/cloudflare-worker-simple/alchemy.run.ts new file mode 100644 index 000000000..5fdabff25 --- /dev/null +++ b/examples/cloudflare-worker-simple/alchemy.run.ts @@ -0,0 +1,54 @@ +/// + +import alchemy from "alchemy"; +import { + D1Database, + DurableObjectNamespace, + KVNamespace, + R2Bucket, + Worker, +} from "alchemy/cloudflare"; + +const app = await alchemy("cloudflare-worker-simple"); + +const [d1, kv, r2] = await Promise.all([ + D1Database("my-d1", { adopt: true }), + KVNamespace("my-kv", { + adopt: true, + values: [ + { key: "test1", value: "test1" }, + { key: "test2", value: "test2" }, + ], + }), + R2Bucket("my-r2", { adopt: true }), +]); +const doNamespace = new DurableObjectNamespace("DO", { + className: "DO", + scriptName: "cloudflare-worker-simple", + sqlite: true, +}); +export const worker1 = await Worker("worker", { + name: "cloudflare-worker-simple", + entrypoint: "src/worker1.ts", + bindings: { + KV: kv, + D1: d1, + R2: r2, + DO: doNamespace, + }, + compatibilityFlags: ["nodejs_compat"], +}); +export const worker2 = await Worker("worker2", { + name: "cloudflare-worker-simple-2", + entrypoint: "src/worker2.ts", + bindings: { + WORKER: worker1, + DO: doNamespace, + }, + compatibilityFlags: ["nodejs_compat"], +}); + +console.log(`worker1.url: ${worker1.url}`); +console.log(`worker2.url: ${worker2.url}`); + +await app.finalize(); diff --git a/examples/cloudflare-worker-simple/package.json b/examples/cloudflare-worker-simple/package.json new file mode 100644 index 000000000..3dc43737e --- /dev/null +++ b/examples/cloudflare-worker-simple/package.json @@ -0,0 +1,19 @@ +{ + "name": "cloudflare-worker-simple", + "version": "0.0.0", + "description": "Alchemy Typescript Project", + "type": "module", + "scripts": { + "build": "tsc -b", + "deploy": "bun run ./alchemy.run.ts", + "destroy": "bun run ./alchemy.run.ts --destroy", + "watch": "bun --watch ./alchemy.run.ts", + "dev": "bun --watch ./alchemy.run.ts --dev" + }, + "devDependencies": { + "@cloudflare/workers-types": "latest", + "@types/node": "^24.0.1", + "alchemy": "workspace:*", + "typescript": "^5.8.3" + } +} diff --git a/examples/cloudflare-worker-simple/src/worker1.ts b/examples/cloudflare-worker-simple/src/worker1.ts new file mode 100644 index 000000000..a1ec75978 --- /dev/null +++ b/examples/cloudflare-worker-simple/src/worker1.ts @@ -0,0 +1,42 @@ +import { DurableObject } from "cloudflare:workers"; +import crypto from "node:crypto"; +import type { worker1 } from "../alchemy.run.ts"; + +export default { + async fetch( + request: Request, + env: typeof worker1.Env, + _ctx: ExecutionContext, + ): Promise { + const url = new URL(request.url); + switch (url.pathname) { + case "/": + return Response.json({ + d1: await env.D1.exec("SELECT 1"), + kv: await env.KV.list(), + r2: await env.R2.list(), + }); + case "/upload": { + await env.KV.put(crypto.randomUUID(), crypto.randomBytes(16)); + return Response.json({ success: true }); + } + case "/download": { + const file = await env.KV.get(url.searchParams.get("name") ?? ""); + if (!file) { + return Response.json({ error: "File not found" }, { status: 404 }); + } + return new Response(file); + } + default: + return Response.json({ error: "Not found" }, { status: 404 }); + } + }, +}; + +export class DO extends DurableObject { + override fetch(_: Request) { + return Response.json({ + message: "hello from DO", + }); + } +} diff --git a/examples/cloudflare-worker-simple/src/worker2.ts b/examples/cloudflare-worker-simple/src/worker2.ts new file mode 100644 index 000000000..35319d637 --- /dev/null +++ b/examples/cloudflare-worker-simple/src/worker2.ts @@ -0,0 +1,12 @@ +import { WorkerEntrypoint } from "cloudflare:workers"; +import type { worker2 } from "../alchemy.run.ts"; + +export default class extends WorkerEntrypoint { + async fetch(request: Request): Promise { + const stub = this.env.DO.get(this.env.DO.idFromName("DO")); + return await stub.fetch(request); + } + rpcMethod() { + return "hello world"; + } +} diff --git a/examples/cloudflare-worker-simple/tsconfig.json b/examples/cloudflare-worker-simple/tsconfig.json new file mode 100644 index 000000000..feea997a5 --- /dev/null +++ b/examples/cloudflare-worker-simple/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "allowImportingTsExtensions": true, + "rewriteRelativeImportExtensions": true, + "types": ["@cloudflare/workers-types", "@types/node"] + }, + "include": ["src/**/*", "types/**/*", "alchemy.run.ts"], + "references": [ + { + "path": "../../alchemy/tsconfig.json" + } + ] +} diff --git a/examples/cloudflare-worker-simple/types/env.d.ts b/examples/cloudflare-worker-simple/types/env.d.ts new file mode 100644 index 000000000..f371005c3 --- /dev/null +++ b/examples/cloudflare-worker-simple/types/env.d.ts @@ -0,0 +1,16 @@ +// This file infers types for the cloudflare:workers environment from your Alchemy Worker. +// @see https://alchemy.run/docs/concepts/bindings.html#type-safe-bindings + +import type { worker } from "../alchemy.run.ts"; + +export type CloudflareEnv = typeof worker.Env; + +declare global { + type Env = CloudflareEnv; +} + +declare module "cloudflare:workers" { + namespace Cloudflare { + export interface Env extends CloudflareEnv {} + } +} diff --git a/examples/docker/.env b/examples/docker/.env new file mode 100644 index 000000000..c8c2d3175 --- /dev/null +++ b/examples/docker/.env @@ -0,0 +1,4 @@ +mongoHost=mongodb://mongo:27017 +database=cart +nodeEnvironment=development +protocol=http:// \ No newline at end of file diff --git a/examples/docker/.gitignore b/examples/docker/.gitignore new file mode 100644 index 000000000..8e0776e8d --- /dev/null +++ b/examples/docker/.gitignore @@ -0,0 +1 @@ +!.env diff --git a/examples/docker/README.md b/examples/docker/README.md new file mode 100644 index 000000000..f0010695c --- /dev/null +++ b/examples/docker/README.md @@ -0,0 +1,76 @@ +# Alchemy Docker Provider Example + +This example demonstrates how to use the Alchemy Docker provider to manage Docker resources declaratively. It follows the [Pulumi Fundamentals tutorial](https://www.pulumi.com/tutorials/pulumi-fundamentals/) for Docker, setting up a three-tier web application with a frontend, backend, and MongoDB database. + +## Overview + +This example: + +1. Creates a Docker network for all services +2. Pulls the necessary container images (frontend, backend, MongoDB) +3. Deploys a MongoDB container +4. Deploys a backend API container +5. Deploys a frontend container +6. Connects all containers to the network + +## Prerequisites + +- Docker installed and running on your machine +- Alchemy installed + +## Configuration + +This example uses environment variables to configure the application. The variables are defined in the `.env` file at the root of the example: + +``` +mongoHost=mongodb://mongo:27017 +database=cart +nodeEnvironment=development +protocol=http:// +``` + +You can modify these values to customize the application behavior. + +## Running the Example + +1. Navigate to the example directory: + ```bash + cd examples/docker + ``` + +2. Run the example with Alchemy: + ```bash + bun run deploy + ``` + +3. Once deployed, you can access the application at http://localhost:3000 + +## Application Structure + +- `alchemy.run.ts` - Main Alchemy configuration file that defines the Docker resources +- `app/` - Sample Node.js application + - `index.js` - Express.js application with Redis integration + - `package.json` - Node.js dependencies + - `Dockerfile` - Docker image definition + +## How It Works + +The example demonstrates key Alchemy concepts: + +1. **Resource Dependencies**: The frontend, backend, and MongoDB containers are deployed in an order that maintains their dependencies. Environment variables are used to connect the containers (like setting `DATABASE_HOST` and `HTTP_PROXY`). + +2. **Container Images**: Shows how to pull and use Docker images with the `Image` resource. + +3. **Networking**: Creates a Docker network and connects all containers to it, allowing inter-container communication. + +4. **Stack-Based Resources**: Uses the Alchemy stack name to create unique resource identifiers, enabling multi-environment deployments. + +## Cleaning Up + +To destroy all resources created by this example, run: + +```bash +bun run destroy +``` + +This will stop and remove all containers and networks created by the example. diff --git a/examples/docker/alchemy.run.ts b/examples/docker/alchemy.run.ts new file mode 100644 index 000000000..db2a933d7 --- /dev/null +++ b/examples/docker/alchemy.run.ts @@ -0,0 +1,95 @@ +import alchemy from "alchemy"; +import * as docker from "alchemy/docker"; + +// Initialize Alchemy +const app = await alchemy("docker", { + // Determine the phase based on command line arguments + phase: process.argv[2] === "destroy" ? "destroy" : "up", + stage: process.argv[3], + quiet: process.argv.includes("--quiet"), +}); + +// Get configuration values (matching the provided Pulumi config) +const frontendPort = 3001; +const backendPort = 3000; +const mongoPort = 27017; +const mongoHost = process.env.mongoHost!; +const database = process.env.database!; +const nodeEnvironment = process.env.nodeEnvironment!; +const protocol = process.env.protocol!; + +const stack = app.stage || "dev"; + +// Create a Docker network +const network = await docker.Network("network", { + name: `services-${stack}`, + driver: "bridge", +}); + +// Pull the images in parallel +const [backend, frontend, mongoImage] = await Promise.all([ + docker.RemoteImage("backendImage", { + name: "pulumi/tutorial-pulumi-fundamentals-backend", + tag: "latest", + }), + docker.RemoteImage("frontendImage", { + name: "pulumi/tutorial-pulumi-fundamentals-frontend", + tag: "latest", + }), + docker.RemoteImage("mongoImage", { + name: "pulumi/tutorial-pulumi-fundamentals-database", + tag: "latest", + }), +]); + +// Create the MongoDB container +const mongoContainer = await docker.Container("mongoContainer", { + image: mongoImage, + name: `mongo-${stack}`, + ports: [{ external: mongoPort, internal: mongoPort }], + networks: [ + { + name: network.name, + aliases: ["mongo"], + }, + ], + restart: "always", + start: true, +}); + +// Create the backend container +const backendContainer = await docker.Container("backendContainer", { + image: backend, + name: `backend-${stack}`, + ports: [{ external: backendPort, internal: backendPort }], + environment: { + DATABASE_HOST: mongoHost, + DATABASE_NAME: database, + NODE_ENV: nodeEnvironment, + }, + networks: [network], + restart: "always", + start: true, +}); + +// Create the frontend container +const frontendContainer = await docker.Container("frontendContainer", { + image: frontend, + name: `frontend-${stack}`, + ports: [{ external: frontendPort, internal: frontendPort }], + environment: { + PORT: frontendPort.toString(), + HTTP_PROXY: `${backendContainer.name}:${backendPort}`, + PROXY_PROTOCOL: protocol, + }, + networks: [network], + restart: "always", + start: true, +}); + +await app.finalize(); + +// Export relevant information +export { backendContainer, frontendContainer, mongoContainer, network }; +export const frontendUrl = `http://localhost:${frontendPort}`; +export const backendUrl = `http://localhost:${backendPort}`; diff --git a/examples/docker/package.json b/examples/docker/package.json new file mode 100644 index 000000000..56e08f347 --- /dev/null +++ b/examples/docker/package.json @@ -0,0 +1,10 @@ +{ + "name": "docker", + "version": "0.0.0", + "private": true, + "type": "module", + "scripts": { + "deploy": "bun run --env-file .env ./alchemy.run.ts", + "destroy": "bun run --env-file .env ./alchemy.run.ts destroy" + } +} diff --git a/examples/docker/tsconfig.json b/examples/docker/tsconfig.json new file mode 100644 index 000000000..50076aa76 --- /dev/null +++ b/examples/docker/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.base.json", + "include": ["src/**/*.ts", "alchemy.run.ts"], + "compilerOptions": { + "composite": true, + "resolveJsonModule": true + }, + "references": [{ "path": "../../alchemy/tsconfig.json" }] +} diff --git a/package.json b/package.json index ae21805d9..ccad10935 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "url": "https://github.com/sam-goodwin/alchemy" }, "scripts": { - "build": "bun run --filter cloudflare-react-router types && cd ./alchemy && tsc -b && tsc -b ./tsconfig.json && bun run --filter alchemy build:cli", + "build": "cd ./alchemy && bunx tsc -b && bun run --filter alchemy build", "check": "bun run build && biome check", "fix": "biome check --write", "deploy:repo": "bun ./stacks/src/repo.run.ts", @@ -24,8 +24,9 @@ "prepare": "husky" }, "workspaces": [ - "alchemy", "alchemy-web", + "alchemy", + "alchemy/templates/*", "examples/*", "stacks" ], @@ -45,6 +46,7 @@ "lint-staged": "^15.3.0", "openai": "^4.103.0", "typescript": "latest", + "vite": "^6.3.5", "vitest": "^3.1.4", "yaml": "^2.7.1" }, diff --git a/stacks/src/repo.run.ts b/stacks/src/repo.run.ts index e00729d40..879da4d84 100644 --- a/stacks/src/repo.run.ts +++ b/stacks/src/repo.run.ts @@ -1,8 +1,8 @@ // ensure providers are registered (for deletion purposes) -import "../alchemy/src/aws/index.ts"; -import "../alchemy/src/aws/oidc/index.ts"; -import "../alchemy/src/cloudflare/index.ts"; -import "../alchemy/src/os/index.ts"; +import "alchemy/aws"; +import "alchemy/aws/oidc"; +import "alchemy/cloudflare"; +import "alchemy/os"; import alchemy from "alchemy"; import { AccountId, Role, SSMParameter } from "alchemy/aws"; @@ -53,6 +53,7 @@ const githubRole = await Role("github-oidc-role", { const stateStore = await R2Bucket("state-store", { name: "alchemy-state-store", + adopt: true, }); const testEnvironment = await RepositoryEnvironment("test environment", {