diff --git a/.changeset/config.json b/.changeset/config.json index cc6eee0cf..3fbd70973 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -18,6 +18,7 @@ "@workflow/example-nitro-v2", "@workflow/example-nuxt", "@workflow/example-vite", - "@workflow/example-sveltekit" + "@workflow/example-sveltekit", + "@workflow/example-astro" ] } diff --git a/.changeset/grumpy-ties-sort.md b/.changeset/grumpy-ties-sort.md new file mode 100644 index 000000000..97cc879fc --- /dev/null +++ b/.changeset/grumpy-ties-sort.md @@ -0,0 +1,7 @@ +--- +"@workflow/builders": patch +"workflow": patch +"@workflow/astro": patch +--- + +Add @workflow/astro package diff --git a/.changeset/pre.json b/.changeset/pre.json index 12cdbf00b..950bcb6eb 100644 --- a/.changeset/pre.json +++ b/.changeset/pre.json @@ -31,6 +31,7 @@ "@workflow/example-nuxt": "0.0.0", "@workflow/example-vite": "0.0.0", "@workflow/example-sveltekit": "0.0.0", + "@workflow/example-astro": "0.0.0", "@workflow/builders": "4.0.1-beta.3", "@workflow/sveltekit": "4.0.0-beta.1", "@workflow/nuxt": "4.0.1-beta.6" diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 7bd31d34f..2b66cb9a0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -75,6 +75,8 @@ jobs: project-id: "prj_p0GIEsfl53L7IwVbosPvi9rPSOYW" - name: "express" project-id: "prj_cCZjpBy92VRbKHHbarDMhOHtkuIr" + - name: "astro" + project-id: "prj_YDAXj3K8LM0hgejuIMhioz2yLgTI" env: TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }} TURBO_TEAM: ${{ vars.TURBO_TEAM }} @@ -191,7 +193,7 @@ jobs: run: cd workbench/${{ matrix.app.name }} && pnpm dev & echo "starting tests in 10 seconds" && sleep 10 && pnpm vitest run packages/core/e2e/dev.test.ts && sleep 10 && pnpm run test:e2e env: APP_NAME: ${{ matrix.app.name }} - DEPLOYMENT_URL: "http://localhost:${{ matrix.app.name == 'sveltekit' && '5173' || '3000' }}" + DEPLOYMENT_URL: "http://localhost:${{ matrix.app.name == 'sveltekit' && '5173' || (matrix.app.name == 'astro' && '4321' || '3000') }}" DEV_TEST_CONFIG: ${{ toJSON(matrix.app) }} e2e-local-prod: @@ -243,7 +245,7 @@ jobs: run: cd workbench/${{ matrix.app.name }} && pnpm start & echo "starting tests in 10 seconds" && sleep 10 && pnpm run test:e2e env: APP_NAME: ${{ matrix.app.name }} - DEPLOYMENT_URL: "http://localhost:${{ matrix.app.name == 'sveltekit' && '4173' || '3000' }}" + DEPLOYMENT_URL: "http://localhost:${{ matrix.app.name == 'sveltekit' && '4173' || (matrix.app.name == 'astro' && '4321' || '3000') }}" e2e-local-postgres: name: E2E Local Postgres Tests (${{ matrix.app.name }} - ${{ matrix.app.canary && 'canary' || 'stable' }}) @@ -314,7 +316,7 @@ jobs: run: cd workbench/${{ matrix.app.name }} && pnpm start & echo "starting tests in 10 seconds" && sleep 10 && pnpm run test:e2e env: APP_NAME: ${{ matrix.app.name }} - DEPLOYMENT_URL: "http://localhost:${{ matrix.app.name == 'sveltekit' && '4173' || '3000' }}" + DEPLOYMENT_URL: "http://localhost:${{ matrix.app.name == 'sveltekit' && '4173' || (matrix.app.name == 'astro' && '4321' || '3000') }}" e2e-windows: name: E2E Windows Tests diff --git a/docs/app/(home)/components/frameworks.tsx b/docs/app/(home)/components/frameworks.tsx index 1d21d9755..6d47cc91b 100644 --- a/docs/app/(home)/components/frameworks.tsx +++ b/docs/app/(home)/components/frameworks.tsx @@ -1,12 +1,12 @@ -"use client"; +'use client'; -import { track } from "@vercel/analytics"; -import Link from "next/link"; -import type { ComponentProps } from "react"; -import { toast } from "sonner"; -import { Badge } from "@/components/ui/badge"; +import { track } from '@vercel/analytics'; +import Link from 'next/link'; +import type { ComponentProps } from 'react'; +import { toast } from 'sonner'; +import { Badge } from '@/components/ui/badge'; -export const Express = (props: ComponentProps<"svg">) => ( +export const Express = (props: ComponentProps<'svg'>) => ( ) => ( ); -export const AstroDark = (props: ComponentProps<"svg">) => ( +export const AstroDark = (props: ComponentProps<'svg'>) => ( - + Astro + ) => ( y2="84.9468" gradientUnits="userSpaceOnUse" > - - + + @@ -63,14 +64,15 @@ export const AstroDark = (props: ComponentProps<"svg">) => ( ); -export const AstroLight = (props: ComponentProps<"svg">) => ( +export const AstroLight = (props: ComponentProps<'svg'>) => ( - + Astro + ) => ( ); -export const AstroGray = (props: ComponentProps<"svg">) => ( +export const AstroGray = (props: ComponentProps<'svg'>) => ( - + Astro + ) => ( ); -export const TanStack = (props: ComponentProps<"svg">) => ( +export const TanStack = (props: ComponentProps<'svg'>) => ( - + TanStack + ) => ( ); -export const TanStackGray = (props: ComponentProps<"svg">) => ( +export const TanStackGray = (props: ComponentProps<'svg'>) => ( ) => ( > ); -export const Vite = (props: ComponentProps<"svg">) => ( +export const Vite = (props: ComponentProps<'svg'>) => ( ) => ( ); -export const Nitro = (props: ComponentProps<"svg">) => ( +export const Nitro = (props: ComponentProps<'svg'>) => ( ) => ( /> ) => ( ); -export const SvelteKit = (props: ComponentProps<"svg">) => ( +export const SvelteKit = (props: ComponentProps<'svg'>) => ( ) => ( ); -export const SvelteKitGray = (props: ComponentProps<"svg">) => ( +export const SvelteKitGray = (props: ComponentProps<'svg'>) => ( ) => ( ); -export const Nuxt = (props: ComponentProps<"svg">) => ( +export const Nuxt = (props: ComponentProps<'svg'>) => ( ) => ( ); -export const NuxtGray = (props: ComponentProps<"svg">) => ( +export const NuxtGray = (props: ComponentProps<'svg'>) => ( ) => ( ); -export const Hono = (props: ComponentProps<"svg">) => ( +export const Hono = (props: ComponentProps<'svg'>) => ( Hono ) => ( ); -export const HonoGray = (props: ComponentProps<"svg">) => ( +export const HonoGray = (props: ComponentProps<'svg'>) => ( Hono ) => ( ); -export const Bun = (props: ComponentProps<"svg">) => ( +export const Bun = (props: ComponentProps<'svg'>) => ( ) => ( id="Top" d="M35.12,5.53A16.41,16.41,0,0,1,29.49,18c-.28.25-.06.73.3.59,3.37-1.31,7.92-5.23,6-13.14C35.71,5,35.12,5.12,35.12,5.53Zm2.27,0A16.24,16.24,0,0,1,39,19c-.12.35.31.65.55.36C41.74,16.56,43.65,11,37.93,5,37.64,4.74,37.19,5.14,37.39,5.49Zm2.76-.17A16.42,16.42,0,0,1,47,17.12a.33.33,0,0,0,.65.11c.92-3.49.4-9.44-7.17-12.53C40.08,4.54,39.82,5.08,40.15,5.32ZM21.69,15.76a16.94,16.94,0,0,0,10.47-9c.18-.36.75-.22.66.18-1.73,8-7.52,9.67-11.12,9.45C21.32,16.4,21.33,15.87,21.69,15.76Z" fill="#ccbea7" - style={{ fillRule: "evenodd" }} + style={{ fillRule: 'evenodd' }} /> ) => ( ); -export const BunGray = (props: ComponentProps<"svg">) => ( +export const BunGray = (props: ComponentProps<'svg'>) => ( ) => ( id="Top" d="M35.12,5.53A16.41,16.41,0,0,1,29.49,18c-.28.25-.06.73.3.59,3.37-1.31,7.92-5.23,6-13.14C35.71,5,35.12,5.12,35.12,5.53Zm2.27,0A16.24,16.24,0,0,1,39,19c-.12.35.31.65.55.36C41.74,16.56,43.65,11,37.93,5,37.64,4.74,37.19,5.14,37.39,5.49Zm2.76-.17A16.42,16.42,0,0,1,47,17.12a.33.33,0,0,0,.65.11c.92-3.49.4-9.44-7.17-12.53C40.08,4.54,39.82,5.08,40.15,5.32ZM21.69,15.76a16.94,16.94,0,0,0,10.47-9c.18-.36.75-.22.66.18-1.73,8-7.52,9.67-11.12,9.45C21.32,16.4,21.33,15.87,21.69,15.76Z" fill="var(--color-background)" - style={{ fillRule: "evenodd" }} + style={{ fillRule: 'evenodd' }} /> ) => ( ); -export const Nest = (props: ComponentProps<"svg">) => ( +export const Nest = (props: ComponentProps<'svg'>) => ( ) => ( ); -export const NestGray = (props: ComponentProps<"svg">) => ( +export const NestGray = (props: ComponentProps<'svg'>) => ( ) => ( ); -export const Next = (props: ComponentProps<"svg">) => ( +export const Next = (props: ComponentProps<'svg'>) => ( Next.js @@ -681,8 +685,8 @@ export const Next = (props: ComponentProps<"svg">) => ( export const Frameworks = () => { const handleRequest = (framework: string) => { - track("Framework requested", { framework: framework.toLowerCase() }); - toast.success("Request received", { + track('Framework requested', { framework: framework.toLowerCase() }); + toast.success('Request received', { description: `Thanks for expressing interest in ${framework}. We will be adding support for it soon.`, }); }; @@ -695,72 +699,93 @@ export const Frameworks = () => { with the frameworks you already use with more coming soon. -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
- +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+ + Coming soon + +
+
handleRequest('NestJS')} + > + + +
+
handleRequest('TanStack')} > - Coming soon - - - Click on a framework to request support for it - - - Click a framework to request support - + + +
-
-
handleRequest("NestJS")} - > - - -
-
handleRequest("TanStack")} - > - - -
-
handleRequest("Astro")} - > - - - +

+ Click a framework to request support for it +

diff --git a/docs/content/docs/getting-started/astro.mdx b/docs/content/docs/getting-started/astro.mdx new file mode 100644 index 000000000..459d2b84a --- /dev/null +++ b/docs/content/docs/getting-started/astro.mdx @@ -0,0 +1,233 @@ +--- +title: Astro +--- + +This guide will walk through setting up your first workflow in an Astro app. Along the way, you'll learn more about the concepts that are fundamental to using the development kit in your own projects. + +--- + + + + +## Create Your Astro Project + +Start by creating a new Astro project. This command will create a new directory named `my-workflow-app` and setup a minimal Astro project inside it. + +```bash +npm create astro@latest my-workflow-app -- --template minimal --install --yes +``` + +Enter the newly made directory: + +```bash +cd my-workflow-app +``` + +### Install `workflow` + +```package-install +npm i workflow +``` + +### Configure Astro + +Add `workflow()` to your Astro config. This enables usage of the `"use workflow"` and `"use step"` directives. + +```typescript title="astro.config.mjs" lineNumbers +// @ts-check +import { defineConfig } from "astro/config"; +import { workflow } from "workflow/astro"; + +// https://astro.build/config +export default defineConfig({ + integrations: [workflow()], +}); +``` + + + + + ### Setup IntelliSense for TypeScript (Optional) + + + +To enable helpful hints in your IDE, setup the workflow plugin in `tsconfig.json`: + +```json title="tsconfig.json" lineNumbers +{ + "compilerOptions": { + // ... rest of your TypeScript config + "plugins": [ + { + "name": "workflow" // [!code highlight] + } + ] + } +} +``` + + + + + + + + + +## Create Your First Workflow + +Create a new file for our first workflow: + +```typescript title="src/workflows/user-signup.ts" lineNumbers +import { sleep } from "workflow"; + +export async function handleUserSignup(email: string) { + "use workflow"; // [!code highlight] + + const user = await createUser(email); + await sendWelcomeEmail(user); + + await sleep("5s"); // Pause for 5s - doesn't consume any resources + await sendOnboardingEmail(user); + + return { userId: user.id, status: "onboarded" }; +} + +``` + +We'll fill in those functions next, but let's take a look at this code: + +* We define a **workflow** function with the directive `"use workflow"`. Think of the workflow function as the _orchestrator_ of individual **steps**. +* The Workflow DevKit's `sleep` function allows us to suspend execution of the workflow without using up any resources. A sleep can be a few seconds, hours, days, or even months long. + +## Create Your Workflow Steps + +Let's now define those missing functions. + +```typescript title="src/workflows/user-signup.ts" lineNumbers +import { FatalError } from "workflow" + +// Our workflow function defined earlier + +async function createUser(email: string) { + "use step"; // [!code highlight] + + console.log(`Creating user with email: ${email}`); + + // Full Node.js access - database calls, APIs, etc. + return { id: crypto.randomUUID(), email }; +} + +async function sendWelcomeEmail(user: { id: string; email: string; }) { + "use step"; // [!code highlight] + + console.log(`Sending welcome email to user: ${user.id}`); + + if (Math.random() < 0.3) { + // By default, steps will be retried for unhandled errors + throw new Error("Retryable!"); + } +} + +async function sendOnboardingEmail(user: { id: string; email: string}) { + "use step"; // [!code highlight] + + if (!user.email.includes("@")) { + // To skip retrying, throw a FatalError instead + throw new FatalError("Invalid Email"); + } + + console.log(`Sending onboarding email to user: ${user.id}`); +} +``` + +Taking a look at this code: + +* Business logic lives inside **steps**. When a step is invoked inside a **workflow**, it gets enqueued to run on a separate request while the workflow is suspended, just like `sleep`. +* If a step throws an error, like in `sendWelcomeEmail`, the step will automatically be retried until it succeeds (or hits the step's max retry count). +* Steps can throw a `FatalError` if an error is intentional and should not be retried. + + +We'll dive deeper into workflows, steps, and other ways to suspend or handle events in [Foundations](/docs/foundations). + + + + + + +## Create Your Route Handler + +To invoke your new workflow, we'll have to add your workflow to a `POST` API route handler, `src/pages/api/signup.ts` with the following code: + +```typescript title="src/pages/api/signup.ts" +import type { APIRoute } from "astro"; +import { start } from "workflow/api"; +import { handleUserSignup } from "../../workflows/user-signup"; + +export const POST: APIRoute = async ({ request }: { request: Request }) => { + const { email } = await request.json(); + + // Executes asynchronously and doesn't block your app + await start(handleUserSignup, [email]); + return Response.json({ + message: "User signup workflow started", + }); +}; + +export const prerender = false; // Don't prerender this page since it's an API route +``` + +This route handler creates a `POST` request endpoint at `/api/signup` that will trigger your workflow. + + +Workflows can be triggered from API routes or any server-side code. + + + + + + +## Run in Development + +To start your development server, run the following command in your terminal in the Vite root directory: + +```bash +npm run dev +``` + +Once your development server is running, you can trigger your workflow by running this command in the terminal: + +```bash +curl -X POST --json '{"email":"hello@example.com"}' http://localhost:5173/api/signup +``` + +Check the Astro development server logs to see your workflow execute as well as the steps that are being processed. + +Additionally, you can use the [Workflow DevKit CLI or Web UI](/docs/observability) to inspect your workflow runs and steps in detail. + +```bash +npx workflow inspect runs +# or add '--web' for an interactive Web based UI +``` + +Workflow DevKit Web UI + +--- + +## Deploying to Production + +Workflow DevKit apps currently work best when deployed to [Vercel](https://vercel.com/home) and needs no special configuration. + +To deploy your Astro project to Vercel, ensure that the [Astro Vercel adapter](https://docs.astro.build/en/guides/integrations-guide/vercel) is configured: + +```bash +npx astro add vercel +``` + +Additionally, check the [Deploying](/docs/deploying) section to learn how your workflows can be deployed elsewhere. + +## Next Steps + +* Learn more about the [Foundations](/docs/foundations). +* Check [Errors](/docs/errors) if you encounter issues. +* Explore the [API Reference](/docs/api-reference). diff --git a/docs/content/docs/getting-started/index.mdx b/docs/content/docs/getting-started/index.mdx index 11a399aea..7f52fd3b3 100644 --- a/docs/content/docs/getting-started/index.mdx +++ b/docs/content/docs/getting-started/index.mdx @@ -3,56 +3,59 @@ title: Getting Started description: Start by choosing your framework. Each guide will walk you through the steps to install the dependencies and start running your first workflow. --- -import { Next, Nitro, SvelteKit, Nuxt, Hono, Bun, AstroDark, AstroLight, TanStack, Vite, Express } from '@/app/(home)/components/frameworks'; +import { Next, Nitro, SvelteKit, Nuxt, Hono, Bun, AstroDark, AstroLight, TanStack, Vite, Express, Nest } from '@/app/(home)/components/frameworks'; -
- - Next.js -
+
Next.js
-
+
Vite
+ +
+ + + Astro +
+
-
+
Express
-
+
Hono
-
+
Nitro
-
+
Nuxt
-
+
SvelteKit
- - - Astro + + NestJS Coming soon
diff --git a/package.json b/package.json index 5e80e2d39..f5e934c8e 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,8 @@ "packageManager": "pnpm@10.20.0+sha512.cf9998222162dd85864d0a8102e7892e7ba4ceadebbf5a31f9c2fce48dfce317a9c53b9f6464d1ef9042cba2e02ae02a9f7c143a2b438cd93c91840f0192b9dd", "pnpm": { "overrides": { - "rfc6902": "5.1.2" + "rfc6902": "5.1.2", + "path-to-regexp": "6.3.0" }, "onlyBuiltDependencies": [ "bun" diff --git a/packages/astro/LICENSE.md b/packages/astro/LICENSE.md new file mode 100644 index 000000000..c4d680f55 --- /dev/null +++ b/packages/astro/LICENSE.md @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2025 Vercel Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/packages/astro/README.md b/packages/astro/README.md new file mode 100644 index 000000000..17a1b2eab --- /dev/null +++ b/packages/astro/README.md @@ -0,0 +1,3 @@ +# workflow/astro + +The docs have moved! Refer to them [here](https://useworkflow.dev/) diff --git a/packages/astro/package.json b/packages/astro/package.json new file mode 100644 index 000000000..dfd49b7ce --- /dev/null +++ b/packages/astro/package.json @@ -0,0 +1,39 @@ +{ + "name": "@workflow/astro", + "version": "4.0.0-beta.1", + "description": "Astro integration for Workflow DevKit", + "type": "module", + "main": "dist/index.js", + "files": [ + "dist" + ], + "publishConfig": { + "access": "public" + }, + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "https://github.com/vercel/workflow.git", + "directory": "packages/astro" + }, + "exports": { + ".": "./dist/index.js" + }, + "scripts": { + "build": "tsc", + "dev": "tsc --watch", + "clean": "tsc --build --clean && rm -rf dist" + }, + "dependencies": { + "@swc/core": "1.11.24", + "@workflow/builders": "workspace:*", + "@workflow/swc-plugin": "workspace:*", + "exsolve": "^1.0.7", + "pathe": "^2.0.3" + }, + "devDependencies": { + "@types/node": "catalog:", + "@workflow/tsconfig": "workspace:*", + "astro": "5.16.0" + } +} diff --git a/packages/astro/src/builder.ts b/packages/astro/src/builder.ts new file mode 100644 index 000000000..94f9852df --- /dev/null +++ b/packages/astro/src/builder.ts @@ -0,0 +1,260 @@ +import { mkdir, readFile, writeFile } from 'node:fs/promises'; +import { join, resolve } from 'node:path'; +import { + BaseBuilder, + VercelBuildOutputAPIBuilder, + createBaseBuilderConfig, + type AstroConfig, +} from '@workflow/builders'; + +// NOTE: This is the same as SvelteKit request converter, should merge +const NORMALIZE_REQUEST_CONVERTER = ` +async function normalizeRequestConverter(request) { + const options = { + method: request.method, + headers: new Headers(request.headers) + }; + if (!['GET', 'HEAD', 'OPTIONS', 'TRACE', 'CONNECT'].includes(request.method)) { + options.body = await request.arrayBuffer(); + } + return new Request(request.url, options); +} +`; + +const WORKFLOW_ROUTES = [ + { + src: '^/\\.well-known/workflow/v1/flow/?$', + dest: '/.well-known/workflow/v1/flow', + }, + { + src: '^/\\.well-known/workflow/v1/step/?$', + dest: '/.well-known/workflow/v1/step', + }, + { + src: '^/\\.well-known/workflow/v1/webhook/([^/]+?)/?$', + dest: '/.well-known/workflow/v1/webhook/[token]', + }, +]; + +export class LocalBuilder extends BaseBuilder { + constructor() { + super({ + dirs: ['src/pages', 'src/workflows'], + buildTarget: 'astro' as const, + stepsBundlePath: '', // unused in base + workflowsBundlePath: '', // unused in base + webhookBundlePath: '', // unused in base + workingDir: process.cwd(), + debugFilePrefix: '_', // Prefix with underscore so Astro ignores debug files + }); + } + + override async build(): Promise { + const pagesDir = resolve(this.config.workingDir, 'src/pages'); + const workflowGeneratedDir = join(pagesDir, '.well-known/workflow/v1'); + + // Ensure output directories exist + await mkdir(workflowGeneratedDir, { recursive: true }); + + // Add .gitignore to exclude generated files from version control + if (process.env.VERCEL_DEPLOYMENT_ID === undefined) { + await writeFile(join(workflowGeneratedDir, '.gitignore'), '*'); + } + + // Get workflow and step files to bundle + const inputFiles = await this.getInputFiles(); + const tsConfig = await this.getTsConfigOptions(); + + const options = { + inputFiles, + workflowGeneratedDir, + tsBaseUrl: tsConfig.baseUrl, + tsPaths: tsConfig.paths, + }; + + // Generate the three Astro route handlers + await this.buildStepsRoute(options); + await this.buildWorkflowsRoute(options); + await this.buildWebhookRoute({ workflowGeneratedDir }); + } + + private async buildStepsRoute({ + inputFiles, + workflowGeneratedDir, + tsPaths, + tsBaseUrl, + }: { + inputFiles: string[]; + workflowGeneratedDir: string; + tsBaseUrl?: string; + tsPaths?: Record; + }) { + // Create steps route: .well-known/workflow/v1/step.js + const stepsRouteFile = join(workflowGeneratedDir, 'step.js'); + await this.createStepsBundle({ + format: 'esm', + inputFiles, + outfile: stepsRouteFile, + externalizeNonSteps: true, + tsBaseUrl, + tsPaths, + }); + + let stepsRouteContent = await readFile(stepsRouteFile, 'utf-8'); + + // Normalize request, needed for preserving request through astro + stepsRouteContent = stepsRouteContent.replace( + /export\s*\{\s*stepEntrypoint\s+as\s+POST\s*\}\s*;?$/m, + `${NORMALIZE_REQUEST_CONVERTER} +export const POST = async ({request}) => { + const normalRequest = await normalizeRequestConverter(request); + return stepEntrypoint(normalRequest); +} + +export const prerender = false;` + ); + await writeFile(stepsRouteFile, stepsRouteContent); + } + + private async buildWorkflowsRoute({ + inputFiles, + workflowGeneratedDir, + tsPaths, + tsBaseUrl, + }: { + inputFiles: string[]; + workflowGeneratedDir: string; + tsBaseUrl?: string; + tsPaths?: Record; + }) { + // Create workflows route: .well-known/workflow/v1/flow.js + const workflowsRouteFile = join(workflowGeneratedDir, 'flow.js'); + await this.createWorkflowsBundle({ + format: 'esm', + outfile: workflowsRouteFile, + bundleFinalOutput: false, + inputFiles, + tsBaseUrl, + tsPaths, + }); + + let workflowsRouteContent = await readFile(workflowsRouteFile, 'utf-8'); + + // Normalize request, needed for preserving request through astro + workflowsRouteContent = workflowsRouteContent.replace( + /export const POST = workflowEntrypoint\(workflowCode\);?$/m, + `${NORMALIZE_REQUEST_CONVERTER} +export const POST = async ({request}) => { + const normalRequest = await normalizeRequestConverter(request); + return workflowEntrypoint(workflowCode)(normalRequest); +} + +export const prerender = false;` + ); + await writeFile(workflowsRouteFile, workflowsRouteContent); + } + + private async buildWebhookRoute({ + workflowGeneratedDir, + }: { + workflowGeneratedDir: string; + }) { + // Create webhook route: .well-known/workflow/v1/webhook/[token].js + const webhookRouteFile = join(workflowGeneratedDir, 'webhook/[token].js'); + + await this.createWebhookBundle({ + outfile: webhookRouteFile, + bundle: false, + }); + + // Post-process the generated file to wrap with Astro request converter + let webhookRouteContent = await readFile(webhookRouteFile, 'utf-8'); + + // Update handler signature to accept token as parameter + webhookRouteContent = webhookRouteContent.replace( + /async function handler\(request\) \{[\s\S]*?const token = decodeURIComponent\(pathParts\[pathParts\.length - 1\]\);/, + `async function handler(request, token) {` + ); + + // Remove the URL parsing code since we get token from params + webhookRouteContent = webhookRouteContent.replace( + /const url = new URL\(request\.url\);[\s\S]*?const pathParts = url\.pathname\.split\('\/'\);[\s\S]*?\n/, + '' + ); + + // Normalize request, needed for preserving request through astro + webhookRouteContent = webhookRouteContent.replace( + /export const GET = handler;\nexport const POST = handler;\nexport const PUT = handler;\nexport const PATCH = handler;\nexport const DELETE = handler;\nexport const HEAD = handler;\nexport const OPTIONS = handler;/, + `${NORMALIZE_REQUEST_CONVERTER} +const createHandler = (method) => async ({ request, params, platform }) => { + const normalRequest = await normalizeRequestConverter(request); + const response = await handler(normalRequest, params.token); + return response; +}; + +export const GET = createHandler('GET'); +export const POST = createHandler('POST'); +export const PUT = createHandler('PUT'); +export const PATCH = createHandler('PATCH'); +export const DELETE = createHandler('DELETE'); +export const HEAD = createHandler('HEAD'); +export const OPTIONS = createHandler('OPTIONS'); + +export const prerender = false;` + ); + + await writeFile(webhookRouteFile, webhookRouteContent); + } +} + +export class VercelBuilder extends VercelBuildOutputAPIBuilder { + constructor(config?: Partial) { + const workingDir = config?.workingDir || process.cwd(); + super({ + ...createBaseBuilderConfig({ + workingDir, + dirs: ['src/pages', 'src/workflows'], + }), + buildTarget: 'vercel-build-output-api', + debugFilePrefix: '_', + }); + } + + override async build(): Promise { + const configPath = join( + this.config.workingDir, + '.vercel/output/config.json' + ); + + // The config output by astro + const config = JSON.parse(await readFile(configPath, 'utf-8')); + + // Filter out existing workflow routes (wrong `dest` mapping) + config.routes = config.routes.filter( + (route: { src?: string; dest: string }) => + !route.src?.includes('.well-known/workflow') + ); + + // Find the index right after the "filesystem" handler and "continue: true" routes + let insertIndex = config.routes.findIndex( + (route: any) => route.handle === 'filesystem' + ); + + // Move past any routes with "continue: true" (like _astro cache headers) + while ( + insertIndex < config.routes.length - 1 && + config.routes[insertIndex + 1]?.continue === true + ) { + insertIndex++; + } + + // Insert workflow routes right after + config.routes.splice(insertIndex + 1, 0, ...WORKFLOW_ROUTES); + + // Bundles workflows for vercel + await super.build(); + + // Use old astro config with updated routes + await writeFile(configPath, JSON.stringify(config, null, 2)); + } +} diff --git a/packages/astro/src/index.ts b/packages/astro/src/index.ts new file mode 100644 index 000000000..0aee8949f --- /dev/null +++ b/packages/astro/src/index.ts @@ -0,0 +1 @@ +export { workflow } from './plugin.js'; diff --git a/packages/astro/src/plugin.ts b/packages/astro/src/plugin.ts new file mode 100644 index 000000000..b96506ab8 --- /dev/null +++ b/packages/astro/src/plugin.ts @@ -0,0 +1,196 @@ +import { relative } from 'node:path'; +import { transform } from '@swc/core'; +import { resolveModulePath } from 'exsolve'; +import type { AstroIntegration, HookParameters } from 'astro'; +import { LocalBuilder, VercelBuilder } from './builder.js'; + +export function workflow(): AstroIntegration { + const builder = new LocalBuilder(); + + return { + name: 'workflow:astro', + hooks: { + 'astro:config:setup': async ({ + updateConfig, + }: HookParameters<'astro:config:setup'>) => { + // Use local builder + if (!process.env.VERCEL_DEPLOYMENT_ID) { + try { + await builder.build(); + } catch (buildError) { + // Build might fail due to invalid workflow files or missing dependencies + // Log the error and rethrow to properly propagate to Astro + console.error('Build failed during config setup:', buildError); + throw buildError; + } + } + updateConfig({ + vite: { + plugins: [ + { + name: 'workflow:vite', + + // TODO: Move this to @workflow/vite or something since this is vite specific + // Transform workflow files with SWC + async transform(code: string, id: string) { + // Only apply the transform if file needs it + if (!code.match(/(use step|use workflow)/)) { + return null; + } + + const isTypeScript = + id.endsWith('.ts') || id.endsWith('.tsx'); + const isTsx = id.endsWith('.tsx'); + + const swcPlugin = resolveModulePath('@workflow/swc-plugin', { + from: [import.meta.url], + }); + + // Calculate relative filename for SWC plugin + // The SWC plugin uses filename to generate workflowId, so it must be relative + const workingDir = process.cwd(); + const normalizedWorkingDir = workingDir + .replace(/\\/g, '/') + .replace(/\/$/, ''); + const normalizedFilepath = id.replace(/\\/g, '/'); + + // Windows fix: Use case-insensitive comparison to work around drive letter casing issues + const lowerWd = normalizedWorkingDir.toLowerCase(); + const lowerPath = normalizedFilepath.toLowerCase(); + + let relativeFilename: string; + if (lowerPath.startsWith(lowerWd + '/')) { + // File is under working directory - manually calculate relative path + relativeFilename = normalizedFilepath.substring( + normalizedWorkingDir.length + 1 + ); + } else if (lowerPath === lowerWd) { + // File IS the working directory (shouldn't happen) + relativeFilename = '.'; + } else { + // Use relative() for files outside working directory + relativeFilename = relative(workingDir, id).replace( + /\\/g, + '/' + ); + + if (relativeFilename.startsWith('../')) { + relativeFilename = relativeFilename + .split('/') + .filter((part) => part !== '..') + .join('/'); + } + } + + // Final safety check - ensure we never pass an absolute path to SWC + if ( + relativeFilename.includes(':') || + relativeFilename.startsWith('/') + ) { + // This should rarely happen, but use filename split as last resort + relativeFilename = + normalizedFilepath.split('/').pop() || 'unknown.ts'; + } + + // Transform with SWC + const result = await transform(code, { + filename: relativeFilename, + jsc: { + parser: { + syntax: isTypeScript ? 'typescript' : 'ecmascript', + tsx: isTsx, + }, + target: 'es2022', + experimental: { + plugins: [[swcPlugin, { mode: 'client' }]], + }, + }, + minify: false, + sourceMaps: true, + inlineSourcesContent: true, + }); + + return { + code: result.code, + map: result.map ? JSON.parse(result.map) : null, + }; + }, + + // TODO: Move this to @workflow/vite or something since this is vite specific + async hotUpdate(options) { + const { file, server, read } = options; + + // Check if this is a TS/JS file that might contain workflow directives + const jsTsRegex = /\.(ts|tsx|js|jsx|mjs|cjs)$/; + if (!jsTsRegex.test(file)) { + return; + } + + // Read the file to check for workflow/step directives + let content: string; + try { + content = await read(); + } catch { + // File might have been deleted - trigger rebuild to update generated routes + console.log( + 'Workflow file deleted, regenerating routes...' + ); + try { + await builder.build(); + } catch (buildError) { + // Build might fail if files are being deleted during test cleanup + // Log but don't crash - the next successful change will trigger a rebuild + console.error( + 'Build failed during file deletion:', + buildError + ); + } + + server.ws.send({ type: 'full-reload', path: '*' }); + return []; + } + + const useWorkflowPattern = /^\s*(['"])use workflow\1;?\s*$/m; + const useStepPattern = /^\s*(['"])use step\1;?\s*$/m; + + if ( + !useWorkflowPattern.test(content) && + !useStepPattern.test(content) + ) { + return; + } + + // Rebuild everything - simpler and more reliable than tracking individual files + console.log('Workflow file changed, regenerating routes...'); + try { + await builder.build(); + } catch (buildError) { + // Build might fail if files are being modified/deleted during test cleanup + // Log but don't crash - the next successful change will trigger a rebuild + console.error('Build failed during HMR:', buildError); + return []; + } + + // Trigger full reload of workflow routes + server.ws.send({ + type: 'full-reload', + path: '*', + }); + + // Prevent Vite from processing this HMR update + return []; + }, + }, + ], + }, + }); + }, + 'astro:build:done': async () => { + if (process.env.VERCEL_DEPLOYMENT_ID) { + const vercelBuilder = new VercelBuilder(); + await vercelBuilder.build(); + } + }, + }, + }; +} diff --git a/packages/astro/tsconfig.json b/packages/astro/tsconfig.json new file mode 100644 index 000000000..ba5d9aec0 --- /dev/null +++ b/packages/astro/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "@workflow/tsconfig/base.json", + "compilerOptions": { + "outDir": "dist", + "target": "es2022", + "module": "preserve", + "baseUrl": ".", + "moduleResolution": "bundler" + }, + "include": ["src"], + "exclude": ["node_modules", "**/*.test.ts"] +} diff --git a/packages/builders/src/base-builder.ts b/packages/builders/src/base-builder.ts index 4319251d2..21d1c0000 100644 --- a/packages/builders/src/base-builder.ts +++ b/packages/builders/src/base-builder.ts @@ -1,5 +1,5 @@ import { mkdir, readFile, writeFile } from 'node:fs/promises'; -import { dirname, join, relative, resolve } from 'node:path'; +import { basename, dirname, join, relative, resolve } from 'node:path'; import { promisify } from 'node:util'; import chalk from 'chalk'; import { parse } from 'comment-json'; @@ -184,14 +184,17 @@ export abstract class BaseBuilder { merge?: boolean ): Promise { try { + const prefix = this.config.debugFilePrefix || ''; + const debugFileName = `${dirname(outfile)}/${prefix}${basename(outfile)}.debug.json`; + let existing = {}; if (merge) { existing = JSON.parse( - await readFile(`${outfile}.debug.json`, 'utf8').catch(() => '{}') + await readFile(debugFileName, 'utf8').catch(() => '{}') ); } await writeFile( - `${outfile}.debug.json`, + debugFileName, JSON.stringify( { ...existing, diff --git a/packages/builders/src/index.ts b/packages/builders/src/index.ts index 1301c5461..d949717b6 100644 --- a/packages/builders/src/index.ts +++ b/packages/builders/src/index.ts @@ -8,6 +8,7 @@ export { createNodeModuleErrorPlugin } from './node-module-esbuild-plugin.js'; export { StandaloneBuilder } from './standalone.js'; export { createSwcPlugin } from './swc-esbuild-plugin.js'; export type { + AstroConfig, BuildTarget, NextConfig, StandaloneConfig, @@ -15,8 +16,5 @@ export type { VercelBuildOutputConfig, WorkflowConfig, } from './types.js'; -export { - isValidBuildTarget, - validBuildTargets, -} from './types.js'; +export { isValidBuildTarget, validBuildTargets } from './types.js'; export { VercelBuildOutputAPIBuilder } from './vercel-build-output-api.js'; diff --git a/packages/builders/src/types.ts b/packages/builders/src/types.ts index 5c514142f..87c0d5428 100644 --- a/packages/builders/src/types.ts +++ b/packages/builders/src/types.ts @@ -3,6 +3,7 @@ export const validBuildTargets = [ 'vercel-build-output-api', 'next', 'sveltekit', + 'astro', ] as const; export type BuildTarget = (typeof validBuildTargets)[number]; @@ -22,6 +23,9 @@ interface BaseWorkflowConfig { externalPackages?: string[]; workflowManifestPath?: string; + + // Optional prefix for debug files (e.g., "_" for Astro to ignore them) + debugFilePrefix?: string; } /** @@ -66,6 +70,17 @@ export interface SvelteKitConfig extends BaseWorkflowConfig { webhookBundlePath: string; } +/** + * Configuration for Astro builds. + */ +export interface AstroConfig extends BaseWorkflowConfig { + buildTarget: 'astro'; + // Astro builder computes paths dynamically, so these are not used + stepsBundlePath: string; + workflowsBundlePath: string; + webhookBundlePath: string; +} + /** * Discriminated union of all builder configuration types. */ @@ -73,7 +88,8 @@ export type WorkflowConfig = | StandaloneConfig | VercelBuildOutputConfig | NextConfig - | SvelteKitConfig; + | SvelteKitConfig + | AstroConfig; export function isValidBuildTarget( target: string | undefined diff --git a/packages/core/e2e/e2e.test.ts b/packages/core/e2e/e2e.test.ts index 27cfaeba3..0ac6e71dc 100644 --- a/packages/core/e2e/e2e.test.ts +++ b/packages/core/e2e/e2e.test.ts @@ -600,7 +600,8 @@ describe('e2e', () => { // TODO: Investigate esbuild source map generation for bundled modules const isViteBasedFrameworkDevMode = (process.env.APP_NAME === 'sveltekit' || - process.env.APP_NAME === 'vite') && + process.env.APP_NAME === 'vite' || + process.env.APP_NAME === 'astro') && isLocalDeployment(); if (!isViteBasedFrameworkDevMode) { diff --git a/packages/core/e2e/local-build.test.ts b/packages/core/e2e/local-build.test.ts index 4daca2b01..b65b64e96 100644 --- a/packages/core/e2e/local-build.test.ts +++ b/packages/core/e2e/local-build.test.ts @@ -14,6 +14,7 @@ describe.each([ 'nuxt', 'hono', 'express', + 'astro', ])('e2e', (project) => { test('builds without errors', { timeout: 180_000 }, async () => { // skip if we're targeting specific app to test diff --git a/packages/workflow/package.json b/packages/workflow/package.json index 2b4fa5c9c..db3957002 100644 --- a/packages/workflow/package.json +++ b/packages/workflow/package.json @@ -41,6 +41,7 @@ "./nitro": "./dist/nitro.js", "./nuxt": "./dist/nuxt.js", "./sveltekit": "./dist/sveltekit.js", + "./astro": "./dist/astro.js", "./rollup": "./dist/rollup.js", "./vite": "./dist/vite.js", "./runtime": "./dist/runtime.js" @@ -53,6 +54,7 @@ "typecheck": "tsc --noEmit" }, "dependencies": { + "@workflow/astro": "workspace:*", "@workflow/cli": "workspace:*", "@workflow/core": "workspace:*", "@workflow/errors": "workspace:*", diff --git a/packages/workflow/src/astro.ts b/packages/workflow/src/astro.ts new file mode 100644 index 000000000..7caf16677 --- /dev/null +++ b/packages/workflow/src/astro.ts @@ -0,0 +1 @@ +export * from '@workflow/astro'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 78e318255..4f48126c1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -45,6 +45,7 @@ catalogs: overrides: rfc6902: 5.1.2 + path-to-regexp: 6.3.0 importers: @@ -314,6 +315,34 @@ importers: specifier: workspace:* version: link:../workflow + packages/astro: + dependencies: + '@swc/core': + specifier: 1.11.24 + version: 1.11.24 + '@workflow/builders': + specifier: workspace:* + version: link:../builders + '@workflow/swc-plugin': + specifier: workspace:* + version: link:../swc-plugin-workflow + exsolve: + specifier: ^1.0.7 + version: 1.0.7 + pathe: + specifier: ^2.0.3 + version: 2.0.3 + devDependencies: + '@types/node': + specifier: 'catalog:' + version: 22.19.0 + '@workflow/tsconfig': + specifier: workspace:* + version: link:../tsconfig + astro: + specifier: 5.16.0 + version: 5.16.0(@netlify/blobs@9.1.2)(@types/node@22.19.0)(@vercel/functions@3.1.4(@aws-sdk/credential-provider-web-identity@3.844.0))(db0@0.3.4(better-sqlite3@11.10.0)(drizzle-orm@0.44.7(@opentelemetry/api@1.9.0)(better-sqlite3@11.10.0)(pg@8.16.3)(postgres@3.4.7)))(ioredis@5.8.2)(jiti@2.6.1)(lightningcss@1.30.1)(rollup@4.53.2)(terser@5.44.0)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1) + packages/builders: dependencies: '@swc/core': @@ -875,6 +904,9 @@ importers: '@opentelemetry/api': specifier: '1' version: 1.9.0 + '@workflow/astro': + specifier: workspace:* + version: link:../astro '@workflow/cli': specifier: workspace:* version: link:../cli @@ -1091,6 +1123,36 @@ importers: specifier: 3.2.0 version: 3.2.0 + workbench/astro: + dependencies: + '@astrojs/node': + specifier: 9.5.0 + version: 9.5.0(astro@5.16.0(@netlify/blobs@9.1.2)(@types/node@24.6.2)(@vercel/functions@3.1.4(@aws-sdk/credential-provider-web-identity@3.844.0))(db0@0.3.4(better-sqlite3@11.10.0)(drizzle-orm@0.44.7(@opentelemetry/api@1.9.0)(better-sqlite3@11.10.0)(pg@8.16.3)(postgres@3.4.7)))(ioredis@5.8.2)(jiti@2.6.1)(lightningcss@1.30.1)(rollup@4.53.2)(terser@5.44.0)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1)) + '@astrojs/vercel': + specifier: ^9.0.0 + version: 9.0.1(@aws-sdk/credential-provider-web-identity@3.844.0)(@sveltejs/kit@2.48.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.1.12(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(svelte@5.43.3)(vite@7.1.12(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(astro@5.16.0(@netlify/blobs@9.1.2)(@types/node@24.6.2)(@vercel/functions@3.1.4(@aws-sdk/credential-provider-web-identity@3.844.0))(db0@0.3.4(better-sqlite3@11.10.0)(drizzle-orm@0.44.7(@opentelemetry/api@1.9.0)(better-sqlite3@11.10.0)(pg@8.16.3)(postgres@3.4.7)))(ioredis@5.8.2)(jiti@2.6.1)(lightningcss@1.30.1)(rollup@4.53.2)(terser@5.44.0)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1))(next@16.0.3(@opentelemetry/api@1.9.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)(rollup@4.53.2)(svelte@5.43.3)(vue-router@4.6.3(vue@3.5.22(typescript@5.9.3)))(vue@3.5.22(typescript@5.9.3)) + '@workflow/world-postgres': + specifier: workspace:* + version: link:../../packages/world-postgres + ai: + specifier: 'catalog:' + version: 5.0.76(zod@4.1.11) + astro: + specifier: ^5.15.6 + version: 5.16.0(@netlify/blobs@9.1.2)(@types/node@24.6.2)(@vercel/functions@3.1.4(@aws-sdk/credential-provider-web-identity@3.844.0))(db0@0.3.4(better-sqlite3@11.10.0)(drizzle-orm@0.44.7(@opentelemetry/api@1.9.0)(better-sqlite3@11.10.0)(pg@8.16.3)(postgres@3.4.7)))(ioredis@5.8.2)(jiti@2.6.1)(lightningcss@1.30.1)(rollup@4.53.2)(terser@5.44.0)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1) + lodash.chunk: + specifier: ^4.2.0 + version: 4.2.0 + openai: + specifier: 6.9.0 + version: 6.9.0(ws@8.18.3)(zod@4.1.11) + workflow: + specifier: workspace:* + version: link:../../packages/workflow + zod: + specifier: 'catalog:' + version: 4.1.11 + workbench/example: dependencies: '@vercel/functions': @@ -1598,6 +1660,36 @@ packages: '@antfu/utils@9.3.0': resolution: {integrity: sha512-9hFT4RauhcUzqOE4f1+frMKLZrgNog5b06I7VmZQV1BkvwvqrbC8EBZf3L1eEL2AKb6rNKjER0sEvJiSP1FXEA==} + '@astrojs/compiler@2.13.0': + resolution: {integrity: sha512-mqVORhUJViA28fwHYaWmsXSzLO9osbdZ5ImUfxBarqsYdMlPbqAqGJCxsNzvppp1BEzc1mJNjOVvQqeDN8Vspw==} + + '@astrojs/internal-helpers@0.7.4': + resolution: {integrity: sha512-lDA9MqE8WGi7T/t2BMi+EAXhs4Vcvr94Gqx3q15cFEz8oFZMO4/SFBqYr/UcmNlvW+35alowkVj+w9VhLvs5Cw==} + + '@astrojs/internal-helpers@0.7.5': + resolution: {integrity: sha512-vreGnYSSKhAjFJCWAwe/CNhONvoc5lokxtRoZims+0wa3KbHBdPHSSthJsKxPd8d/aic6lWKpRTYGY/hsgK6EA==} + + '@astrojs/markdown-remark@6.3.9': + resolution: {integrity: sha512-hX2cLC/KW74Io1zIbn92kI482j9J7LleBLGCVU9EP3BeH5MVrnFawOnqD0t/q6D1Z+ZNeQG2gNKMslCcO36wng==} + + '@astrojs/node@9.5.0': + resolution: {integrity: sha512-x1whLIatmCefaqJA8FjfI+P6FStF+bqmmrib0OUGM1M3cZhAXKLgPx6UF2AzQ3JgpXgCWYM24MHtraPvZhhyLQ==} + peerDependencies: + astro: ^5.14.3 + + '@astrojs/prism@3.3.0': + resolution: {integrity: sha512-q8VwfU/fDZNoDOf+r7jUnMC2//H2l0TuQ6FkGJL8vD8nw/q5KiL3DS1KKBI3QhI9UQhpJ5dc7AtqfbXWuOgLCQ==} + engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} + + '@astrojs/telemetry@3.3.0': + resolution: {integrity: sha512-UFBgfeldP06qu6khs/yY+q1cDAaArM2/7AEIqQ9Cuvf7B1hNLq0xDrZkct+QoIGyjq56y8IaE2I3CTvG99mlhQ==} + engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0} + + '@astrojs/vercel@9.0.1': + resolution: {integrity: sha512-wdDcRygD7hnFy4c+5ynCZ43lsSXaAeTr7d3A2VedDf/vNrSmT6ZNIECUHzbizrraTqKk8eFC9RQf5aKouSdirw==} + peerDependencies: + astro: ^5.0.0 + '@aws-crypto/sha256-browser@5.2.0': resolution: {integrity: sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==} @@ -1909,6 +2001,10 @@ packages: '@braintree/sanitize-url@7.1.1': resolution: {integrity: sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw==} + '@capsizecss/unpack@3.0.1': + resolution: {integrity: sha512-8XqW8xGn++Eqqbz3e9wKuK7mxryeRjs4LOHLxbh2lwKeSbuNR4NFifDZT4KzvjU6HMOPbiNTsWpniK5EJfTWkg==} + engines: {node: '>=18'} + '@cbor-extract/cbor-extract-darwin-arm64@2.2.0': resolution: {integrity: sha512-P7swiOAdF7aSi0H+tHtHtr6zrpF3aAq/W9FXx5HektRvLTM2O89xCyXF3pk7pLc7QpaY7AoaE8UowVf9QBdh3w==} cpu: [arm64] @@ -3515,6 +3611,9 @@ packages: resolution: {integrity: sha512-scSmQBD8eANlMUOglxHrN1JdSW8tDghsPuS83otqealBiIeMukCQMOf/wc0JJjDXomqwNdEQFLXLGHrU6PGxuA==} engines: {node: '>= 20.0.0'} + '@oslojs/encoding@1.1.0': + resolution: {integrity: sha512-70wQhgYmndg4GCPxPPxPGevRKqTIJ2Nh4OkiMWmDAVYsTQ+Ta7Sq+rPevXyXGdzr30/qZBnyOalCszoMxlyldQ==} + '@oven/bun-darwin-aarch64@1.3.0': resolution: {integrity: sha512-WeXSaL29ylJEZMYHHW28QZ6rgAbxQ1KuNSZD9gvd3fPlo0s6s2PglvPArjjP07nmvIK9m4OffN0k4M98O7WmAg==} cpu: [arm64] @@ -6133,6 +6232,9 @@ packages: '@types/express@5.0.5': resolution: {integrity: sha512-LuIQOcb6UmnF7C1PCFmEU1u2hmiHL43fgFQX67sN3H4Z+0Yk0Neo++mFsBjhOAuLzvlQeqAAkeDOZrJs9rzumQ==} + '@types/fontkit@2.0.8': + resolution: {integrity: sha512-wN+8bYxIpJf+5oZdrdtaX04qUuWHcKxcDEgRS9Qm9ZClSHjzEn13SxUC+5eRM+4yXIeTYk8mTzLAWGF64847ew==} + '@types/fs-extra@11.0.4': resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==} @@ -6181,6 +6283,9 @@ packages: '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + '@types/nlcst@2.0.3': + resolution: {integrity: sha512-vSYNSDe6Ix3q+6Z7ri9lyWqgGhJTmzRjZRqyq15N0Z/1/UnVsno9G/N40NBijoYx2seFDIl0+B2mgAb9mezUCA==} + '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} @@ -6334,6 +6439,15 @@ packages: '@opentelemetry/api': optional: true + '@vercel/functions@2.2.13': + resolution: {integrity: sha512-14ArBSIIcOBx9nrEgaJb4Bw+en1gl6eSoJWh8qjifLl5G3E4dRXCFOT8HP+w66vb9Wqyd1lAQBrmRhRwOj9X9A==} + engines: {node: '>= 18'} + peerDependencies: + '@aws-sdk/credential-provider-web-identity': '*' + peerDependenciesMeta: + '@aws-sdk/credential-provider-web-identity': + optional: true + '@vercel/functions@3.1.4': resolution: {integrity: sha512-1dEfZkb7qxsA+ilo+1uBUCEgr7e90vHcimpDYkUB84DM051wQ5amJDk9x+cnaI29paZb5XukXwGl8yk3Udb/DQ==} engines: {node: '>= 20'} @@ -6353,6 +6467,10 @@ packages: engines: {node: '>=18'} hasBin: true + '@vercel/oidc@2.0.2': + resolution: {integrity: sha512-59PBFx3T+k5hLTEWa3ggiMpGRz1OVvl9eN8SUai+A43IsqiOuAe7qPBf+cray/Fj6mkgnxm/D7IAtjc8zSHi7g==} + engines: {node: '>= 18'} + '@vercel/oidc@3.0.3': resolution: {integrity: sha512-yNEQvPcVrK9sIe637+I0jD6leluPxzwJKx/Haw6F4H77CdDsszUn5V3o96LPziXkSNE2B83+Z3mjqGKBK/R6Gg==} engines: {node: '>= 20'} @@ -6378,6 +6496,9 @@ packages: engines: {node: '>=20.0.0'} hasBin: true + '@vercel/routing-utils@5.3.0': + resolution: {integrity: sha512-gKsPSGc/hBMSHiAXTjTcbSa9egy7wCogrLctx8bJUS21w2OppWzrd5ZqUMqvnrfdKzLMpGIJ2bZMxBQ9xjGkaA==} + '@vercel/speed-insights@1.2.0': resolution: {integrity: sha512-y9GVzrUJ2xmgtQlzFP2KhVRoCglwfRQgjyfY607aU0hh0Un6d0OUyrJkjuAlsV18qR4zfoFPs/BiIj9YDS6Wzw==} peerDependencies: @@ -6675,6 +6796,9 @@ packages: resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} engines: {node: '>= 0.4'} + array-iterate@2.0.1: + resolution: {integrity: sha512-I1jXZMjAgCMmxT4qxXfPXa6SthSoE8h6gkSI9BGGNv8mP8G/v0blc+qFnZu6K42vTOiuME596QaLO0TP3Lk0xg==} + array-timsort@1.0.3: resolution: {integrity: sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==} @@ -6708,6 +6832,11 @@ packages: resolution: {integrity: sha512-LElXdjswlqjWrPpJFg1Fx4wpkOCxj1TDHlSV4PlaRxHGWko024xICaa97ZkMfs6DRKlCguiAI+rbXv5GWwXIkg==} hasBin: true + astro@5.16.0: + resolution: {integrity: sha512-GaDRs2Mngpw3dr2vc085GnORh98NiXxwIjg/EoQQQl/icZt3Z7s0BRsYHDZ8swkZbOA6wZsqWJdrNirl+iKcDg==} + engines: {node: 18.20.8 || ^20.3.0 || >=22.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0'} + hasBin: true + async-lock@1.4.1: resolution: {integrity: sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==} @@ -6780,6 +6909,9 @@ packages: bare-url@2.3.0: resolution: {integrity: sha512-c+RCqMSZbkz97Mw1LWR0gcOqwK82oyYKfLoHJ8k13ybi1+I80ffdDzUy0TdAburdrR/kI0/VuN8YgEnJqX+Nyw==} + base-64@1.0.0: + resolution: {integrity: sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==} + base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} @@ -6845,6 +6977,9 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} + brotli@1.3.3: + resolution: {integrity: sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==} + browserslist@4.27.0: resolution: {integrity: sha512-AXVQwdhot1eqLihwasPElhX2tAZiBjWdJ9i/Zcj2S6QYIjkx62OKSfnobkriB81C3l4w0rVy3Nt4jaTBltYEpw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} @@ -7008,6 +7143,10 @@ packages: resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} engines: {node: '>=8'} + ci-info@4.3.1: + resolution: {integrity: sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA==} + engines: {node: '>=8'} + citty@0.1.6: resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} @@ -7056,6 +7195,10 @@ packages: resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} engines: {node: '>=0.8'} + clone@2.1.2: + resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} + engines: {node: '>=0.8'} + cloudflare-video-element@1.3.4: resolution: {integrity: sha512-F9g+tXzGEXI6v6L48qXxr8vnR8+L6yy7IhpJxK++lpzuVekMHTixxH7/dzLuq6OacVGziU4RB5pzZYJ7/LYtJg==} @@ -7145,6 +7288,9 @@ packages: resolution: {integrity: sha512-bKw/r35jR3HGt5PEPm1ljsQQGyCrR8sFGNiN5L+ykDHdpO8Smxkrkla9Yi6NkQyUrb8V54PGhfMs6NrIwtxtdw==} engines: {node: '>= 6'} + common-ancestor-path@1.0.1: + resolution: {integrity: sha512-L3sHRo1pXXEqX8VU28kfgUY+YGsk09hPqZiZmLacNib6XNTCM8ubYeT7ryXQw8asB1sKgcU5lkB7ONug08aB8w==} + common-path-prefix@3.0.0: resolution: {integrity: sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==} @@ -7666,6 +7812,10 @@ packages: peerDependencies: typescript: ^5.4.4 + deterministic-object-hash@2.0.2: + resolution: {integrity: sha512-KxektNH63SrbfUyDiwXqRb1rLwKt33AmMv+5Nhsw1kqZ13SJBRTgZHtGbE+hH3a1mVW1cz+4pqSWVPAtLVXTzQ==} + engines: {node: '>=18'} + devalue@5.5.0: resolution: {integrity: sha512-69sM5yrHfFLJt0AZ9QqZXGCPfJ7fQjvpln3Rq5+PS03LD32Ost1Q9N+eEnaQwGRIriKkMImXD56ocjQmfjbV3w==} @@ -7682,6 +7832,13 @@ packages: dexie@4.2.1: resolution: {integrity: sha512-Ckej0NS6jxQ4Po3OrSQBFddayRhTCic2DoCAG5zacOfOVB9P2Q5Xc5uL/nVa7ZVs+HdMnvUPzLFCB/JwpB6Csg==} + dfa@1.2.0: + resolution: {integrity: sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==} + + diff@5.2.0: + resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} + engines: {node: '>=0.3.1'} + diff@8.0.2: resolution: {integrity: sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==} engines: {node: '>=0.3.1'} @@ -7690,6 +7847,9 @@ packages: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + docker-compose@1.3.0: resolution: {integrity: sha512-7Gevk/5eGD50+eMD+XDnFnOrruFkL0kSd7jEG4cjmqweDSUhB7i0g8is/nBdVpl+Bx338SqIB2GLKm32M+Vs6g==} engines: {node: '>= 6.0.0'} @@ -7837,6 +7997,10 @@ packages: sqlite3: optional: true + dset@3.1.4: + resolution: {integrity: sha512-2QF/g9/zTaPDc3BjNcVTGoBbXBgYfMTTceLaYcFJ/W9kggFUkhxD/hMEeuLKbugyef9SqAx8cpgwlIP/jinUTA==} + engines: {node: '>=4'} + dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} @@ -8258,9 +8422,19 @@ packages: flatted@3.3.3: resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + flattie@1.1.1: + resolution: {integrity: sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==} + engines: {node: '>=8'} + fn.name@1.1.0: resolution: {integrity: sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==} + fontace@0.3.1: + resolution: {integrity: sha512-9f5g4feWT1jWT8+SbL85aLIRLIXUaDygaM2xPXRmzPYxrOMNok79Lr3FGJoKVNKibE0WCunNiEVG2mwuE+2qEg==} + + fontkit@2.0.4: + resolution: {integrity: sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==} + foreground-child@3.3.1: resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} @@ -8682,12 +8856,18 @@ packages: html-escaper@2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + html-escaper@3.0.3: + resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==} + html-url-attributes@3.0.1: resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} html-void-elements@3.0.0: resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==} + http-cache-semantics@4.2.0: + resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} + http-errors@2.0.0: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} engines: {node: '>= 0.8'} @@ -8761,6 +8941,9 @@ packages: import-in-the-middle@1.15.0: resolution: {integrity: sha512-bpQy+CrsRmYmoPMAE/0G33iwRqwW4ouqdRg8jgbH3aKuCtOc8lxgmYXg2dMM92CRiGP660EtBcymH/eVUpCSaA==} + import-meta-resolve@4.2.0: + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + impound@1.0.0: resolution: {integrity: sha512-8lAJ+1Arw2sMaZ9HE2ZmL5zOcMnt18s6+7Xqgq2aUVy4P1nlzAyPtzCDxsk51KVFwHEEdc6OWvUyqwHwhRYaug==} @@ -9046,6 +9229,10 @@ packages: resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} hasBin: true + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} @@ -9361,6 +9548,9 @@ packages: magicast@0.3.5: resolution: {integrity: sha512-L0WhttDl+2BOsybvEOLK7fW3UA0OQ0IQ2d6Zl2x/a6vVRs3bAY0ECOSHHeL5jD+SbOpOCUEi0y1DgHEn9Qn1AQ==} + magicast@0.5.1: + resolution: {integrity: sha512-xrHS24IxaLrvuo613F719wvOIv9xPHFWQHuvGUBmPnCA/3MQxKI3b+r7n1jAoDHmsbC5bRhTZYR77invLAxVnw==} + make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} @@ -9381,6 +9571,9 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + mdast-util-definitions@6.0.0: + resolution: {integrity: sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==} + mdast-util-find-and-replace@3.0.2: resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} @@ -9772,6 +9965,10 @@ packages: resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} engines: {node: '>= 0.6'} + neotraverse@0.6.18: + resolution: {integrity: sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA==} + engines: {node: '>= 10'} + netlify@13.3.5: resolution: {integrity: sha512-Nc3loyVASW59W+8fLDZT1lncpG7llffyZ2o0UQLx/Fr20i7P8oP+lE7+TEcFvXj9IUWU6LjB9P3BH+iFGyp+mg==} engines: {node: ^14.16.0 || >=16.0.0} @@ -9890,6 +10087,9 @@ packages: xml2js: optional: true + nlcst-to-string@4.0.0: + resolution: {integrity: sha512-YKLBCcUYKAg0FNlOBT6aI91qFmSiFKiluk655WzPF+DDMA02qIyy8uiRqI8QXtcFpEvll12LpL5MXqEmAZ+dcA==} + node-abi@3.78.0: resolution: {integrity: sha512-E2wEyrgX/CqvicaQYU3Ze1PFGjc4QYPGsjUrlYkqAE0WjHEZwgOsGMPMzkMse4LjJbDmaEuDX3CM036j5K2DSQ==} engines: {node: '>=10'} @@ -10095,6 +10295,18 @@ packages: zod: optional: true + openai@6.9.0: + resolution: {integrity: sha512-n2sJRYmM+xfJ0l3OfH8eNnIyv3nQY7L08gZQu3dw6wSdfPtKAk92L83M2NIP5SS8Cl/bsBBG3yKzEOjkx0O+7A==} + hasBin: true + peerDependencies: + ws: ^8.18.0 + zod: ^3.25 || ^4.0 + peerDependenciesMeta: + ws: + optional: true + zod: + optional: true + optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -10176,6 +10388,10 @@ packages: resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + p-limit@6.2.0: + resolution: {integrity: sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==} + engines: {node: '>=18'} + p-locate@4.1.0: resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} engines: {node: '>=8'} @@ -10196,6 +10412,10 @@ packages: resolution: {integrity: sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==} engines: {node: '>=18'} + p-queue@8.1.1: + resolution: {integrity: sha512-aNZ+VfjobsWryoiPnEApGGmf5WmNsCo9xu8dfaYamG5qaLP7ClhLN6NgsFe6SwJ2UbLEBK5dv9x8Mn5+RVhMWQ==} + engines: {node: '>=18'} + p-timeout@6.1.4: resolution: {integrity: sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==} engines: {node: '>=14.16'} @@ -10217,6 +10437,9 @@ packages: package-manager-detector@1.5.0: resolution: {integrity: sha512-uBj69dVlYe/+wxj8JOpr97XfsxH/eumMt6HqjNTmJDf/6NO9s+0uxeOneIz3AsPt2m6y9PqzDzd3ATcU17MNfw==} + pako@0.2.9: + resolution: {integrity: sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==} + parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} @@ -10239,6 +10462,9 @@ packages: resolution: {integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==} engines: {node: '>=18'} + parse-latin@7.0.0: + resolution: {integrity: sha512-mhHgobPPua5kZ98EF4HWiH167JWBfl4pvAIXXdbaVohtK7a6YBOy56kvhCqduqyo/f3yrHFWmqmiMg/BkBkYYQ==} + parse-ms@4.0.0: resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} engines: {node: '>=18'} @@ -10290,8 +10516,8 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} - path-to-regexp@8.3.0: - resolution: {integrity: sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==} + path-to-regexp@6.3.0: + resolution: {integrity: sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==} path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} @@ -10358,6 +10584,9 @@ packages: pgpass@1.0.5: resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} + piccolore@0.1.3: + resolution: {integrity: sha512-o8bTeDWjE086iwKrROaDf31K0qC/BENdm15/uH9usSC/uZjJOKb2YGiVHfLY4GhwsERiPI1jmwI2XrA7ACOxVw==} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -10633,6 +10862,10 @@ packages: resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==} engines: {node: '>=18'} + prismjs@1.30.0: + resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} + engines: {node: '>=6'} + process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} @@ -10921,12 +11154,21 @@ packages: rehype-katex@7.0.1: resolution: {integrity: sha512-OiM2wrZ/wuhKkigASodFoo8wimG3H12LWQaH8qSPVJn9apWKFSH3YOCtbKpBorTVw/eI7cuT21XBbvwEswbIOA==} + rehype-parse@9.0.1: + resolution: {integrity: sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==} + rehype-raw@7.0.0: resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==} rehype-recma@1.0.0: resolution: {integrity: sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==} + rehype-stringify@10.0.1: + resolution: {integrity: sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==} + + rehype@13.0.2: + resolution: {integrity: sha512-j31mdaRFrwFRUIlxGeuPXXKWQxet52RBQRvCmzl5eCefn/KGbomK5GMHNMsOJf55fgo3qw5tST5neDuarDYR2A==} + remark-gfm@4.0.1: resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} @@ -10942,6 +11184,10 @@ packages: remark-rehype@11.1.2: resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + remark-smartypants@3.0.2: + resolution: {integrity: sha512-ILTWeOriIluwEvPjv67v7Blgrcx+LZOkAUVtKI3putuhlZm84FnqDORNXPPm+HY3NdZOMhyDwZ1E+eZB/Df5dA==} + engines: {node: '>=16.0.0'} + remark-stringify@11.0.0: resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} @@ -10990,6 +11236,21 @@ packages: resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} engines: {node: '>=18'} + restructure@3.0.2: + resolution: {integrity: sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==} + + retext-latin@4.0.0: + resolution: {integrity: sha512-hv9woG7Fy0M9IlRQloq/N6atV82NxLGveq+3H2WOi79dtIYWN8OaxogDm77f8YnVXJL2VD3bbqowu5E3EMhBYA==} + + retext-smartypants@6.2.0: + resolution: {integrity: sha512-kk0jOU7+zGv//kfjXEBjdIryL1Acl4i9XNkHxtM7Tm5lFiCog576fjNC9hjoR7LTKQ0DsPWy09JummSsH1uqfQ==} + + retext-stringify@4.0.0: + resolution: {integrity: sha512-rtfN/0o8kL1e+78+uxPTqu1Klt0yPzKuQ2BfWwwfgIUSayyzxpM1PJzkKt4V8803uB9qSy32MvI7Xep9khTpiA==} + + retext@9.0.0: + resolution: {integrity: sha512-sbMDcpHCNjvlheSgMfEcVrZko3cDzdbe1x/e7G66dFp0Ff7Mldvi2uv6JkJQzdRcvLYE8CA8Oe8siQx8ZOgTcA==} + retry@0.12.0: resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} engines: {node: '>= 4'} @@ -11123,6 +11384,9 @@ packages: resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==} engines: {node: '>= 18'} + server-destroy@1.0.1: + resolution: {integrity: sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==} + set-cookie-parser@2.7.2: resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==} @@ -11215,6 +11479,10 @@ packages: smob@1.5.0: resolution: {integrity: sha512-g6T+p7QO8npa+/hNx9ohv1E5pVCmWrVCUzUXJyLdMmftX6ER0oiWY/w9knEonLpnOp6b6FenKnMfR8gqwWdwig==} + smol-toml@1.5.2: + resolution: {integrity: sha512-QlaZEqcAH3/RtNyet1IPIYPsEWAaYyXXv1Krsi+1L/QHppjX4Ifm8MQsBISz9vE8cHicIq3clogsheili5vhaQ==} + engines: {node: '>= 18'} + sonner@2.0.7: resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==} peerDependencies: @@ -11537,6 +11805,9 @@ packages: tiktok-video-element@0.1.1: resolution: {integrity: sha512-BaiVzvNz2UXDKTdSrXzrNf4q6Ecc+/utYUh7zdEu2jzYcJVDoqYbVfUl0bCfMoOeeAqg28vD/yN63Y3E9jOrlA==} + tiny-inflate@1.0.3: + resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==} + tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} @@ -11783,6 +12054,12 @@ packages: unhead@2.0.19: resolution: {integrity: sha512-gEEjkV11Aj+rBnY6wnRfsFtF2RxKOLaPN4i+Gx3UhBxnszvV6ApSNZbGk7WKyy/lErQ6ekPN63qdFL7sa1leow==} + unicode-properties@1.4.1: + resolution: {integrity: sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==} + + unicode-trie@2.0.0: + resolution: {integrity: sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==} + unicorn-magic@0.1.0: resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} engines: {node: '>=18'} @@ -11794,6 +12071,9 @@ packages: unified@11.0.5: resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + unifont@0.6.0: + resolution: {integrity: sha512-5Fx50fFQMQL5aeHyWnZX9122sSLckcDvcfFiBf3QYeHa7a1MKJooUy52b67moi2MJYkrfo/TWY+CoLdr/w0tTA==} + unimport@5.5.0: resolution: {integrity: sha512-/JpWMG9s1nBSlXJAQ8EREFTFy3oy6USFd8T6AoBaw1q2GGcF4R9yp3ofg32UODZlYEO5VD0EWE1RpI9XDWyPYg==} engines: {node: '>=18.12.0'} @@ -11804,6 +12084,9 @@ packages: unist-util-is@6.0.0: resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} + unist-util-modify-children@4.0.0: + resolution: {integrity: sha512-+tdN5fGNddvsQdIzUF3Xx82CU9sMM+fA0dLgR9vOmT0oPT2jH+P1nd5lSqfCfXAw+93NhcXNY2qqvTUtE4cQkw==} + unist-util-position-from-estree@2.0.0: resolution: {integrity: sha512-KaFVRjoqLyF6YXCbVLNad/eS4+OfPQQn2yOd7zF/h5T/CSL2v8NpN6a5TPvtbXthAGw5nG+PuTtq+DdIZr+cRQ==} @@ -11816,9 +12099,15 @@ packages: unist-util-stringify-position@4.0.0: resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + unist-util-visit-children@3.0.0: + resolution: {integrity: sha512-RgmdTfSBOg04sdPcpTSD1jzoNBjt9a80/ZCzp5cI9n1qPzLZWF9YdvWGN2zmTumP1HWhXKdUWexjy/Wy/lJ7tA==} + unist-util-visit-parents@6.0.1: resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + unist-util-visit@5.0.0: resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} @@ -11936,6 +12225,68 @@ packages: uploadthing: optional: true + unstorage@1.17.3: + resolution: {integrity: sha512-i+JYyy0DoKmQ3FximTHbGadmIYb8JEpq7lxUjnjeB702bCPum0vzo6oy5Mfu0lpqISw7hCyMW2yj4nWC8bqJ3Q==} + peerDependencies: + '@azure/app-configuration': ^1.8.0 + '@azure/cosmos': ^4.2.0 + '@azure/data-tables': ^13.3.0 + '@azure/identity': ^4.6.0 + '@azure/keyvault-secrets': ^4.9.0 + '@azure/storage-blob': ^12.26.0 + '@capacitor/preferences': ^6.0.3 || ^7.0.0 + '@deno/kv': '>=0.9.0' + '@netlify/blobs': ^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0 + '@planetscale/database': ^1.19.0 + '@upstash/redis': ^1.34.3 + '@vercel/blob': '>=0.27.1' + '@vercel/functions': ^2.2.12 || ^3.0.0 + '@vercel/kv': ^1.0.1 + aws4fetch: ^1.0.20 + db0: '>=0.2.1' + idb-keyval: ^6.2.1 + ioredis: ^5.4.2 + uploadthing: ^7.4.4 + peerDependenciesMeta: + '@azure/app-configuration': + optional: true + '@azure/cosmos': + optional: true + '@azure/data-tables': + optional: true + '@azure/identity': + optional: true + '@azure/keyvault-secrets': + optional: true + '@azure/storage-blob': + optional: true + '@capacitor/preferences': + optional: true + '@deno/kv': + optional: true + '@netlify/blobs': + optional: true + '@planetscale/database': + optional: true + '@upstash/redis': + optional: true + '@vercel/blob': + optional: true + '@vercel/functions': + optional: true + '@vercel/kv': + optional: true + aws4fetch: + optional: true + db0: + optional: true + idb-keyval: + optional: true + ioredis: + optional: true + uploadthing: + optional: true + unstorage@2.0.0-alpha.4: resolution: {integrity: sha512-ywXZMZRfrvmO1giJeMTCw6VUn0ALYxVl8pFqJPStiyQUvgJImejtAHrKvXPj4QGJAoS/iLGcVGF6ljN/lkh1bw==} peerDependencies: @@ -12215,6 +12566,46 @@ packages: vite: ^6.0.0 || ^7.0.0 vue: ^3.5.0 + vite@6.4.1: + resolution: {integrity: sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + 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 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + vite@7.1.11: resolution: {integrity: sha512-uzcxnSDVjAopEUjljkWh8EIrg6tlzrjFUfMcR1EVsRDGwf/ccef0qQPRyOrROwhrTDaApueq+ja+KLPlzR/zdg==} engines: {node: ^20.19.0 || >=22.12.0} @@ -12408,6 +12799,10 @@ packages: whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + which-pm-runs@1.1.0: + resolution: {integrity: sha512-n1brCuqClxfFfq/Rb0ICg9giSZqCS+pLtccdag6C2HyufBrh3fBOiy9nb6ggRMvWOVH5GrdJskj5iGTZNxd7SA==} + engines: {node: '>=4'} + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -12500,6 +12895,9 @@ packages: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} + xxhash-wasm@1.1.0: + resolution: {integrity: sha512-147y/6YNh+tlp6nd/2pWq38i9h6mz/EuQ6njIrmW8D1BS5nCqs0P6DG+m6zTGnNz5I+uhZ0SHxBs9BsPrwcKDA==} + y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} @@ -12540,6 +12938,10 @@ packages: resolution: {integrity: sha512-AyeEbWOu/TAXdxlV9wmGcR0+yh2j3vYPGOECcIj2S7MkrLyC7ne+oye2BKTItt0ii2PHk4cDy+95+LshzbXnGg==} engines: {node: '>=12.20'} + yocto-spinner@0.2.3: + resolution: {integrity: sha512-sqBChb33loEnkoXte1bLg45bEBsOP9N1kzQh5JZNKj/0rik4zAPTNSAVPj3uQAdc6slYJ0Ksc403G2XgxsJQFQ==} + engines: {node: '>=18.19'} + yoctocolors@2.1.2: resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} engines: {node: '>=18'} @@ -12564,6 +12966,17 @@ packages: resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==} engines: {node: '>= 14'} + zod-to-json-schema@3.25.0: + resolution: {integrity: sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ==} + peerDependencies: + zod: ^3.25 || ^4 + + zod-to-ts@1.2.0: + resolution: {integrity: sha512-x30XE43V+InwGpvTySRNz9kB7qFU8DlyEy7BsSTCHPH1R0QasMmHWZDCzYm6bVXtj/9NNJAZF3jW8rzFvH5OFA==} + peerDependencies: + typescript: ^4.9.4 || ^5.0.2 + zod: ^3 + zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -12630,23 +13043,103 @@ snapshots: '@antfu/utils@9.3.0': {} - '@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.840.0 - '@aws-sdk/util-locate-window': 3.893.0 - '@smithy/util-utf8': 2.3.0 - tslib: 2.8.1 + '@astrojs/compiler@2.13.0': {} - '@aws-crypto/sha256-js@5.2.0': - dependencies: - '@aws-crypto/util': 5.2.0 - '@aws-sdk/types': 3.840.0 - tslib: 2.8.1 + '@astrojs/internal-helpers@0.7.4': {} - '@aws-crypto/supports-web-crypto@5.2.0': + '@astrojs/internal-helpers@0.7.5': {} + + '@astrojs/markdown-remark@6.3.9': + dependencies: + '@astrojs/internal-helpers': 0.7.5 + '@astrojs/prism': 3.3.0 + github-slugger: 2.0.0 + hast-util-from-html: 2.0.3 + hast-util-to-text: 4.0.2 + import-meta-resolve: 4.2.0 + js-yaml: 4.1.1 + mdast-util-definitions: 6.0.0 + rehype-raw: 7.0.0 + rehype-stringify: 10.0.1 + remark-gfm: 4.0.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + remark-smartypants: 3.0.2 + shiki: 3.13.0 + smol-toml: 1.5.2 + unified: 11.0.5 + unist-util-remove-position: 5.0.0 + unist-util-visit: 5.0.0 + unist-util-visit-parents: 6.0.2 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + '@astrojs/node@9.5.0(astro@5.16.0(@netlify/blobs@9.1.2)(@types/node@24.6.2)(@vercel/functions@3.1.4(@aws-sdk/credential-provider-web-identity@3.844.0))(db0@0.3.4(better-sqlite3@11.10.0)(drizzle-orm@0.44.7(@opentelemetry/api@1.9.0)(better-sqlite3@11.10.0)(pg@8.16.3)(postgres@3.4.7)))(ioredis@5.8.2)(jiti@2.6.1)(lightningcss@1.30.1)(rollup@4.53.2)(terser@5.44.0)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1))': + dependencies: + '@astrojs/internal-helpers': 0.7.4 + astro: 5.16.0(@netlify/blobs@9.1.2)(@types/node@24.6.2)(@vercel/functions@3.1.4(@aws-sdk/credential-provider-web-identity@3.844.0))(db0@0.3.4(better-sqlite3@11.10.0)(drizzle-orm@0.44.7(@opentelemetry/api@1.9.0)(better-sqlite3@11.10.0)(pg@8.16.3)(postgres@3.4.7)))(ioredis@5.8.2)(jiti@2.6.1)(lightningcss@1.30.1)(rollup@4.53.2)(terser@5.44.0)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1) + send: 1.2.0 + server-destroy: 1.0.1 + transitivePeerDependencies: + - supports-color + + '@astrojs/prism@3.3.0': + dependencies: + prismjs: 1.30.0 + + '@astrojs/telemetry@3.3.0': + dependencies: + ci-info: 4.3.1 + debug: 4.4.3(supports-color@8.1.1) + dlv: 1.1.3 + dset: 3.1.4 + is-docker: 3.0.0 + is-wsl: 3.1.0 + which-pm-runs: 1.1.0 + transitivePeerDependencies: + - supports-color + + '@astrojs/vercel@9.0.1(@aws-sdk/credential-provider-web-identity@3.844.0)(@sveltejs/kit@2.48.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.1.12(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(svelte@5.43.3)(vite@7.1.12(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(astro@5.16.0(@netlify/blobs@9.1.2)(@types/node@24.6.2)(@vercel/functions@3.1.4(@aws-sdk/credential-provider-web-identity@3.844.0))(db0@0.3.4(better-sqlite3@11.10.0)(drizzle-orm@0.44.7(@opentelemetry/api@1.9.0)(better-sqlite3@11.10.0)(pg@8.16.3)(postgres@3.4.7)))(ioredis@5.8.2)(jiti@2.6.1)(lightningcss@1.30.1)(rollup@4.53.2)(terser@5.44.0)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1))(next@16.0.3(@opentelemetry/api@1.9.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)(rollup@4.53.2)(svelte@5.43.3)(vue-router@4.6.3(vue@3.5.22(typescript@5.9.3)))(vue@3.5.22(typescript@5.9.3))': + dependencies: + '@astrojs/internal-helpers': 0.7.5 + '@vercel/analytics': 1.5.0(@sveltejs/kit@2.48.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.1.12(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(svelte@5.43.3)(vite@7.1.12(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(next@16.0.3(@opentelemetry/api@1.9.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)(svelte@5.43.3)(vue-router@4.6.3(vue@3.5.22(typescript@5.9.3)))(vue@3.5.22(typescript@5.9.3)) + '@vercel/functions': 2.2.13(@aws-sdk/credential-provider-web-identity@3.844.0) + '@vercel/nft': 0.30.3(rollup@4.53.2) + '@vercel/routing-utils': 5.3.0 + astro: 5.16.0(@netlify/blobs@9.1.2)(@types/node@24.6.2)(@vercel/functions@3.1.4(@aws-sdk/credential-provider-web-identity@3.844.0))(db0@0.3.4(better-sqlite3@11.10.0)(drizzle-orm@0.44.7(@opentelemetry/api@1.9.0)(better-sqlite3@11.10.0)(pg@8.16.3)(postgres@3.4.7)))(ioredis@5.8.2)(jiti@2.6.1)(lightningcss@1.30.1)(rollup@4.53.2)(terser@5.44.0)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1) + esbuild: 0.25.12 + tinyglobby: 0.2.15 + transitivePeerDependencies: + - '@aws-sdk/credential-provider-web-identity' + - '@remix-run/react' + - '@sveltejs/kit' + - encoding + - next + - react + - rollup + - supports-color + - svelte + - vue + - vue-router + + '@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.840.0 + '@aws-sdk/util-locate-window': 3.893.0 + '@smithy/util-utf8': 2.3.0 + tslib: 2.8.1 + + '@aws-crypto/sha256-js@5.2.0': + dependencies: + '@aws-crypto/util': 5.2.0 + '@aws-sdk/types': 3.840.0 + tslib: 2.8.1 + + '@aws-crypto/supports-web-crypto@5.2.0': dependencies: tslib: 2.8.1 @@ -13211,6 +13704,10 @@ snapshots: '@braintree/sanitize-url@7.1.1': {} + '@capsizecss/unpack@3.0.1': + dependencies: + fontkit: 2.0.4 + '@cbor-extract/cbor-extract-darwin-arm64@2.2.0': optional: true @@ -13770,7 +14267,7 @@ snapshots: globals: 14.0.0 ignore: 5.3.2 import-fresh: 3.3.1 - js-yaml: 4.1.0 + js-yaml: 4.1.1 minimatch: 3.1.2 strip-json-comments: 3.1.1 transitivePeerDependencies: @@ -14744,7 +15241,7 @@ snapshots: consola: 3.4.2 cssnano: 7.1.1(postcss@8.5.6) defu: 6.1.4 - esbuild: 0.25.11 + esbuild: 0.25.12 escape-string-regexp: 5.0.0 exsolve: 1.0.7 get-port-please: 3.2.0 @@ -15019,6 +15516,8 @@ snapshots: '@orama/orama@3.1.16': {} + '@oslojs/encoding@1.1.0': {} + '@oven/bun-darwin-aarch64@1.3.0': optional: true @@ -17678,6 +18177,10 @@ snapshots: '@types/express-serve-static-core': 5.1.0 '@types/serve-static': 1.15.10 + '@types/fontkit@2.0.8': + dependencies: + '@types/node': 24.6.2 + '@types/fs-extra@11.0.4': dependencies: '@types/jsonfile': 6.1.4 @@ -17726,6 +18229,10 @@ snapshots: '@types/ms@2.1.0': {} + '@types/nlcst@2.0.3': + dependencies: + '@types/unist': 3.0.3 + '@types/node@12.20.55': {} '@types/node@18.19.130': @@ -17866,6 +18373,15 @@ snapshots: vue: 3.5.22(typescript@5.9.3) vue-router: 4.6.3(vue@3.5.22(typescript@5.9.3)) + '@vercel/analytics@1.5.0(@sveltejs/kit@2.48.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.1.12(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(svelte@5.43.3)(vite@7.1.12(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(next@16.0.3(@opentelemetry/api@1.9.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)(svelte@5.43.3)(vue-router@4.6.3(vue@3.5.22(typescript@5.9.3)))(vue@3.5.22(typescript@5.9.3))': + optionalDependencies: + '@sveltejs/kit': 2.48.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.1.12(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(svelte@5.43.3)(vite@7.1.12(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + next: 16.0.3(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0) + react: 19.2.0 + svelte: 5.43.3 + vue: 3.5.22(typescript@5.9.3) + vue-router: 4.6.3(vue@3.5.22(typescript@5.9.3)) + '@vercel/edge-config-fs@0.1.0': {} '@vercel/edge-config@1.4.0(@opentelemetry/api@1.9.0)': @@ -17874,6 +18390,12 @@ snapshots: optionalDependencies: '@opentelemetry/api': 1.9.0 + '@vercel/functions@2.2.13(@aws-sdk/credential-provider-web-identity@3.844.0)': + dependencies: + '@vercel/oidc': 2.0.2 + optionalDependencies: + '@aws-sdk/credential-provider-web-identity': 3.844.0 + '@vercel/functions@3.1.4(@aws-sdk/credential-provider-web-identity@3.609.0(@aws-sdk/client-sts@3.844.0))': dependencies: '@vercel/oidc': 3.0.3 @@ -17924,6 +18446,11 @@ snapshots: - rollup - supports-color + '@vercel/oidc@2.0.2': + dependencies: + '@types/ms': 2.1.0 + ms: 2.1.3 + '@vercel/oidc@3.0.3': {} '@vercel/oidc@3.0.5': {} @@ -17943,6 +18470,13 @@ snapshots: '@vercel/oidc': 3.0.5 mixpart: 0.0.5-alpha.1 + '@vercel/routing-utils@5.3.0': + dependencies: + path-to-regexp: 6.3.0 + path-to-regexp-updated: path-to-regexp@6.3.0 + optionalDependencies: + ajv: 6.12.6 + '@vercel/speed-insights@1.2.0(@sveltejs/kit@2.48.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.1.12(@types/node@22.19.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(svelte@5.43.3)(vite@7.1.12(@types/node@22.19.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(next@16.0.3(@babel/core@7.28.4)(@opentelemetry/api@1.9.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react@19.2.0)(svelte@5.43.3)(vue-router@4.6.3(vue@3.5.22(typescript@5.9.3)))(vue@3.5.22(typescript@5.9.3))': optionalDependencies: '@sveltejs/kit': 2.48.4(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.3)(vite@7.1.12(@types/node@22.19.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)))(svelte@5.43.3)(vite@7.1.12(@types/node@22.19.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) @@ -18341,6 +18875,8 @@ snapshots: aria-query@5.3.2: {} + array-iterate@2.0.1: {} + array-timsort@1.0.3: {} array-union@2.1.0: {} @@ -18371,6 +18907,210 @@ snapshots: astring@1.9.0: {} + astro@5.16.0(@netlify/blobs@9.1.2)(@types/node@22.19.0)(@vercel/functions@3.1.4(@aws-sdk/credential-provider-web-identity@3.844.0))(db0@0.3.4(better-sqlite3@11.10.0)(drizzle-orm@0.44.7(@opentelemetry/api@1.9.0)(better-sqlite3@11.10.0)(pg@8.16.3)(postgres@3.4.7)))(ioredis@5.8.2)(jiti@2.6.1)(lightningcss@1.30.1)(rollup@4.53.2)(terser@5.44.0)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1): + dependencies: + '@astrojs/compiler': 2.13.0 + '@astrojs/internal-helpers': 0.7.5 + '@astrojs/markdown-remark': 6.3.9 + '@astrojs/telemetry': 3.3.0 + '@capsizecss/unpack': 3.0.1 + '@oslojs/encoding': 1.1.0 + '@rollup/pluginutils': 5.3.0(rollup@4.53.2) + acorn: 8.15.0 + aria-query: 5.3.2 + axobject-query: 4.1.0 + boxen: 8.0.1 + ci-info: 4.3.1 + clsx: 2.1.1 + common-ancestor-path: 1.0.1 + cookie: 1.0.2 + cssesc: 3.0.0 + debug: 4.4.3(supports-color@8.1.1) + deterministic-object-hash: 2.0.2 + devalue: 5.5.0 + diff: 5.2.0 + dlv: 1.1.3 + dset: 3.1.4 + es-module-lexer: 1.7.0 + esbuild: 0.25.12 + estree-walker: 3.0.3 + flattie: 1.1.1 + fontace: 0.3.1 + github-slugger: 2.0.0 + html-escaper: 3.0.3 + http-cache-semantics: 4.2.0 + import-meta-resolve: 4.2.0 + js-yaml: 4.1.1 + magic-string: 0.30.21 + magicast: 0.5.1 + mrmime: 2.0.1 + neotraverse: 0.6.18 + p-limit: 6.2.0 + p-queue: 8.1.1 + package-manager-detector: 1.5.0 + piccolore: 0.1.3 + picomatch: 4.0.3 + prompts: 2.4.2 + rehype: 13.0.2 + semver: 7.7.3 + shiki: 3.15.0 + smol-toml: 1.5.2 + svgo: 4.0.0 + tinyexec: 1.0.2 + tinyglobby: 0.2.15 + tsconfck: 3.1.6(typescript@5.9.3) + ultrahtml: 1.6.0 + unifont: 0.6.0 + unist-util-visit: 5.0.0 + unstorage: 1.17.3(@netlify/blobs@9.1.2)(@vercel/functions@3.1.4(@aws-sdk/credential-provider-web-identity@3.844.0))(db0@0.3.4(better-sqlite3@11.10.0)(drizzle-orm@0.44.7(@opentelemetry/api@1.9.0)(better-sqlite3@11.10.0)(pg@8.16.3)(postgres@3.4.7)))(ioredis@5.8.2) + vfile: 6.0.3 + vite: 6.4.1(@types/node@22.19.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) + vitefu: 1.1.1(vite@6.4.1(@types/node@22.19.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + xxhash-wasm: 1.1.0 + yargs-parser: 21.1.1 + yocto-spinner: 0.2.3 + zod: 3.25.76 + zod-to-json-schema: 3.25.0(zod@3.25.76) + zod-to-ts: 1.2.0(typescript@5.9.3)(zod@3.25.76) + optionalDependencies: + sharp: 0.34.4 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@types/node' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - idb-keyval + - ioredis + - jiti + - less + - lightningcss + - rollup + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - typescript + - uploadthing + - yaml + + astro@5.16.0(@netlify/blobs@9.1.2)(@types/node@24.6.2)(@vercel/functions@3.1.4(@aws-sdk/credential-provider-web-identity@3.844.0))(db0@0.3.4(better-sqlite3@11.10.0)(drizzle-orm@0.44.7(@opentelemetry/api@1.9.0)(better-sqlite3@11.10.0)(pg@8.16.3)(postgres@3.4.7)))(ioredis@5.8.2)(jiti@2.6.1)(lightningcss@1.30.1)(rollup@4.53.2)(terser@5.44.0)(tsx@4.20.6)(typescript@5.9.3)(yaml@2.8.1): + dependencies: + '@astrojs/compiler': 2.13.0 + '@astrojs/internal-helpers': 0.7.5 + '@astrojs/markdown-remark': 6.3.9 + '@astrojs/telemetry': 3.3.0 + '@capsizecss/unpack': 3.0.1 + '@oslojs/encoding': 1.1.0 + '@rollup/pluginutils': 5.3.0(rollup@4.53.2) + acorn: 8.15.0 + aria-query: 5.3.2 + axobject-query: 4.1.0 + boxen: 8.0.1 + ci-info: 4.3.1 + clsx: 2.1.1 + common-ancestor-path: 1.0.1 + cookie: 1.0.2 + cssesc: 3.0.0 + debug: 4.4.3(supports-color@8.1.1) + deterministic-object-hash: 2.0.2 + devalue: 5.5.0 + diff: 5.2.0 + dlv: 1.1.3 + dset: 3.1.4 + es-module-lexer: 1.7.0 + esbuild: 0.25.12 + estree-walker: 3.0.3 + flattie: 1.1.1 + fontace: 0.3.1 + github-slugger: 2.0.0 + html-escaper: 3.0.3 + http-cache-semantics: 4.2.0 + import-meta-resolve: 4.2.0 + js-yaml: 4.1.1 + magic-string: 0.30.21 + magicast: 0.5.1 + mrmime: 2.0.1 + neotraverse: 0.6.18 + p-limit: 6.2.0 + p-queue: 8.1.1 + package-manager-detector: 1.5.0 + piccolore: 0.1.3 + picomatch: 4.0.3 + prompts: 2.4.2 + rehype: 13.0.2 + semver: 7.7.3 + shiki: 3.15.0 + smol-toml: 1.5.2 + svgo: 4.0.0 + tinyexec: 1.0.2 + tinyglobby: 0.2.15 + tsconfck: 3.1.6(typescript@5.9.3) + ultrahtml: 1.6.0 + unifont: 0.6.0 + unist-util-visit: 5.0.0 + unstorage: 1.17.3(@netlify/blobs@9.1.2)(@vercel/functions@3.1.4(@aws-sdk/credential-provider-web-identity@3.844.0))(db0@0.3.4(better-sqlite3@11.10.0)(drizzle-orm@0.44.7(@opentelemetry/api@1.9.0)(better-sqlite3@11.10.0)(pg@8.16.3)(postgres@3.4.7)))(ioredis@5.8.2) + vfile: 6.0.3 + vite: 6.4.1(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) + vitefu: 1.1.1(vite@6.4.1(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)) + xxhash-wasm: 1.1.0 + yargs-parser: 21.1.1 + yocto-spinner: 0.2.3 + zod: 3.25.76 + zod-to-json-schema: 3.25.0(zod@3.25.76) + zod-to-ts: 1.2.0(typescript@5.9.3)(zod@3.25.76) + optionalDependencies: + sharp: 0.34.4 + transitivePeerDependencies: + - '@azure/app-configuration' + - '@azure/cosmos' + - '@azure/data-tables' + - '@azure/identity' + - '@azure/keyvault-secrets' + - '@azure/storage-blob' + - '@capacitor/preferences' + - '@deno/kv' + - '@netlify/blobs' + - '@planetscale/database' + - '@types/node' + - '@upstash/redis' + - '@vercel/blob' + - '@vercel/functions' + - '@vercel/kv' + - aws4fetch + - db0 + - idb-keyval + - ioredis + - jiti + - less + - lightningcss + - rollup + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - typescript + - uploadthing + - yaml + async-lock@1.4.1: {} async-sema@3.1.1: {} @@ -18432,6 +19172,8 @@ snapshots: bare-path: 3.0.0 optional: true + base-64@1.0.0: {} + base64-js@1.5.1: {} baseline-browser-mapping@2.8.19: {} @@ -18521,6 +19263,10 @@ snapshots: dependencies: fill-range: 7.1.1 + brotli@1.3.3: + dependencies: + base64-js: 1.5.1 + browserslist@4.27.0: dependencies: baseline-browser-mapping: 2.8.19 @@ -18697,6 +19443,8 @@ snapshots: ci-info@3.9.0: {} + ci-info@4.3.1: {} + citty@0.1.6: dependencies: consola: 3.4.2 @@ -18748,6 +19496,8 @@ snapshots: clone@1.0.4: optional: true + clone@2.1.2: {} + cloudflare-video-element@1.3.4: {} clsx@2.1.1: {} @@ -18821,6 +19571,8 @@ snapshots: has-own-prop: 2.0.0 repeat-string: 1.6.1 + common-ancestor-path@1.0.1: {} + common-path-prefix@3.0.0: {} commondir@1.0.1: {} @@ -19352,6 +20104,10 @@ snapshots: transitivePeerDependencies: - supports-color + deterministic-object-hash@2.0.2: + dependencies: + base-64: 1.0.0 + devalue@5.5.0: {} devlop@1.1.0: @@ -19366,12 +20122,18 @@ snapshots: dexie@4.2.1: {} + dfa@1.2.0: {} + + diff@5.2.0: {} + diff@8.0.2: {} dir-glob@3.0.1: dependencies: path-type: 4.0.0 + dlv@1.1.3: {} + docker-compose@1.3.0: dependencies: yaml: 2.8.1 @@ -19454,6 +20216,8 @@ snapshots: pg: 8.16.3 postgres: 3.4.7 + dset@3.1.4: {} + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 @@ -20045,8 +20809,27 @@ snapshots: flatted@3.3.3: optional: true + flattie@1.1.1: {} + fn.name@1.1.0: {} + fontace@0.3.1: + dependencies: + '@types/fontkit': 2.0.8 + fontkit: 2.0.4 + + fontkit@2.0.4: + dependencies: + '@swc/helpers': 0.5.15 + brotli: 1.3.3 + clone: 2.1.2 + dfa: 1.2.0 + fast-deep-equal: 3.1.3 + restructure: 3.0.2 + tiny-inflate: 1.0.3 + unicode-properties: 1.4.1 + unicode-trie: 2.0.0 + foreground-child@3.3.1: dependencies: cross-spawn: 7.0.6 @@ -20107,7 +20890,7 @@ snapshots: image-size: 2.0.2 negotiator: 1.0.0 npm-to-yarn: 3.0.1 - path-to-regexp: 8.3.0 + path-to-regexp: 6.3.0 remark: 15.0.1 remark-gfm: 4.0.1 remark-rehype: 11.1.2 @@ -20136,7 +20919,7 @@ snapshots: image-size: 2.0.2 negotiator: 1.0.0 npm-to-yarn: 3.0.1 - path-to-regexp: 8.3.0 + path-to-regexp: 6.3.0 remark: 15.0.1 remark-gfm: 4.0.1 remark-rehype: 11.1.2 @@ -20592,10 +21375,14 @@ snapshots: html-escaper@2.0.2: {} + html-escaper@3.0.3: {} + html-url-attributes@3.0.1: {} html-void-elements@3.0.0: {} + http-cache-semantics@4.2.0: {} + http-errors@2.0.0: dependencies: depd: 2.0.0 @@ -20659,6 +21446,8 @@ snapshots: cjs-module-lexer: 1.4.3 module-details-from-path: 1.0.4 + import-meta-resolve@4.2.0: {} + impound@1.0.0: dependencies: exsolve: 1.0.7 @@ -20889,6 +21678,10 @@ snapshots: dependencies: argparse: 2.0.1 + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + jsesc@3.1.0: {} json-buffer@3.0.1: @@ -21207,6 +22000,12 @@ snapshots: '@babel/types': 7.28.5 source-map-js: 1.2.1 + magicast@0.5.1: + dependencies: + '@babel/parser': 7.28.5 + '@babel/types': 7.28.5 + source-map-js: 1.2.1 + make-dir@4.0.0: dependencies: semver: 7.7.3 @@ -21219,6 +22018,12 @@ snapshots: math-intrinsics@1.1.0: {} + mdast-util-definitions@6.0.0: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + unist-util-visit: 5.0.0 + mdast-util-find-and-replace@3.0.2: dependencies: '@types/mdast': 4.0.4 @@ -21866,6 +22671,8 @@ snapshots: negotiator@1.0.0: {} + neotraverse@0.6.18: {} + netlify@13.3.5: dependencies: '@netlify/open-api': 2.43.1 @@ -22296,6 +23103,10 @@ snapshots: - supports-color - uploadthing + nlcst-to-string@4.0.0: + dependencies: + '@types/nlcst': 2.0.3 + node-abi@3.78.0: dependencies: semver: 7.7.3 @@ -22716,6 +23527,11 @@ snapshots: ws: 8.18.3 zod: 4.1.11 + openai@6.9.0(ws@8.18.3)(zod@4.1.11): + optionalDependencies: + ws: 8.18.3 + zod: 4.1.11 + optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -22924,6 +23740,10 @@ snapshots: dependencies: yocto-queue: 1.2.1 + p-limit@6.2.0: + dependencies: + yocto-queue: 1.2.1 + p-locate@4.1.0: dependencies: p-limit: 2.3.0 @@ -22941,6 +23761,11 @@ snapshots: p-map@7.0.4: {} + p-queue@8.1.1: + dependencies: + eventemitter3: 5.0.1 + p-timeout: 6.1.4 + p-timeout@6.1.4: {} p-try@2.2.0: {} @@ -22957,6 +23782,8 @@ snapshots: package-manager-detector@1.5.0: {} + pako@0.2.9: {} + parent-module@1.0.1: dependencies: callsites: 3.1.0 @@ -22990,6 +23817,15 @@ snapshots: index-to-position: 1.2.0 type-fest: 4.41.0 + parse-latin@7.0.0: + dependencies: + '@types/nlcst': 2.0.3 + '@types/unist': 3.0.3 + nlcst-to-string: 4.0.0 + unist-util-modify-children: 4.0.0 + unist-util-visit-children: 3.0.0 + vfile: 6.0.3 + parse-ms@4.0.0: {} parse-path@7.1.0: @@ -23028,7 +23864,7 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.2 - path-to-regexp@8.3.0: {} + path-to-regexp@6.3.0: {} path-type@4.0.0: {} @@ -23089,6 +23925,8 @@ snapshots: dependencies: split2: 4.2.0 + piccolore@0.1.3: {} + picocolors@1.1.1: {} picomatch@2.3.1: {} @@ -23365,6 +24203,8 @@ snapshots: dependencies: parse-ms: 4.0.0 + prismjs@1.30.0: {} + process-nextick-args@2.0.1: {} process@0.11.10: {} @@ -23825,6 +24665,12 @@ snapshots: unist-util-visit-parents: 6.0.1 vfile: 6.0.3 + rehype-parse@9.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-from-html: 2.0.3 + unified: 11.0.5 + rehype-raw@7.0.0: dependencies: '@types/hast': 3.0.4 @@ -23839,6 +24685,19 @@ snapshots: transitivePeerDependencies: - supports-color + rehype-stringify@10.0.1: + dependencies: + '@types/hast': 3.0.4 + hast-util-to-html: 9.0.5 + unified: 11.0.5 + + rehype@13.0.2: + dependencies: + '@types/hast': 3.0.4 + rehype-parse: 9.0.1 + rehype-stringify: 10.0.1 + unified: 11.0.5 + remark-gfm@4.0.1: dependencies: '@types/mdast': 4.0.4 @@ -23883,6 +24742,13 @@ snapshots: unified: 11.0.5 vfile: 6.0.3 + remark-smartypants@3.0.2: + dependencies: + retext: 9.0.0 + retext-smartypants: 6.2.0 + unified: 11.0.5 + unist-util-visit: 5.0.0 + remark-stringify@11.0.0: dependencies: '@types/mdast': 4.0.4 @@ -23937,6 +24803,33 @@ snapshots: onetime: 7.0.0 signal-exit: 4.1.0 + restructure@3.0.2: {} + + retext-latin@4.0.0: + dependencies: + '@types/nlcst': 2.0.3 + parse-latin: 7.0.0 + unified: 11.0.5 + + retext-smartypants@6.2.0: + dependencies: + '@types/nlcst': 2.0.3 + nlcst-to-string: 4.0.0 + unist-util-visit: 5.0.0 + + retext-stringify@4.0.0: + dependencies: + '@types/nlcst': 2.0.3 + nlcst-to-string: 4.0.0 + unified: 11.0.5 + + retext@9.0.0: + dependencies: + '@types/nlcst': 2.0.3 + retext-latin: 4.0.0 + retext-stringify: 4.0.0 + unified: 11.0.5 + retry@0.12.0: {} reusify@1.1.0: {} @@ -24005,7 +24898,7 @@ snapshots: depd: 2.0.0 is-promise: 4.0.0 parseurl: 1.3.3 - path-to-regexp: 8.3.0 + path-to-regexp: 6.3.0 transitivePeerDependencies: - supports-color @@ -24093,6 +24986,8 @@ snapshots: transitivePeerDependencies: - supports-color + server-destroy@1.0.1: {} + set-cookie-parser@2.7.2: {} setprototypeof@1.2.0: {} @@ -24235,6 +25130,8 @@ snapshots: smob@1.5.0: {} + smol-toml@1.5.2: {} + sonner@2.0.7(react-dom@19.1.0(react@19.1.0))(react@19.1.0): dependencies: react: 19.1.0 @@ -24637,6 +25534,8 @@ snapshots: tiktok-video-element@0.1.1: {} + tiny-inflate@1.0.3: {} + tiny-invariant@1.3.3: {} tinybench@2.9.0: {} @@ -24863,6 +25762,16 @@ snapshots: dependencies: hookable: 5.5.3 + unicode-properties@1.4.1: + dependencies: + base64-js: 1.5.1 + unicode-trie: 2.0.0 + + unicode-trie@2.0.0: + dependencies: + pako: 0.2.9 + tiny-inflate: 1.0.3 + unicorn-magic@0.1.0: {} unicorn-magic@0.3.0: {} @@ -24877,6 +25786,12 @@ snapshots: trough: 2.2.0 vfile: 6.0.3 + unifont@0.6.0: + dependencies: + css-tree: 3.1.0 + ofetch: 1.5.1 + ohash: 2.0.11 + unimport@5.5.0: dependencies: acorn: 8.15.0 @@ -24903,6 +25818,11 @@ snapshots: dependencies: '@types/unist': 3.0.3 + unist-util-modify-children@4.0.0: + dependencies: + '@types/unist': 3.0.3 + array-iterate: 2.0.1 + unist-util-position-from-estree@2.0.0: dependencies: '@types/unist': 3.0.3 @@ -24920,11 +25840,20 @@ snapshots: dependencies: '@types/unist': 3.0.3 + unist-util-visit-children@3.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-visit-parents@6.0.1: dependencies: '@types/unist': 3.0.3 unist-util-is: 6.0.0 + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + unist-util-visit@5.0.0: dependencies: '@types/unist': 3.0.3 @@ -25025,6 +25954,22 @@ snapshots: db0: 0.3.4(better-sqlite3@11.10.0)(drizzle-orm@0.44.7(@opentelemetry/api@1.9.0)(better-sqlite3@11.10.0)(pg@8.16.3)(postgres@3.4.7)) ioredis: 5.8.2 + unstorage@1.17.3(@netlify/blobs@9.1.2)(@vercel/functions@3.1.4(@aws-sdk/credential-provider-web-identity@3.844.0))(db0@0.3.4(better-sqlite3@11.10.0)(drizzle-orm@0.44.7(@opentelemetry/api@1.9.0)(better-sqlite3@11.10.0)(pg@8.16.3)(postgres@3.4.7)))(ioredis@5.8.2): + dependencies: + anymatch: 3.1.3 + chokidar: 4.0.3 + destr: 2.0.5 + h3: 1.15.4 + lru-cache: 10.4.3 + node-fetch-native: 1.6.7 + ofetch: 1.5.1 + ufo: 1.6.1 + optionalDependencies: + '@netlify/blobs': 9.1.2 + '@vercel/functions': 3.1.4(@aws-sdk/credential-provider-web-identity@3.844.0) + db0: 0.3.4(better-sqlite3@11.10.0)(drizzle-orm@0.44.7(@opentelemetry/api@1.9.0)(better-sqlite3@11.10.0)(pg@8.16.3)(postgres@3.4.7)) + ioredis: 5.8.2 + unstorage@2.0.0-alpha.4(@netlify/blobs@9.1.2)(@vercel/functions@3.1.4(@aws-sdk/credential-provider-web-identity@3.844.0))(chokidar@4.0.3)(db0@0.3.4(better-sqlite3@11.10.0)(drizzle-orm@0.44.7(@opentelemetry/api@1.9.0)(better-sqlite3@11.10.0)(pg@8.16.3)(postgres@3.4.7)))(ioredis@5.8.2)(lru-cache@11.2.2)(ofetch@2.0.0-alpha.3): optionalDependencies: '@netlify/blobs': 9.1.2 @@ -25298,9 +26243,43 @@ snapshots: vite: 7.1.12(@types/node@22.19.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) vue: 3.5.22(typescript@5.9.3) + vite@6.4.1(@types/node@22.19.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1): + dependencies: + esbuild: 0.25.12 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.53.2 + tinyglobby: 0.2.14 + optionalDependencies: + '@types/node': 22.19.0 + fsevents: 2.3.3 + jiti: 2.6.1 + lightningcss: 1.30.1 + terser: 5.44.0 + tsx: 4.20.6 + yaml: 2.8.1 + + vite@6.4.1(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1): + dependencies: + esbuild: 0.25.12 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.53.2 + tinyglobby: 0.2.14 + optionalDependencies: + '@types/node': 24.6.2 + fsevents: 2.3.3 + jiti: 2.6.1 + lightningcss: 1.30.1 + terser: 5.44.0 + tsx: 4.20.6 + yaml: 2.8.1 + vite@7.1.11(@types/node@22.19.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1): dependencies: - esbuild: 0.25.11 + esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.3) picomatch: 4.0.3 postcss: 8.5.6 @@ -25349,6 +26328,14 @@ snapshots: tsx: 4.20.6 yaml: 2.8.1 + vitefu@1.1.1(vite@6.4.1(@types/node@22.19.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)): + optionalDependencies: + vite: 6.4.1(@types/node@22.19.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) + + vitefu@1.1.1(vite@6.4.1(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)): + optionalDependencies: + vite: 6.4.1(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) + vitefu@1.1.1(vite@7.1.12(@types/node@22.19.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1)): optionalDependencies: vite: 7.1.12(@types/node@22.19.0)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.1) @@ -25514,6 +26501,8 @@ snapshots: tr46: 0.0.3 webidl-conversions: 3.0.1 + which-pm-runs@1.1.0: {} + which@2.0.2: dependencies: isexe: 2.0.0 @@ -25609,6 +26598,8 @@ snapshots: xtend@4.0.2: {} + xxhash-wasm@1.1.0: {} + y18n@5.0.8: {} yallist@3.1.1: {} @@ -25641,6 +26632,10 @@ snapshots: yocto-queue@1.2.1: {} + yocto-spinner@0.2.3: + dependencies: + yoctocolors: 2.1.2 + yoctocolors@2.1.2: {} youch-core@0.3.3: @@ -25674,6 +26669,15 @@ snapshots: compress-commons: 6.0.2 readable-stream: 4.7.0 + zod-to-json-schema@3.25.0(zod@3.25.76): + dependencies: + zod: 3.25.76 + + zod-to-ts@1.2.0(typescript@5.9.3)(zod@3.25.76): + dependencies: + typescript: 5.9.3 + zod: 3.25.76 + zod@3.25.76: {} zod@4.1.11: {} diff --git a/scripts/create-test-matrix.mjs b/scripts/create-test-matrix.mjs index 369ec344d..fb07f268c 100644 --- a/scripts/create-test-matrix.mjs +++ b/scripts/create-test-matrix.mjs @@ -49,6 +49,13 @@ const DEV_TEST_CONFIGS = { apiFilePath: './src/index.ts', apiFileImportPath: '..', }, + astro: { + generatedStepPath: 'src/pages/.well-known/workflow/v1/step.js', + generatedWorkflowPath: 'src/pages/.well-known/workflow/v1/flow.js', + apiFilePath: 'src/pages/api/chat.ts', + apiFileImportPath: '../..', + workflowsDir: 'src/workflows', + }, }; const matrix = { @@ -112,4 +119,10 @@ matrix.app.push({ ...DEV_TEST_CONFIGS.express, }); +matrix.app.push({ + name: 'astro', + project: 'workbench-astro-workflow', + ...DEV_TEST_CONFIGS.astro, +}); + console.log(JSON.stringify(matrix)); diff --git a/workbench/astro/.gitignore b/workbench/astro/.gitignore new file mode 100644 index 000000000..185adda60 --- /dev/null +++ b/workbench/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/ + +# Workflow +src/lib/_workflows.ts diff --git a/workbench/astro/.npmrc b/workbench/astro/.npmrc new file mode 100644 index 000000000..b6f27f135 --- /dev/null +++ b/workbench/astro/.npmrc @@ -0,0 +1 @@ +engine-strict=true diff --git a/workbench/astro/README.md b/workbench/astro/README.md new file mode 100644 index 000000000..87b813ae7 --- /dev/null +++ b/workbench/astro/README.md @@ -0,0 +1,43 @@ +# Astro Starter Kit: Minimal + +```sh +npm create astro@latest -- --template minimal +``` + +> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun! + +## 🚀 Project Structure + +Inside of your Astro project, you'll see the following folders and files: + +```text +/ +├── public/ +├── src/ +│ └── pages/ +│ └── index.astro +└── package.json +``` + +Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name. + +There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components. + +Any static assets, like images, can be placed in the `public/` directory. + +## 🧞 Commands + +All commands are run from the root of the project, from a terminal: + +| Command | Action | +| :------------------------ | :----------------------------------------------- | +| `npm install` | Installs dependencies | +| `npm run dev` | Starts local dev server at `localhost:4321` | +| `npm run build` | Build your production site to `./dist/` | +| `npm run preview` | Preview your build locally, before deploying | +| `npm run astro ...` | Run CLI commands like `astro add`, `astro check` | +| `npm run 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/workbench/astro/astro.config.mjs b/workbench/astro/astro.config.mjs new file mode 100644 index 000000000..5087a1f12 --- /dev/null +++ b/workbench/astro/astro.config.mjs @@ -0,0 +1,25 @@ +import { defineConfig } from 'astro/config'; +import { workflow } from 'workflow/astro'; +import node from '@astrojs/node'; +import vercel from '@astrojs/vercel'; + +// Node adapter needed for ci tests +const adapter = process.env.VERCEL_DEPLOYMENT_ID + ? vercel() + : node({ + mode: 'standalone', + }); + +// https://astro.build/config +export default defineConfig({ + output: 'server', + integrations: [workflow()], + adapter: adapter, + // WARNING: CSRF protection is disabled for testing/development purposes. + // This configuration trusts all origins and should NOT be used in production. + // In production, specify only trusted origins or remove this configuration + // to use Astro's default CSRF protection. + security: { + checkOrigin: false, + }, +}); diff --git a/workbench/astro/package.json b/workbench/astro/package.json new file mode 100644 index 000000000..e1b132bf5 --- /dev/null +++ b/workbench/astro/package.json @@ -0,0 +1,26 @@ +{ + "name": "@workflow/example-astro", + "type": "module", + "version": "0.0.0", + "scripts": { + "generate:workflows": "node ../scripts/generate-workflows-registry.js ./src/workflows src/lib/_workflows.ts", + "predev": "pnpm generate:workflows", + "prebuild": "pnpm generate:workflows", + "dev": "astro dev", + "build": "astro build", + "preview": "astro preview", + "astro": "astro", + "start": "node scripts/start-with-pg.mjs" + }, + "dependencies": { + "@astrojs/node": "9.5.0", + "@astrojs/vercel": "^9.0.0", + "ai": "catalog:", + "astro": "^5.15.6", + "lodash.chunk": "^4.2.0", + "openai": "6.9.0", + "workflow": "workspace:*", + "@workflow/world-postgres": "workspace:*", + "zod": "catalog:" + } +} diff --git a/workbench/astro/public/favicon.svg b/workbench/astro/public/favicon.svg new file mode 100644 index 000000000..f157bd1c5 --- /dev/null +++ b/workbench/astro/public/favicon.svg @@ -0,0 +1,9 @@ + + + + diff --git a/workbench/astro/scripts/start-with-pg.mjs b/workbench/astro/scripts/start-with-pg.mjs new file mode 100644 index 000000000..c6c506090 --- /dev/null +++ b/workbench/astro/scripts/start-with-pg.mjs @@ -0,0 +1,21 @@ +#!/usr/bin/env node + +// Start the Postgres World before starting the Astro server +// Needed since we test this in CI +// Astro doesn't have a hook for starting the Postgres World in production + +async function main() { + if (process.env.WORKFLOW_TARGET_WORLD === '@workflow/world-postgres') { + console.log('Starting Postgres World...'); + const { getWorld } = await import('workflow/runtime'); + await getWorld().start?.(); + } + + // Now start the Astro server + await import('../dist/server/entry.mjs'); +} + +main().catch((error) => { + console.error('Failed to start server:', error); + process.exit(1); +}); diff --git a/workbench/astro/src/lib/.gitkeep b/workbench/astro/src/lib/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/workbench/astro/src/pages/api/chat.ts b/workbench/astro/src/pages/api/chat.ts new file mode 100644 index 000000000..420339e43 --- /dev/null +++ b/workbench/astro/src/pages/api/chat.ts @@ -0,0 +1,12 @@ +// THIS FILE IS JUST FOR TESTING HMR AS AN ENTRY NEEDS +// TO IMPORT THE WORKFLOWS TO DISCOVER THEM AND WATCH + +import type { APIRoute } from 'astro'; +import * as workflows from '../../workflows/3_streams'; + +export const POST: APIRoute = async ({ request }: { request: Request }) => { + console.log(workflows); + return Response.json('hello world'); +}; + +export const prerender = false; diff --git a/workbench/astro/src/pages/api/hook.ts b/workbench/astro/src/pages/api/hook.ts new file mode 100644 index 000000000..afaa46ddc --- /dev/null +++ b/workbench/astro/src/pages/api/hook.ts @@ -0,0 +1,27 @@ +import type { APIRoute } from 'astro'; +import { getHookByToken, resumeHook } from 'workflow/api'; + +export const POST: APIRoute = async ({ request }: { request: Request }) => { + const { token, data } = await request.json(); + + let hook: Awaited>; + try { + hook = await getHookByToken(token); + console.log('hook', hook); + } catch (error) { + console.log('error during getHookByToken', error); + // TODO: `WorkflowAPIError` is not exported, so for now + // we'll return 404 assuming it's the "invalid" token test case + return Response.json(null, { status: 404 }); + } + + await resumeHook(hook.token, { + ...data, + // @ts-expect-error metadata is not typed + customData: hook.metadata?.customData, + }); + + return Response.json(hook); +}; + +export const prerender = false; diff --git a/workbench/astro/src/pages/api/test-direct-step-call.ts b/workbench/astro/src/pages/api/test-direct-step-call.ts new file mode 100644 index 000000000..205dff6c5 --- /dev/null +++ b/workbench/astro/src/pages/api/test-direct-step-call.ts @@ -0,0 +1,19 @@ +import { add } from '../../workflows/99_e2e'; + +export async function POST({ request }: { request: Request }) { + // This route tests calling step functions directly outside of any workflow context + // After the SWC compiler changes, step functions in client mode have their directive removed + // and keep their original implementation, allowing them to be called as regular async functions + const body = await request.json(); + const { x, y } = body; + + console.log(`Calling step function directly with x=${x}, y=${y}`); + + // Call step function directly as a regular async function (no workflow context) + const result = await add(x, y); + console.log(`add(${x}, ${y}) = ${result}`); + + return Response.json({ result }); +} + +export const prerender = false; diff --git a/workbench/astro/src/pages/api/trigger.ts b/workbench/astro/src/pages/api/trigger.ts new file mode 100644 index 000000000..fe54124d5 --- /dev/null +++ b/workbench/astro/src/pages/api/trigger.ts @@ -0,0 +1,153 @@ +import { getRun, start } from 'workflow/api'; +import { + WorkflowRunFailedError, + WorkflowRunNotCompletedError, +} from 'workflow/internal/errors'; +import { hydrateWorkflowArguments } from 'workflow/internal/serialization'; +import { allWorkflows } from '../../lib/_workflows'; +import type { APIRoute } from 'astro'; + +export async function POST({ request }: { request: Request }) { + const url = new URL(request.url); + const workflowFile = + url.searchParams.get('workflowFile') || 'workflows/99_e2e.ts'; + if (!workflowFile) { + return new Response('No workflowFile query parameter provided', { + status: 400, + }); + } + const workflows = allWorkflows[workflowFile as keyof typeof allWorkflows]; + if (!workflows) { + return new Response(`Workflow file "${workflowFile}" not found`, { + status: 400, + }); + } + + const workflowFn = url.searchParams.get('workflowFn') || 'simple'; + if (!workflowFn) { + return new Response('No workflow query parameter provided', { + status: 400, + }); + } + const workflow = workflows[workflowFn as keyof typeof workflows]; + if (!workflow) { + return new Response(`Workflow "${workflowFn}" not found`, { status: 400 }); + } + + let args: any[] = []; + + // Args from query string + const argsParam = url.searchParams.get('args'); + if (argsParam) { + args = argsParam.split(',').map((arg) => { + const num = parseFloat(arg); + return Number.isNaN(num) ? arg.trim() : num; + }); + } else { + // Args from body + const body = await request.text(); + if (body) { + args = hydrateWorkflowArguments(JSON.parse(body), globalThis); + } else { + args = [42]; + } + } + console.log(`Starting "${workflowFn}" workflow with args: ${args}`); + + try { + const run = await start(workflow as any, args as any); + console.log('Run:', run); + return Response.json(run); + } catch (err) { + console.error(`Failed to start!!`, err); + throw err; + } +} + +export const GET: APIRoute = async ({ request }: { request: Request }) => { + const url = new URL(request.url); + const runId = url.searchParams.get('runId'); + if (!runId) { + return new Response('No runId provided', { status: 400 }); + } + + const outputStreamParam = url.searchParams.get('output-stream'); + if (outputStreamParam) { + const namespace = outputStreamParam === '1' ? undefined : outputStreamParam; + const run = getRun(runId); + const stream = run.getReadable({ + namespace, + }); + // Add JSON framing to the stream, wrapping binary data in base64 + const streamWithFraming = new TransformStream({ + transform(chunk, controller) { + const data = + chunk instanceof Uint8Array + ? { data: Buffer.from(chunk).toString('base64') } + : chunk; + controller.enqueue(`${JSON.stringify(data)}\n`); + }, + }); + return new Response(stream.pipeThrough(streamWithFraming), { + headers: { + 'Content-Type': 'application/octet-stream', + }, + }); + } + + try { + const run = getRun(runId); + const returnValue = await run.returnValue; + console.log('Return value:', returnValue); + return returnValue instanceof ReadableStream + ? new Response(returnValue, { + headers: { + 'Content-Type': 'application/octet-stream', + }, + }) + : Response.json(returnValue); + } catch (error) { + if (error instanceof Error) { + if (WorkflowRunNotCompletedError.is(error)) { + return Response.json( + { + ...error, + name: error.name, + message: error.message, + }, + { status: 202 } + ); + } + + if (WorkflowRunFailedError.is(error)) { + const cause = error.cause; + return Response.json( + { + ...error, + name: error.name, + message: error.message, + cause: { + message: cause.message, + stack: cause.stack, + code: cause.code, + }, + }, + { status: 400 } + ); + } + } + + console.error( + 'Unexpected error while getting workflow return value:', + error + ); + return Response.json( + { + error: 'Internal server error', + }, + { status: 500 } + ); + } +}; + +export const prerender = false; diff --git a/workbench/astro/src/pages/index.astro b/workbench/astro/src/pages/index.astro new file mode 100644 index 000000000..45a53644c --- /dev/null +++ b/workbench/astro/src/pages/index.astro @@ -0,0 +1,138 @@ +--- +// No server-side code needed for this page +--- + + + + + + Workflow DevKit + Astro Example + + + + +

Workflow DevKit + Nitro Example

+
+ + + + + diff --git a/workbench/astro/src/workflows b/workbench/astro/src/workflows new file mode 120000 index 000000000..8a9aab9c9 --- /dev/null +++ b/workbench/astro/src/workflows @@ -0,0 +1 @@ +../../sveltekit/src/workflows \ No newline at end of file diff --git a/workbench/astro/tsconfig.json b/workbench/astro/tsconfig.json new file mode 100644 index 000000000..8bf91d3bb --- /dev/null +++ b/workbench/astro/tsconfig.json @@ -0,0 +1,5 @@ +{ + "extends": "astro/tsconfigs/strict", + "include": [".astro/types.d.ts", "**/*"], + "exclude": ["dist"] +}