diff --git a/.changeset/config.json b/.changeset/config.json
index 4b700162e..ff0a3ea14 100644
--- a/.changeset/config.json
+++ b/.changeset/config.json
@@ -15,6 +15,7 @@
"@workflow/example-hono",
"@workflow/example-nitro-v3",
"@workflow/example-nitro-v2",
- "@workflow/example-nuxt"
+ "@workflow/example-nuxt",
+ "@workflow/example-sveltekit"
]
}
diff --git a/.changeset/pre.json b/.changeset/pre.json
index 4507a2e09..01854a64a 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -26,7 +26,8 @@
"nextjs-webpack": "0.0.2-alpha.5",
"@workflow/example-nitro-v3": "0.0.0",
"@workflow/example-nitro-v2": "0.0.0",
- "@workflow/example-nuxt": "0.0.0"
+ "@workflow/example-nuxt": "0.0.0",
+ "@workflow/example-sveltekit": "0.0.0"
},
"changesets": [
"angry-owls-beg",
diff --git a/.changeset/purple-regions-vanish.md b/.changeset/purple-regions-vanish.md
new file mode 100644
index 000000000..540aca355
--- /dev/null
+++ b/.changeset/purple-regions-vanish.md
@@ -0,0 +1,9 @@
+---
+"@workflow/world-local": patch
+"@workflow/sveltekit": patch
+"workflow": patch
+"@workflow/core": patch
+"@workflow/cli": patch
+---
+
+Add sveltekit workflow integration
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index f23442bd9..5a1f1ac0b 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -56,6 +56,8 @@ jobs:
project-id: "prj_avRPBF3eWjh6iDNQgmhH4VOg27h0"
- name: "nitro"
project-id: "prj_e7DZirYdLrQKXNrlxg7KmA6ABx8r"
+ - name: "sveltekit"
+ project-id: "prj_MqnBLm71ceXGSnm3Fs8i8gBnI23G"
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
@@ -170,7 +172,7 @@ jobs:
run: cd workbench/${{ matrix.app.name }} && ./resolve-symlinks.sh
- name: Run E2E Tests (Next.js)
- if: matrix.app.name != 'nitro'
+ if: matrix.app.name != 'nitro' && matrix.app.name != 'sveltekit'
run: cd workbench/${{ matrix.app.name }} && pnpm dev & echo "starting tests in 10 seconds" && sleep 10 && pnpm vitest run packages/core/e2e/next-dev.test.ts && pnpm run test:e2e
env:
APP_NAME: ${{ matrix.app.name }}
@@ -182,6 +184,12 @@ jobs:
env:
APP_NAME: ${{ matrix.app.name }}
DEPLOYMENT_URL: "http://localhost:3000"
+ - name: Run E2E Tests (SvelteKit)
+ if: matrix.app.name == 'sveltekit'
+ run: cd workbench/${{ matrix.app.name }} && pnpm dev & echo "starting tests in 10 seconds" && sleep 10 && pnpm vitest run packages/core/e2e/e2e.test.ts
+ env:
+ APP_NAME: ${{ matrix.app.name }}
+ DEPLOYMENT_URL: "http://localhost:3000"
e2e-local-prod:
name: E2E Local Prod Tests (${{ matrix.app.name }} - ${{ matrix.app.canary && 'canary' || 'stable' }})
diff --git a/docs/app/(home)/components/frameworks.tsx b/docs/app/(home)/components/frameworks.tsx
index f5bbbeb40..19e7b68b6 100644
--- a/docs/app/(home)/components/frameworks.tsx
+++ b/docs/app/(home)/components/frameworks.tsx
@@ -485,11 +485,18 @@ export const Frameworks = () => {
-
-
+
+
+
-
+
+
+
+
+
+
+
@@ -508,13 +515,6 @@ export const Frameworks = () => {
- handleRequest('SvelteKit')}
- >
-
-
-
handleRequest('Nuxt')}
diff --git a/docs/app/docs/[[...slug]]/page.tsx b/docs/app/docs/[[...slug]]/page.tsx
index e1426eefd..93d9fa412 100644
--- a/docs/app/docs/[[...slug]]/page.tsx
+++ b/docs/app/docs/[[...slug]]/page.tsx
@@ -34,7 +34,7 @@ function Card({ title, href, className, children, disabled }: CardProps) {
diff --git a/docs/content/docs/getting-started/index.mdx b/docs/content/docs/getting-started/index.mdx
index 773827587..44822bc01 100644
--- a/docs/content/docs/getting-started/index.mdx
+++ b/docs/content/docs/getting-started/index.mdx
@@ -25,10 +25,9 @@ Start by choosing your framework. Each guide will walk you through the steps to
Nitro
-
+
SvelteKit
- Coming soon
diff --git a/docs/content/docs/getting-started/sveltekit.mdx b/docs/content/docs/getting-started/sveltekit.mdx
new file mode 100644
index 000000000..1c15df29d
--- /dev/null
+++ b/docs/content/docs/getting-started/sveltekit.mdx
@@ -0,0 +1,258 @@
+---
+title: SvelteKit
+---
+
+# SvelteKit
+
+This guide will walk through setting up your first workflow in a SvelteKit 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 SvelteKit Project
+
+Start by creating a new SvelteKit project. This command will create a new directory named `my-workflow-app` with a minimal setup and setup a SvelteKit project inside it.
+
+```bash
+npx sv create my-workflow-app --template=minimal --types=ts --no-add-ons
+```
+
+Enter the newly made directory:
+
+```bash
+cd my-workflow-app
+```
+
+### Install `workflow`
+
+
+
+
+ npm i workflow
+
+
+
+
+ pnpm i workflow
+
+
+
+
+ yarn add workflow
+
+
+
+
+### Configure Vite
+
+Add `workflowPlugin()` to your Vite config. This enables usage of the `"use workflow"` and `"use step"` directives.
+
+```typescript title="vite.config.ts" lineNumbers
+import { sveltekit } from "@sveltejs/kit/vite";
+import { defineConfig } from "vite";
+import { workflowPlugin } from "workflow/sveltekit"; // [!code highlight]
+
+export default defineConfig({
+ plugins: [sveltekit(), workflowPlugin()], // [!code highlight]
+});
+```
+
+### Update `package.json`
+
+Update your `package.json` to include port `3000` for the development server:
+
+```json title="package.json" lineNumbers
+{
+ // ...
+ "scripts": {
+ "dev": "vite dev --port 3000"
+ // ...
+ },
+}
+```
+
+
+
+
+ ### 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="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="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/routes/api/signup/+server.ts` with the following code:
+
+```typescript title="src/routes/api/signup/+server.ts"
+import { start } from "workflow/api";
+import { handleUserSignup } from "../../../../workflows/user-signup";
+import { json, type RequestHandler } from "@sveltejs/kit";
+
+export const POST: RequestHandler = async ({
+ request,
+}: {
+ request: Request;
+}) => {
+ const { email } = await request.json();
+
+ // Executes asynchronously and doesn't block your app
+ await start(handleUserSignup, [email]);
+
+ return json({ message: "User signup workflow started" });
+};
+
+```
+
+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 SvelteKit 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:3000/api/signup
+```
+
+Check the SvelteKit 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
+```
+
+
+
+---
+
+## Deploying to production
+
+Workflow DevKit apps currently work best when deployed to [Vercel](https://vercel.com/home) and needs no special configuration.
+
+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/packages/cli/src/lib/builders/base-builder.ts b/packages/cli/src/lib/builders/base-builder.ts
index 9ac184ca3..422ecdff7 100644
--- a/packages/cli/src/lib/builders/base-builder.ts
+++ b/packages/cli/src/lib/builders/base-builder.ts
@@ -18,6 +18,20 @@ const enhancedResolve = promisify(enhancedResolveOriginal);
const EMIT_SOURCEMAPS_FOR_DEBUGGING =
process.env.WORKFLOW_EMIT_SOURCEMAPS_FOR_DEBUGGING === '1';
+// Helper function code for converting SvelteKit requests to standard Request objects
+const SVELTEKIT_REQUEST_CONVERTER = `
+async function convertSvelteKitRequest(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);
+}
+`;
+
export abstract class BaseBuilder {
protected config: WorkflowConfig;
@@ -254,13 +268,28 @@ export abstract class BaseBuilder {
// Create a virtual entry that imports all files. All step definitions
// will get registered thanks to the swc transform.
const imports = stepFiles.map((file) => `import '${file}';`).join('\n');
- const entryContent = `
+
+ let entryContent = `
// Built in steps
import '${builtInSteps}';
// User steps
- ${imports}
+ ${imports}`;
+ if (this.config.buildTarget === 'sveltekit') {
+ entryContent += `
+ // API entrypoint
+ import { stepEntrypoint } from 'workflow/runtime';
+ ${SVELTEKIT_REQUEST_CONVERTER}
+ export const POST = async ({request}) => {
+ const normalRequest = await convertSvelteKitRequest(request);
+ return stepEntrypoint(normalRequest);
+ }
+ `;
+ } else {
+ entryContent += `
// API entrypoint
- export { stepEntrypoint as POST } from 'workflow/runtime';`;
+ export { stepEntrypoint as POST } from 'workflow/runtime';
+ `;
+ }
// Bundle with esbuild and our custom SWC plugin
const esbuildCtx = await esbuild.context({
@@ -455,14 +484,23 @@ export abstract class BaseBuilder {
const bundleFinal = async (interimBundle: string) => {
const workflowBundleCode = interimBundle;
- // Create the workflow function handler with proper linter suppressions
- const workflowFunctionCode = `// biome-ignore-all lint: generated file
+ let workflowFunctionCode = `// biome-ignore-all lint: generated file
/* eslint-disable */
import { workflowEntrypoint } from 'workflow/runtime';
const workflowCode = \`${workflowBundleCode.replace(/[\\`$]/g, '\\$&')}\`;
-
+`;
+ if (this.config.buildTarget === 'sveltekit') {
+ workflowFunctionCode += `
+${SVELTEKIT_REQUEST_CONVERTER}
+export const POST = async ({ request }) => {
+ const normalRequest = await convertSvelteKitRequest(request);
+ return workflowEntrypoint(workflowCode)(normalRequest);
+}`;
+ } else {
+ workflowFunctionCode += `
export const POST = workflowEntrypoint(workflowCode);`;
+ }
// we skip the final bundling step for Next.js so it can bundle itself
if (!bundleFinalOutput) {
@@ -585,7 +623,7 @@ export const POST = workflowEntrypoint(workflowCode);`;
// Create a static route that calls resumeWebhook
// This route works for both Next.js and Vercel Build Output API
- const routeContent = `import { resumeWebhook } from 'workflow/api';
+ let routeContent = `import { resumeWebhook } from 'workflow/api';
async function handler(request) {
const url = new URL(request.url);
@@ -605,8 +643,25 @@ async function handler(request) {
console.error('Error during resumeWebhook', error);
return new Response(null, { status: 404 });
}
-}
-
+}`;
+ if (this.config.buildTarget === 'sveltekit') {
+ routeContent += `
+${SVELTEKIT_REQUEST_CONVERTER}
+const createSvelteKitHandler = (method) => async ({ request }) => {
+ const normalRequest = await convertSvelteKitRequest(request);
+ return handler(normalRequest);
+};
+
+export const GET = createSvelteKitHandler('GET');
+export const POST = createSvelteKitHandler('POST');
+export const PUT = createSvelteKitHandler('PUT');
+export const PATCH = createSvelteKitHandler('PATCH');
+export const DELETE = createSvelteKitHandler('DELETE');
+export const HEAD = createSvelteKitHandler('HEAD');
+export const OPTIONS = createSvelteKitHandler('OPTIONS');
+`;
+ } else {
+ routeContent += `
export const GET = handler;
export const POST = handler;
export const PUT = handler;
@@ -615,7 +670,7 @@ export const DELETE = handler;
export const HEAD = handler;
export const OPTIONS = handler;
`;
-
+ }
if (!bundle) {
// For Next.js, just write the unbundled file
await writeFile(outfile, routeContent);
diff --git a/packages/cli/src/lib/config/types.ts b/packages/cli/src/lib/config/types.ts
index 9bdeb712d..b4d729c74 100644
--- a/packages/cli/src/lib/config/types.ts
+++ b/packages/cli/src/lib/config/types.ts
@@ -2,6 +2,7 @@ export const validBuildTargets = [
'standalone',
'vercel-build-output-api',
'next',
+ 'sveltekit',
] as const;
export type BuildTarget = (typeof validBuildTargets)[number];
diff --git a/packages/core/e2e/local-build.test.ts b/packages/core/e2e/local-build.test.ts
index c32a3690c..237d6628c 100644
--- a/packages/core/e2e/local-build.test.ts
+++ b/packages/core/e2e/local-build.test.ts
@@ -5,7 +5,7 @@ import { getWorkbenchAppPath } from './utils';
const exec = promisify(execOriginal);
-describe.each(['nextjs-webpack', 'nextjs-turbopack', 'nitro'])(
+describe.each(['nextjs-webpack', 'nextjs-turbopack', 'nitro', 'sveltekit'])(
'e2e',
(project) => {
test('builds without errors', { timeout: 180_000 }, async () => {
diff --git a/packages/core/src/runtime.ts b/packages/core/src/runtime.ts
index 9fa31c92d..01bce4bc1 100644
--- a/packages/core/src/runtime.ts
+++ b/packages/core/src/runtime.ts
@@ -401,7 +401,11 @@ export function workflowEntrypoint(workflowCode: string) {
input: dehydratedArgs as Serializable[],
});
- waitUntil(Promise.all(ops));
+ waitUntil(
+ Promise.all(ops).catch((err) => {
+ console.error('Error waiting for ops', err);
+ })
+ );
await world.queue(
`__wkf_step_${queueItem.stepName}`,
@@ -680,7 +684,11 @@ export const stepEntrypoint =
result = dehydrateStepReturnValue(result, ops);
- waitUntil(Promise.all(ops));
+ waitUntil(
+ Promise.all(ops).catch((err) => {
+ console.error('Error waiting for ops', err);
+ })
+ );
// Update the event log with the step result
await world.events.create(workflowRunId, {
diff --git a/packages/core/src/runtime/resume-hook.ts b/packages/core/src/runtime/resume-hook.ts
index aa6401aa8..119ed71c5 100644
--- a/packages/core/src/runtime/resume-hook.ts
+++ b/packages/core/src/runtime/resume-hook.ts
@@ -78,7 +78,11 @@ export async function resumeHook(
ops,
globalThis
);
- waitUntil(Promise.all(ops));
+ waitUntil(
+ Promise.all(ops).catch((err) => {
+ console.error('Error waiting for ops', err);
+ })
+ );
// Create a hook_received event with the payload
await world.events.create(hook.runId, {
diff --git a/packages/core/src/runtime/start.ts b/packages/core/src/runtime/start.ts
index ee61093df..56e379cbc 100644
--- a/packages/core/src/runtime/start.ts
+++ b/packages/core/src/runtime/start.ts
@@ -91,7 +91,11 @@ export async function start(
input: workflowArguments,
executionContext: { traceCarrier },
});
- waitUntil(Promise.all(ops));
+ waitUntil(
+ Promise.all(ops).catch((err) => {
+ console.error('Error waiting for ops', err);
+ })
+ );
span?.setAttributes({
...Attribute.WorkflowRunId(runResponse.runId),
diff --git a/packages/sveltekit/LICENSE.md b/packages/sveltekit/LICENSE.md
new file mode 120000
index 000000000..f0608a63a
--- /dev/null
+++ b/packages/sveltekit/LICENSE.md
@@ -0,0 +1 @@
+../../LICENSE.md
\ No newline at end of file
diff --git a/packages/sveltekit/README.md b/packages/sveltekit/README.md
new file mode 100644
index 000000000..88571598b
--- /dev/null
+++ b/packages/sveltekit/README.md
@@ -0,0 +1,3 @@
+# workflow/sveltekit
+
+The docs have moved! Refer to them [here](https://useworkflow.dev/)
diff --git a/packages/sveltekit/package.json b/packages/sveltekit/package.json
new file mode 100644
index 000000000..474770527
--- /dev/null
+++ b/packages/sveltekit/package.json
@@ -0,0 +1,39 @@
+{
+ "name": "@workflow/sveltekit",
+ "version": "4.0.0-beta.1",
+ "description": "SvelteKit integration for Workflow DevKit",
+ "type": "module",
+ "main": "dist/index.js",
+ "files": [
+ "dist"
+ ],
+ "publishConfig": {
+ "access": "public"
+ },
+ "license": "MIT",
+ "repository": {
+ "type": "git",
+ "url": "https://github.com/vercel/workflow.git",
+ "directory": "packages/sveltekit"
+ },
+ "exports": {
+ ".": "./dist/index.js"
+ },
+ "scripts": {
+ "build": "tsc",
+ "dev": "tsc --watch",
+ "clean": "tsc --build --clean && rm -rf dist"
+ },
+ "dependencies": {
+ "@sveltejs/kit": "^2.48.2",
+ "@workflow/cli": "workspace:*",
+ "fs-extra": "^11.3.2",
+ "@workflow/nitro": "workspace:*"
+ },
+ "devDependencies": {
+ "@types/fs-extra": "^11.0.4",
+ "@types/node": "catalog:",
+ "@workflow/tsconfig": "workspace:*",
+ "vite": "^7.1.11"
+ }
+}
diff --git a/packages/sveltekit/src/builders.ts b/packages/sveltekit/src/builders.ts
new file mode 100644
index 000000000..bea422bbe
--- /dev/null
+++ b/packages/sveltekit/src/builders.ts
@@ -0,0 +1,151 @@
+import { constants } from 'node:fs';
+import { access, mkdir, stat, writeFile } from 'node:fs/promises';
+import { join, resolve } from 'node:path';
+import { BaseBuilder } from '@workflow/cli/dist/lib/builders/base-builder.js';
+import type { WorkflowConfig } from '@workflow/cli/dist/lib/config/types.js';
+
+const CommonBuildOptions = {
+ dirs: ['workflows', 'src/workflows'],
+ buildTarget: 'sveltekit' as const,
+ stepsBundlePath: '', // unused in base
+ workflowsBundlePath: '', // unused in base
+ webhookBundlePath: '', // unused in base
+};
+
+export class LocalBuilder extends BaseBuilder {
+ constructor(config?: Partial) {
+ super({
+ ...CommonBuildOptions,
+ ...config,
+ workingDir: config?.workingDir || process.cwd(),
+ });
+ }
+
+ override async build(): Promise {
+ // Find SvelteKit routes directory (src/routes or routes)
+ const routesDir = await this.findRoutesDirectory();
+ const workflowGeneratedDir = join(routesDir, '.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 SvelteKit 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/+server.js
+ const stepsRouteDir = join(workflowGeneratedDir, 'step');
+ await mkdir(stepsRouteDir, { recursive: true });
+
+ return await this.createStepsBundle({
+ format: 'esm',
+ inputFiles,
+ outfile: join(stepsRouteDir, '+server.js'),
+ externalizeNonSteps: true,
+ tsBaseUrl,
+ tsPaths,
+ });
+ }
+
+ private async buildWorkflowsRoute({
+ inputFiles,
+ workflowGeneratedDir,
+ tsPaths,
+ tsBaseUrl,
+ }: {
+ inputFiles: string[];
+ workflowGeneratedDir: string;
+ tsBaseUrl?: string;
+ tsPaths?: Record;
+ }) {
+ // Create workflows route: .well-known/workflow/v1/flow/+server.js
+ const workflowsRouteDir = join(workflowGeneratedDir, 'flow');
+ await mkdir(workflowsRouteDir, { recursive: true });
+
+ return await this.createWorkflowsBundle({
+ format: 'esm',
+ outfile: join(workflowsRouteDir, '+server.js'),
+ bundleFinalOutput: false,
+ inputFiles,
+ tsBaseUrl,
+ tsPaths,
+ });
+ }
+
+ private async buildWebhookRoute({
+ workflowGeneratedDir,
+ }: {
+ workflowGeneratedDir: string;
+ }) {
+ // Create webhook route: .well-known/workflow/v1/webhook/[token]/+server.js
+ const webhookRouteFile = join(
+ workflowGeneratedDir,
+ 'webhook/[token]/+server.js'
+ );
+
+ return await this.createWebhookBundle({
+ outfile: webhookRouteFile,
+ bundle: false, // SvelteKit will handle bundling
+ });
+ }
+
+ private async findRoutesDirectory(): Promise {
+ const routesDir = resolve(this.config.workingDir, 'src/routes');
+ const rootRoutesDir = resolve(this.config.workingDir, 'routes');
+
+ // Try src/routes first (standard SvelteKit convention)
+ try {
+ await access(routesDir, constants.F_OK);
+ const routesStats = await stat(routesDir);
+ if (!routesStats.isDirectory()) {
+ throw new Error(`Path exists but is not a directory: ${routesDir}`);
+ }
+ return routesDir;
+ } catch {
+ // Try routes as fallback
+ try {
+ await access(rootRoutesDir, constants.F_OK);
+ const rootRoutesStats = await stat(rootRoutesDir);
+ if (!rootRoutesStats.isDirectory()) {
+ throw new Error(
+ `Path exists but is not a directory: ${rootRoutesDir}`
+ );
+ }
+ return rootRoutesDir;
+ } catch {
+ throw new Error(
+ 'Could not find SvelteKit routes directory. Expected either "src/routes" or "routes" to exist.'
+ );
+ }
+ }
+ }
+}
diff --git a/packages/sveltekit/src/index.ts b/packages/sveltekit/src/index.ts
new file mode 100644
index 000000000..df9b09ba7
--- /dev/null
+++ b/packages/sveltekit/src/index.ts
@@ -0,0 +1,78 @@
+import path from 'node:path';
+import fs from 'fs-extra';
+import { LocalBuilder } from './builders.js';
+
+const localBuilder = new LocalBuilder();
+
+// This needs to be in the top-level as we need to create these
+// entries before svelte plugin is started or the entries are
+// a race to be created before svelte discovers entries
+await localBuilder.build();
+
+process.on('beforeExit', () => {
+ // Don't patch functions output if not in Vercel adapter
+ if (!process.env.VERCEL_DEPLOYMENT_ID) {
+ return;
+ }
+ for (const { file, config } of [
+ {
+ file: '.vercel/output/functions/.well-known/workflow/v1/flow.func/.vc-config.json',
+ config: {
+ experimentalTriggers: [
+ {
+ type: 'queue/v1beta',
+ topic: '__wkf_workflow_*',
+ consumer: 'default',
+ maxDeliveries: 64,
+ retryAfterSeconds: 5,
+ initialDelaySeconds: 0,
+ },
+ ],
+ },
+ },
+ {
+ file: '.vercel/output/functions/.well-known/workflow/v1/step.func/.vc-config.json',
+ config: {
+ experimentalTriggers: [
+ {
+ type: 'queue/v1beta',
+ topic: '__wkf_step_*',
+ consumer: 'default',
+ maxDeliveries: 64,
+ retryAfterSeconds: 5,
+ initialDelaySeconds: 0,
+ },
+ ],
+ },
+ },
+ ]) {
+ // Un-symlink these as they can't be shared due to different
+ // experimental triggers config
+ const toCopy = fs.readdirSync(path.dirname(file));
+ fs.removeSync(path.dirname(file));
+ fs.mkdirSync(path.dirname(file), { recursive: true });
+
+ for (const item of toCopy) {
+ fs.copySync(
+ path.join(
+ path.dirname(file).replace(/\.func$/, ''),
+ '__data.json.func',
+ item
+ ),
+ path.join(path.dirname(file), item)
+ );
+ }
+
+ // Update .vc-config.json with the new experimental triggers config
+ const existingConfig = JSON.parse(fs.readFileSync(file, 'utf8'));
+ fs.writeFileSync(
+ file,
+ JSON.stringify({
+ ...existingConfig,
+ ...config,
+ })
+ );
+ }
+});
+
+export { workflowRollupPlugin as workflowPlugin } from '@workflow/nitro/rollup-plugin';
diff --git a/packages/sveltekit/tsconfig.json b/packages/sveltekit/tsconfig.json
new file mode 100644
index 000000000..ba5d9aec0
--- /dev/null
+++ b/packages/sveltekit/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/workflow/package.json b/packages/workflow/package.json
index de26b0124..8d6a49f3e 100644
--- a/packages/workflow/package.json
+++ b/packages/workflow/package.json
@@ -34,6 +34,7 @@
"./internal/private": "./dist/internal/private.js",
"./next": "./dist/next.js",
"./nitro": "./dist/nitro.js",
+ "./sveltekit": "./dist/sveltekit.js",
"./rollup-plugin": "./dist/rollup-plugin.js",
"./runtime": "./dist/runtime.js"
},
@@ -51,7 +52,8 @@
"@workflow/typescript-plugin": "workspace:*",
"ms": "2.1.3",
"@workflow/next": "workspace:*",
- "@workflow/nitro": "workspace:*"
+ "@workflow/nitro": "workspace:*",
+ "@workflow/sveltekit": "workspace:*"
},
"devDependencies": {
"@types/ms": "^2.1.0",
diff --git a/packages/workflow/src/sveltekit.ts b/packages/workflow/src/sveltekit.ts
new file mode 100644
index 000000000..7de66f9c0
--- /dev/null
+++ b/packages/workflow/src/sveltekit.ts
@@ -0,0 +1 @@
+export * from '@workflow/sveltekit';
diff --git a/packages/world-local/src/queue.ts b/packages/world-local/src/queue.ts
index 93ce627dd..2b02973cf 100644
--- a/packages/world-local/src/queue.ts
+++ b/packages/world-local/src/queue.ts
@@ -59,6 +59,7 @@ export function createQueue(port?: number): Queue {
method: 'POST',
duplex: 'half',
headers: {
+ 'content-type': 'application/json',
'x-vqs-queue-name': queueName,
'x-vqs-message-id': messageId,
'x-vqs-message-attempt': String(attempt + 1),
@@ -115,7 +116,11 @@ export function createQueue(port?: number): Queue {
if (!headers.success || !req.body) {
return Response.json(
- { error: 'Missing required headers' },
+ {
+ error: !req.body
+ ? 'Missing request body'
+ : 'Missing required headers',
+ },
{ status: 400 }
);
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 3177f9c95..41ff4110d 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -106,7 +106,7 @@ importers:
version: 19.1.9(@types/react@19.1.13)
'@vercel/analytics':
specifier: ^1.5.0
- version: 1.5.0(next@15.6.0-canary.13(@opentelemetry/api@1.9.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)(vue-router@4.6.3(vue@3.5.22(typescript@5.9.3)))(vue@3.5.22(typescript@5.9.3))
+ version: 1.5.0(@sveltejs/kit@2.48.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.0)(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0)))(svelte@5.43.0)(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0)))(next@15.6.0-canary.13(@opentelemetry/api@1.9.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)(svelte@5.43.0)(vue-router@4.6.3(vue@3.5.22(typescript@5.9.3)))(vue@3.5.22(typescript@5.9.3))
'@vercel/edge-config':
specifier: ^1.4.0
version: 1.4.0(@opentelemetry/api@1.9.0)
@@ -508,6 +508,34 @@ importers:
specifier: latest
version: 3.0.1-alpha.0(@vercel/functions@3.1.4(@aws-sdk/credential-provider-web-identity@3.844.0))(better-sqlite3@11.10.0)(chokidar@4.0.3)(drizzle-orm@0.31.4(@opentelemetry/api@1.9.0)(@types/react@19.1.13)(better-sqlite3@11.10.0)(pg@8.16.3)(postgres@3.4.7)(react@19.2.0))(ioredis@5.8.2)(lru-cache@11.2.2)(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0))
+ packages/sveltekit:
+ dependencies:
+ '@sveltejs/kit':
+ specifier: ^2.48.2
+ version: 2.48.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.0)(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0)))(svelte@5.43.0)(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0))
+ '@workflow/cli':
+ specifier: workspace:*
+ version: link:../cli
+ '@workflow/nitro':
+ specifier: workspace:*
+ version: link:../nitro
+ fs-extra:
+ specifier: ^11.3.2
+ version: 11.3.2
+ devDependencies:
+ '@types/fs-extra':
+ specifier: ^11.0.4
+ version: 11.0.4
+ '@types/node':
+ specifier: 'catalog:'
+ version: 24.6.2
+ '@workflow/tsconfig':
+ specifier: workspace:*
+ version: link:../tsconfig
+ vite:
+ specifier: ^7.1.11
+ version: 7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0)
+
packages/swc-plugin-workflow:
dependencies:
'@swc/core':
@@ -704,6 +732,9 @@ importers:
'@workflow/nitro':
specifier: workspace:*
version: link:../nitro
+ '@workflow/sveltekit':
+ specifier: workspace:*
+ version: link:../sveltekit
'@workflow/typescript-plugin':
specifier: workspace:*
version: link:../typescript-plugin
@@ -1175,6 +1206,67 @@ importers:
specifier: 'catalog:'
version: 4.1.11
+ workbench/sveltekit:
+ dependencies:
+ '@ai-sdk/react':
+ specifier: 2.0.76
+ version: 2.0.76(react@19.2.0)(zod@4.1.11)
+ '@node-rs/xxhash':
+ specifier: 1.7.6
+ version: 1.7.6
+ '@opentelemetry/api':
+ specifier: ^1.9.0
+ version: 1.9.0
+ '@sveltejs/adapter-node':
+ specifier: ^5.4.0
+ version: 5.4.0(@sveltejs/kit@2.48.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.0)(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0)))(svelte@5.43.0)(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0)))
+ '@sveltejs/adapter-vercel':
+ specifier: ^6.1.1
+ version: 6.1.1(@sveltejs/kit@2.48.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.0)(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0)))(svelte@5.43.0)(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0)))(rollup@4.52.5)
+ '@vercel/otel':
+ specifier: ^1.13.0
+ version: 1.13.0(@opentelemetry/api-logs@0.57.2)(@opentelemetry/api@1.9.0)(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/resources@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-logs@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-metrics@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))
+ '@workflow/ai':
+ specifier: workspace:*
+ version: link:../../packages/ai
+ ai:
+ specifier: 'catalog:'
+ version: 5.0.76(zod@4.1.11)
+ lodash.chunk:
+ specifier: ^4.2.0
+ version: 4.2.0
+ workflow:
+ specifier: workspace:*
+ version: link:../../packages/workflow
+ zod:
+ specifier: 'catalog:'
+ version: 4.1.11
+ devDependencies:
+ '@sveltejs/kit':
+ specifier: ^2.43.2
+ version: 2.48.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.0)(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0)))(svelte@5.43.0)(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0))
+ '@sveltejs/vite-plugin-svelte':
+ specifier: ^6.2.0
+ version: 6.2.1(svelte@5.43.0)(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0))
+ '@types/lodash.chunk':
+ specifier: ^4.2.9
+ version: 4.2.9
+ svelte:
+ specifier: ^5.39.5
+ version: 5.43.0
+ svelte-check:
+ specifier: ^4.3.2
+ version: 4.3.3(picomatch@4.0.3)(svelte@5.43.0)(typescript@5.9.3)
+ typescript:
+ specifier: ^5.9.2
+ version: 5.9.3
+ vite:
+ specifier: ^7.1.7
+ version: 7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0)
+ vite-plugin-devtools-json:
+ specifier: ^1.0.0
+ version: 1.0.0(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0))
+
packages:
'@ai-sdk/gateway@2.0.0':
@@ -4269,6 +4361,50 @@ packages:
'@standard-schema/utils@0.3.0':
resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==}
+ '@sveltejs/acorn-typescript@1.0.6':
+ resolution: {integrity: sha512-4awhxtMh4cx9blePWl10HRHj8Iivtqj+2QdDCSMDzxG+XKa9+VCNupQuCuvzEhYPzZSrX+0gC+0lHA/0fFKKQQ==}
+ peerDependencies:
+ acorn: ^8.9.0
+
+ '@sveltejs/adapter-node@5.4.0':
+ resolution: {integrity: sha512-NMsrwGVPEn+J73zH83Uhss/hYYZN6zT3u31R3IHAn3MiKC3h8fjmIAhLfTSOeNHr5wPYfjjMg8E+1gyFgyrEcQ==}
+ peerDependencies:
+ '@sveltejs/kit': ^2.4.0
+
+ '@sveltejs/adapter-vercel@6.1.1':
+ resolution: {integrity: sha512-rnuREIO/dBYZn825aXTmdCU7sBr4nQqxNVkqYLUHoUnuv3vaas6O/SxAI1TiYBDEetgeQ5m51I5dTVJvhVzASA==}
+ engines: {node: '>=20.0'}
+ peerDependencies:
+ '@sveltejs/kit': ^2.4.0
+
+ '@sveltejs/kit@2.48.2':
+ resolution: {integrity: sha512-WIgVMGt+b9OxPDtu0Txow28RsBrLoV3wr2QoUxEHd4CHbpxbqKQf2SIEzd+SE+bqrUL2D5MxBeQBdY+t7o6n1A==}
+ engines: {node: '>=18.13'}
+ hasBin: true
+ peerDependencies:
+ '@opentelemetry/api': ^1.0.0
+ '@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0
+ svelte: ^4.0.0 || ^5.0.0-next.0
+ vite: ^5.0.3 || ^6.0.0 || ^7.0.0-beta.0
+ peerDependenciesMeta:
+ '@opentelemetry/api':
+ optional: true
+
+ '@sveltejs/vite-plugin-svelte-inspector@5.0.1':
+ resolution: {integrity: sha512-ubWshlMk4bc8mkwWbg6vNvCeT7lGQojE3ijDh3QTR6Zr/R+GXxsGbyH4PExEPpiFmqPhYiVSVmHBjUcVc1JIrA==}
+ engines: {node: ^20.19 || ^22.12 || >=24}
+ peerDependencies:
+ '@sveltejs/vite-plugin-svelte': ^6.0.0-next.0
+ svelte: ^5.0.0
+ vite: ^6.3.0 || ^7.0.0
+
+ '@sveltejs/vite-plugin-svelte@6.2.1':
+ resolution: {integrity: sha512-YZs/OSKOQAQCnJvM/P+F1URotNnYNeU3P2s4oIpzm1uFaqUEqRxUB0g5ejMjEb5Gjb9/PiBI5Ktrq4rUUF8UVQ==}
+ engines: {node: ^20.19 || ^22.12 || >=24}
+ peerDependencies:
+ svelte: ^5.0.0
+ vite: ^6.3.0 || ^7.0.0
+
'@swc/core-darwin-arm64@1.11.24':
resolution: {integrity: sha512-dhtVj0PC1APOF4fl5qT2neGjRLgHAAYfiVP8poJelhzhB/318bO+QCFWAiimcDoyMgpCXOhTp757gnoJJrheWA==}
engines: {node: '>=10'}
@@ -4447,6 +4583,9 @@ packages:
'@types/chai@5.2.2':
resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==}
+ '@types/cookie@0.6.0':
+ resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==}
+
'@types/d3-array@3.2.2':
resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==}
@@ -4558,6 +4697,9 @@ packages:
'@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
+ '@types/fs-extra@11.0.4':
+ resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==}
+
'@types/geojson@7946.0.16':
resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==}
@@ -4570,6 +4712,9 @@ packages:
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+ '@types/jsonfile@6.1.4':
+ resolution: {integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==}
+
'@types/jsonlines@0.1.5':
resolution: {integrity: sha512-/zOl7I350g4/G6fEW9dktpTrkcKqZDMRkr2SuDla0utgwkUXrm7OFXq2WZT0W9Jl7BYoisGbn1EZsV/Z2F9LGg==}
@@ -4965,6 +5110,10 @@ packages:
resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==}
engines: {node: '>=10'}
+ aria-query@5.3.2:
+ resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
+ engines: {node: '>= 0.4'}
+
array-timsort@1.0.3:
resolution: {integrity: sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==}
@@ -5010,6 +5159,10 @@ packages:
peerDependencies:
postcss: ^8.1.0
+ axobject-query@4.1.0:
+ resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
+ engines: {node: '>= 0.4'}
+
b4a@1.7.3:
resolution: {integrity: sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==}
peerDependencies:
@@ -5376,6 +5529,10 @@ packages:
cookie-es@2.0.0:
resolution: {integrity: sha512-RAj4E421UYRgqokKUmotqAwuplYw15qtdXfY+hGzgCJ/MBjCVZcSoHK/kH9kocfjRjcDME7IiDWR/1WX1TM2Pg==}
+ cookie@0.6.0:
+ resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==}
+ engines: {node: '>= 0.6'}
+
cookie@1.0.2:
resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==}
engines: {node: '>=18'}
@@ -6049,6 +6206,9 @@ packages:
jiti:
optional: true
+ esm-env@1.2.2:
+ resolution: {integrity: sha512-Epxrv+Nr/CaL4ZcFGPJIYLWFom+YeV1DqMLHJoEd9SYRxNbaFruBwfEX/kkHUJf55j2+TUbmDcmuilbP1TmXHA==}
+
espree@10.4.0:
resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -6062,6 +6222,9 @@ packages:
resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
engines: {node: '>=0.10'}
+ esrap@2.1.1:
+ resolution: {integrity: sha512-ebTT9B6lOtZGMgJ3o5r12wBacHctG7oEWazIda8UlPfA3HD/Wrv8FdXoVo73vzdpwCxNyXjPauyN2bbJzMkB9A==}
+
esrecurse@4.3.0:
resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
engines: {node: '>=4.0'}
@@ -6274,6 +6437,10 @@ packages:
fs-constants@1.0.0:
resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
+ fs-extra@11.3.2:
+ resolution: {integrity: sha512-Xr9F6z6up6Ws+NjzMCZc6WXg2YFRlrLP9NQDO3VQrWrfiojdhS56TzueT88ze0uBdCTwEIhQ3ptnmKeWGFAe0A==}
+ engines: {node: '>=14.14'}
+
fs-extra@7.0.1:
resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==}
engines: {node: '>=6 <7 || >=8'}
@@ -6733,6 +6900,9 @@ packages:
is-reference@1.2.1:
resolution: {integrity: sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==}
+ is-reference@3.0.3:
+ resolution: {integrity: sha512-ixkJoqQvAP88E6wLydLGGqCJsrFUnqoH6HnaczB8XmDH1oaWU+xxdptvikTgaEhtZ53Ky6YXiBuUI2WXLMCwjw==}
+
is-ssh@1.4.1:
resolution: {integrity: sha512-JNeu1wQsHjyHgn9NcWTaXq6zWSR6hqE0++zhfZlkFBbScNkyvxCdeV8sRkSBaeLKxmbpR21brail63ACNxJ0Tg==}
@@ -6861,6 +7031,9 @@ packages:
jsonfile@4.0.0:
resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==}
+ jsonfile@6.2.0:
+ resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==}
+
jsonlines@0.1.1:
resolution: {integrity: sha512-ekDrAGso79Cvf+dtm+mL8OBI2bmAOt3gssYs833De/C9NmIpWDWyUO4zPgB5x2/OhY366dkhgfPMYfwZF7yOZA==}
@@ -7002,6 +7175,9 @@ packages:
resolution: {integrity: sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==}
engines: {node: '>=14'}
+ locate-character@3.0.0:
+ resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==}
+
locate-path@5.0.0:
resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
engines: {node: '>=8'}
@@ -8521,6 +8697,10 @@ packages:
rw@1.3.3:
resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==}
+ sade@1.8.1:
+ resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==}
+ engines: {node: '>=6'}
+
safe-buffer@5.1.2:
resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==}
@@ -8584,6 +8764,9 @@ packages:
resolution: {integrity: sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==}
engines: {node: '>= 18'}
+ set-cookie-parser@2.7.2:
+ resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==}
+
setprototypeof@1.2.0:
resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
@@ -8849,6 +9032,18 @@ packages:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
+ svelte-check@4.3.3:
+ resolution: {integrity: sha512-RYP0bEwenDXzfv0P1sKAwjZSlaRyqBn0Fz1TVni58lqyEiqgwztTpmodJrGzP6ZT2aHl4MbTvWP6gbmQ3FOnBg==}
+ engines: {node: '>= 18.0.0'}
+ hasBin: true
+ peerDependencies:
+ svelte: ^4.0.0 || ^5.0.0-next.0
+ typescript: '>=5.0.0'
+
+ svelte@5.43.0:
+ resolution: {integrity: sha512-1sRxVbgJAB+UGzwkc3GUoiBSzEOf0jqzccMaVoI2+pI+kASUe9qubslxace8+Mzhqw19k4syTA5niCIJwfXpOA==}
+ engines: {node: '>=18'}
+
svgo@4.0.0:
resolution: {integrity: sha512-VvrHQ+9uniE+Mvx3+C9IEe/lWasXCU0nXMY2kZeLrHNICuRiC8uMPyM14UEaMOFA5mhyQqEkB02VoQ16n3DLaw==}
engines: {node: '>=16'}
@@ -9140,6 +9335,10 @@ packages:
resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==}
engines: {node: '>= 4.0.0'}
+ universalify@2.0.1:
+ resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
+ engines: {node: '>= 10.0.0'}
+
unplugin-utils@0.2.5:
resolution: {integrity: sha512-gwXJnPRewT4rT7sBi/IvxKTjsms7jX7QIDLOClApuZwR49SXbrB1z2NLUZ+vDHyqCj/n58OzRRqaW+B8OZi8vg==}
engines: {node: '>=18.12.0'}
@@ -9423,6 +9622,11 @@ packages:
vue-tsc:
optional: true
+ vite-plugin-devtools-json@1.0.0:
+ resolution: {integrity: sha512-MobvwqX76Vqt/O4AbnNMNWoXWGrKUqZbphCUle/J2KXH82yKQiunOeKnz/nqEPosPsoWWPP9FtNuPBSYpiiwkw==}
+ peerDependencies:
+ vite: ^5.0.0 || ^6.0.0 || ^7.0.0
+
vite-plugin-inspect@11.3.3:
resolution: {integrity: sha512-u2eV5La99oHoYPHE6UvbwgEqKKOQGz86wMg40CCosP6q8BkB6e5xPneZfYagK4ojPJSj5anHCrnvC20DpwVdRA==}
engines: {node: '>=14'}
@@ -9479,6 +9683,14 @@ packages:
yaml:
optional: true
+ vitefu@1.1.1:
+ resolution: {integrity: sha512-B/Fegf3i8zh0yFbpzZ21amWzHmuNlLlmJT6n7bu5e+pCHUKQIfXSYokrqOBGEMMe9UG2sostKQF9mml/vYaWJQ==}
+ peerDependencies:
+ vite: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0-beta.0
+ peerDependenciesMeta:
+ vite:
+ optional: true
+
vitest@3.2.4:
resolution: {integrity: sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==}
engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0}
@@ -9680,6 +9892,9 @@ packages:
youch@4.1.0-beta.11:
resolution: {integrity: sha512-sQi6PERyO/mT8w564ojOVeAlYTtVQmC2GaktQAf+IdI75/GKIggosBuvyVXvEV+FATAT6RbLdIjFoiIId4ozoQ==}
+ zimmerframe@1.1.4:
+ resolution: {integrity: sha512-B58NGBEoc8Y9MWWCQGl/gq9xBCe4IiKM0a2x7GZdQKOW5Exr8S1W24J6OgM1njK8xCRGvAJIL/MxXHf6SkmQKQ==}
+
zip-stream@6.0.1:
resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==}
engines: {node: '>= 14'}
@@ -9723,6 +9938,16 @@ snapshots:
optionalDependencies:
zod: 4.1.11
+ '@ai-sdk/react@2.0.76(react@19.2.0)(zod@4.1.11)':
+ dependencies:
+ '@ai-sdk/provider-utils': 3.0.12(zod@4.1.11)
+ ai: 5.0.76(zod@4.1.11)
+ react: 19.2.0
+ swr: 2.3.6(react@19.2.0)
+ throttleit: 2.1.0
+ optionalDependencies:
+ zod: 4.1.11
+
'@alloc/quick-lru@5.2.0': {}
'@ampproject/remapping@2.3.0':
@@ -13344,6 +13569,70 @@ snapshots:
'@standard-schema/utils@0.3.0': {}
+ '@sveltejs/acorn-typescript@1.0.6(acorn@8.15.0)':
+ dependencies:
+ acorn: 8.15.0
+
+ '@sveltejs/adapter-node@5.4.0(@sveltejs/kit@2.48.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.0)(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0)))(svelte@5.43.0)(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0)))':
+ dependencies:
+ '@rollup/plugin-commonjs': 28.0.8(rollup@4.52.5)
+ '@rollup/plugin-json': 6.1.0(rollup@4.52.5)
+ '@rollup/plugin-node-resolve': 16.0.3(rollup@4.52.5)
+ '@sveltejs/kit': 2.48.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.0)(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0)))(svelte@5.43.0)(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0))
+ rollup: 4.52.5
+
+ '@sveltejs/adapter-vercel@6.1.1(@sveltejs/kit@2.48.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.0)(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0)))(svelte@5.43.0)(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0)))(rollup@4.52.5)':
+ dependencies:
+ '@sveltejs/kit': 2.48.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.0)(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0)))(svelte@5.43.0)(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0))
+ '@vercel/nft': 0.30.3(rollup@4.52.5)
+ esbuild: 0.25.11
+ transitivePeerDependencies:
+ - encoding
+ - rollup
+ - supports-color
+
+ '@sveltejs/kit@2.48.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.0)(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0)))(svelte@5.43.0)(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0))':
+ dependencies:
+ '@standard-schema/spec': 1.0.0
+ '@sveltejs/acorn-typescript': 1.0.6(acorn@8.15.0)
+ '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.43.0)(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0))
+ '@types/cookie': 0.6.0
+ acorn: 8.15.0
+ cookie: 0.6.0
+ devalue: 5.4.1
+ esm-env: 1.2.2
+ kleur: 4.1.5
+ magic-string: 0.30.19
+ mrmime: 2.0.1
+ sade: 1.8.1
+ set-cookie-parser: 2.7.2
+ sirv: 3.0.2
+ svelte: 5.43.0
+ vite: 7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0)
+ optionalDependencies:
+ '@opentelemetry/api': 1.9.0
+
+ '@sveltejs/vite-plugin-svelte-inspector@5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.0)(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0)))(svelte@5.43.0)(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0))':
+ dependencies:
+ '@sveltejs/vite-plugin-svelte': 6.2.1(svelte@5.43.0)(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0))
+ debug: 4.4.3(supports-color@8.1.1)
+ svelte: 5.43.0
+ vite: 7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.0)(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0))':
+ dependencies:
+ '@sveltejs/vite-plugin-svelte-inspector': 5.0.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.0)(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0)))(svelte@5.43.0)(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0))
+ debug: 4.4.3(supports-color@8.1.1)
+ deepmerge: 4.3.1
+ magic-string: 0.30.19
+ svelte: 5.43.0
+ vite: 7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0)
+ vitefu: 1.1.1(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0))
+ transitivePeerDependencies:
+ - supports-color
+
'@swc/core-darwin-arm64@1.11.24':
optional: true
@@ -13496,6 +13785,8 @@ snapshots:
dependencies:
'@types/deep-eql': 4.0.2
+ '@types/cookie@0.6.0': {}
+
'@types/d3-array@3.2.2': {}
'@types/d3-axis@3.0.6':
@@ -13636,6 +13927,11 @@ snapshots:
'@types/estree@1.0.8': {}
+ '@types/fs-extra@11.0.4':
+ dependencies:
+ '@types/jsonfile': 6.1.4
+ '@types/node': 24.6.2
+
'@types/geojson@7946.0.16': {}
'@types/graceful-fs@4.1.9':
@@ -13649,6 +13945,10 @@ snapshots:
'@types/json-schema@7.0.15':
optional: true
+ '@types/jsonfile@6.1.4':
+ dependencies:
+ '@types/node': 24.6.2
+
'@types/jsonlines@0.1.5':
dependencies:
'@types/node': 24.6.2
@@ -13732,10 +14032,12 @@ snapshots:
unhead: 2.0.19
vue: 3.5.22(typescript@5.9.3)
- '@vercel/analytics@1.5.0(next@15.6.0-canary.13(@opentelemetry/api@1.9.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)(vue-router@4.6.3(vue@3.5.22(typescript@5.9.3)))(vue@3.5.22(typescript@5.9.3))':
+ '@vercel/analytics@1.5.0(@sveltejs/kit@2.48.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.0)(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0)))(svelte@5.43.0)(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0)))(next@15.6.0-canary.13(@opentelemetry/api@1.9.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1))(react@19.1.1)(svelte@5.43.0)(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.2(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.43.0)(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0)))(svelte@5.43.0)(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0))
next: 15.6.0-canary.13(@opentelemetry/api@1.9.0)(react-dom@19.1.1(react@19.1.1))(react@19.1.1)
react: 19.1.1
+ svelte: 5.43.0
vue: 3.5.22(typescript@5.9.3)
vue-router: 4.6.3(vue@3.5.22(typescript@5.9.3))
@@ -14119,6 +14421,8 @@ snapshots:
dependencies:
tslib: 2.8.1
+ aria-query@5.3.2: {}
+
array-timsort@1.0.3: {}
array-union@2.1.0: {}
@@ -14163,6 +14467,8 @@ snapshots:
postcss: 8.5.6
postcss-value-parser: 4.2.0
+ axobject-query@4.1.0: {}
+
b4a@1.7.3: {}
bail@2.0.2: {}
@@ -14528,6 +14834,8 @@ snapshots:
cookie-es@2.0.0: {}
+ cookie@0.6.0: {}
+
cookie@1.0.2: {}
copy-anything@4.0.5:
@@ -15232,6 +15540,8 @@ snapshots:
- supports-color
optional: true
+ esm-env@1.2.2: {}
+
espree@10.4.0:
dependencies:
acorn: 8.15.0
@@ -15246,6 +15556,10 @@ snapshots:
estraverse: 5.3.0
optional: true
+ esrap@2.1.1:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+
esrecurse@4.3.0:
dependencies:
estraverse: 5.3.0
@@ -15468,6 +15782,12 @@ snapshots:
fs-constants@1.0.0: {}
+ fs-extra@11.3.2:
+ dependencies:
+ graceful-fs: 4.2.11
+ jsonfile: 6.2.0
+ universalify: 2.0.1
+
fs-extra@7.0.1:
dependencies:
graceful-fs: 4.2.11
@@ -15974,6 +16294,10 @@ snapshots:
dependencies:
'@types/estree': 1.0.8
+ is-reference@3.0.3:
+ dependencies:
+ '@types/estree': 1.0.8
+
is-ssh@1.4.1:
dependencies:
protocols: 2.0.2
@@ -16084,6 +16408,12 @@ snapshots:
optionalDependencies:
graceful-fs: 4.2.11
+ jsonfile@6.2.0:
+ dependencies:
+ universalify: 2.0.1
+ optionalDependencies:
+ graceful-fs: 4.2.11
+
jsonlines@0.1.1: {}
katex@0.16.25:
@@ -16234,6 +16564,8 @@ snapshots:
pkg-types: 2.3.0
quansync: 0.2.11
+ locate-character@3.0.0: {}
+
locate-path@5.0.0:
dependencies:
p-locate: 4.1.0
@@ -18486,6 +18818,10 @@ snapshots:
rw@1.3.3: {}
+ sade@1.8.1:
+ dependencies:
+ mri: 1.2.0
+
safe-buffer@5.1.2: {}
safe-buffer@5.2.1: {}
@@ -18554,6 +18890,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ set-cookie-parser@2.7.2: {}
+
setprototypeof@1.2.0: {}
sharp@0.34.4:
@@ -18842,6 +19180,35 @@ snapshots:
supports-preserve-symlinks-flag@1.0.0: {}
+ svelte-check@4.3.3(picomatch@4.0.3)(svelte@5.43.0)(typescript@5.9.3):
+ dependencies:
+ '@jridgewell/trace-mapping': 0.3.29
+ chokidar: 4.0.3
+ fdir: 6.5.0(picomatch@4.0.3)
+ picocolors: 1.1.1
+ sade: 1.8.1
+ svelte: 5.43.0
+ typescript: 5.9.3
+ transitivePeerDependencies:
+ - picomatch
+
+ svelte@5.43.0:
+ dependencies:
+ '@jridgewell/remapping': 2.3.5
+ '@jridgewell/sourcemap-codec': 1.5.5
+ '@sveltejs/acorn-typescript': 1.0.6(acorn@8.15.0)
+ '@types/estree': 1.0.8
+ acorn: 8.15.0
+ aria-query: 5.3.2
+ axobject-query: 4.1.0
+ clsx: 2.1.1
+ esm-env: 1.2.2
+ esrap: 2.1.1
+ is-reference: 3.0.3
+ locate-character: 3.0.0
+ magic-string: 0.30.19
+ zimmerframe: 1.1.4
+
svgo@4.0.0:
dependencies:
commander: 11.1.0
@@ -18864,6 +19231,12 @@ snapshots:
react: 19.1.1
use-sync-external-store: 1.5.0(react@19.1.1)
+ swr@2.3.6(react@19.2.0):
+ dependencies:
+ dequal: 2.0.3
+ react: 19.2.0
+ use-sync-external-store: 1.5.0(react@19.2.0)
+
system-architecture@0.1.0: {}
tagged-tag@1.0.0: {}
@@ -19181,6 +19554,8 @@ snapshots:
universalify@0.1.2: {}
+ universalify@2.0.1: {}
+
unplugin-utils@0.2.5:
dependencies:
pathe: 2.0.3
@@ -19321,6 +19696,10 @@ snapshots:
dependencies:
react: 19.1.1
+ use-sync-external-store@1.5.0(react@19.2.0):
+ dependencies:
+ react: 19.2.0
+
util-deprecate@1.0.2: {}
uuid@10.0.0: {}
@@ -19411,6 +19790,11 @@ snapshots:
optionator: 0.9.4
typescript: 5.9.3
+ vite-plugin-devtools-json@1.0.0(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0)):
+ dependencies:
+ uuid: 11.1.0
+ vite: 7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0)
+
vite-plugin-inspect@11.3.3(@nuxt/kit@3.19.3(magicast@0.3.5))(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0)):
dependencies:
ansis: 4.2.0
@@ -19455,6 +19839,10 @@ snapshots:
tsx: 4.20.6
yaml: 2.8.0
+ vitefu@1.1.1(vite@7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0)):
+ optionalDependencies:
+ vite: 7.1.11(@types/node@24.6.2)(jiti@2.6.1)(lightningcss@1.30.1)(terser@5.44.0)(tsx@4.20.6)(yaml@2.8.0)
+
vitest@3.2.4(@types/debug@4.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.0):
dependencies:
'@types/chai': 5.2.2
@@ -19659,6 +20047,8 @@ snapshots:
cookie: 1.0.2
youch-core: 0.3.3
+ zimmerframe@1.1.4: {}
+
zip-stream@6.0.1:
dependencies:
archiver-utils: 5.0.2
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index f6fe1858c..99e018529 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -14,3 +14,5 @@ catalog:
typescript: ^5.9.3
vitest: ^3.2.4
zod: 4.1.11
+onlyBuiltDependencies:
+ - esbuild
diff --git a/scripts/create-test-matrix.mjs b/scripts/create-test-matrix.mjs
index bb5731fd8..007fd7598 100644
--- a/scripts/create-test-matrix.mjs
+++ b/scripts/create-test-matrix.mjs
@@ -26,4 +26,9 @@ matrix.app.push({
project: 'workbench-nitro-workflow',
});
+matrix.app.push({
+ name: 'sveltekit',
+ project: 'workbench-sveltekit-workflow',
+});
+
console.log(JSON.stringify(matrix));
diff --git a/turbo.json b/turbo.json
index ca0f500f6..3b5c81471 100644
--- a/turbo.json
+++ b/turbo.json
@@ -4,17 +4,19 @@
"tasks": {
"build": {
"dependsOn": ["^build"],
- "outputs": ["dist/**"]
+ "outputs": ["dist/**", "build", ".svelte-kit", ".vercel/output"]
},
"dev": {
"dependsOn": ["^build"],
- "outputs": ["dist/**"]
+ "outputs": ["dist/**", "build", ".svelte-kit", ".vercel/output"]
},
"typecheck": {
- "dependsOn": ["^build"]
+ "dependsOn": ["^build"],
+ "outputs": ["dist/**", "build", ".svelte-kit", ".vercel/output"]
},
"test": {
- "dependsOn": ["^build"]
+ "dependsOn": ["^build"],
+ "outputs": ["dist/**", "build", ".svelte-kit", ".vercel/output"]
},
"clean": {
"cache": false
diff --git a/workbench/sveltekit/.gitignore b/workbench/sveltekit/.gitignore
new file mode 100644
index 000000000..3b462cb0c
--- /dev/null
+++ b/workbench/sveltekit/.gitignore
@@ -0,0 +1,23 @@
+node_modules
+
+# Output
+.output
+.vercel
+.netlify
+.wrangler
+/.svelte-kit
+/build
+
+# OS
+.DS_Store
+Thumbs.db
+
+# Env
+.env
+.env.*
+!.env.example
+!.env.test
+
+# Vite
+vite.config.js.timestamp-*
+vite.config.ts.timestamp-*
diff --git a/workbench/sveltekit/.npmrc b/workbench/sveltekit/.npmrc
new file mode 100644
index 000000000..b6f27f135
--- /dev/null
+++ b/workbench/sveltekit/.npmrc
@@ -0,0 +1 @@
+engine-strict=true
diff --git a/workbench/sveltekit/README.md b/workbench/sveltekit/README.md
new file mode 100644
index 000000000..75842c404
--- /dev/null
+++ b/workbench/sveltekit/README.md
@@ -0,0 +1,38 @@
+# sv
+
+Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli).
+
+## Creating a project
+
+If you're seeing this, you've probably already done this step. Congrats!
+
+```sh
+# create a new project in the current directory
+npx sv create
+
+# create a new project in my-app
+npx sv create my-app
+```
+
+## Developing
+
+Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
+
+```sh
+npm run dev
+
+# or start the server and open the app in a new browser tab
+npm run dev -- --open
+```
+
+## Building
+
+To create a production version of your app:
+
+```sh
+npm run build
+```
+
+You can preview the production build with `npm run preview`.
+
+> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.
diff --git a/workbench/sveltekit/package.json b/workbench/sveltekit/package.json
new file mode 100644
index 000000000..a901376ec
--- /dev/null
+++ b/workbench/sveltekit/package.json
@@ -0,0 +1,37 @@
+{
+ "name": "@workflow/example-sveltekit",
+ "private": true,
+ "version": "0.0.0",
+ "type": "module",
+ "scripts": {
+ "dev": "vite dev --port 3000",
+ "build": "vite build",
+ "start": "vite preview --port 3000",
+ "prepare": "svelte-kit sync || echo ''",
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
+ },
+ "devDependencies": {
+ "@sveltejs/kit": "^2.43.2",
+ "@sveltejs/vite-plugin-svelte": "^6.2.0",
+ "@types/lodash.chunk": "^4.2.9",
+ "svelte": "^5.39.5",
+ "svelte-check": "^4.3.2",
+ "typescript": "^5.9.2",
+ "vite": "^7.1.7",
+ "vite-plugin-devtools-json": "^1.0.0"
+ },
+ "dependencies": {
+ "@ai-sdk/react": "2.0.76",
+ "@node-rs/xxhash": "1.7.6",
+ "@opentelemetry/api": "^1.9.0",
+ "@sveltejs/adapter-node": "^5.4.0",
+ "@sveltejs/adapter-vercel": "^6.1.1",
+ "@vercel/otel": "^1.13.0",
+ "@workflow/ai": "workspace:*",
+ "ai": "catalog:",
+ "lodash.chunk": "^4.2.0",
+ "workflow": "workspace:*",
+ "zod": "catalog:"
+ }
+}
diff --git a/workbench/sveltekit/src/app.d.ts b/workbench/sveltekit/src/app.d.ts
new file mode 100644
index 000000000..520c4217a
--- /dev/null
+++ b/workbench/sveltekit/src/app.d.ts
@@ -0,0 +1,13 @@
+// See https://svelte.dev/docs/kit/types#app.d.ts
+// for information about these interfaces
+declare global {
+ namespace App {
+ // interface Error {}
+ // interface Locals {}
+ // interface PageData {}
+ // interface PageState {}
+ // interface Platform {}
+ }
+}
+
+export {};
diff --git a/workbench/sveltekit/src/app.html b/workbench/sveltekit/src/app.html
new file mode 100644
index 000000000..44e6779cb
--- /dev/null
+++ b/workbench/sveltekit/src/app.html
@@ -0,0 +1,72 @@
+
+
+
+
+
+ Workflows DevKit + SvelteKit Example
+
+ %sveltekit.head%
+
+
+
+ %sveltekit.body%
+
+
+
diff --git a/workbench/sveltekit/src/lib/assets/favicon.svg b/workbench/sveltekit/src/lib/assets/favicon.svg
new file mode 100644
index 000000000..cc5dc66a3
--- /dev/null
+++ b/workbench/sveltekit/src/lib/assets/favicon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/workbench/sveltekit/src/lib/index.ts b/workbench/sveltekit/src/lib/index.ts
new file mode 100644
index 000000000..856f2b6c3
--- /dev/null
+++ b/workbench/sveltekit/src/lib/index.ts
@@ -0,0 +1 @@
+// place files you want to import through the `$lib` alias in this folder.
diff --git a/workbench/sveltekit/src/routes/+layout.svelte b/workbench/sveltekit/src/routes/+layout.svelte
new file mode 100644
index 000000000..20f8d044f
--- /dev/null
+++ b/workbench/sveltekit/src/routes/+layout.svelte
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+{@render children?.()}
diff --git a/workbench/sveltekit/src/routes/+page.svelte b/workbench/sveltekit/src/routes/+page.svelte
new file mode 100644
index 000000000..8cf31343c
--- /dev/null
+++ b/workbench/sveltekit/src/routes/+page.svelte
@@ -0,0 +1,3 @@
+Workflows DevKit + SvelteKit Example
+
+
\ No newline at end of file
diff --git a/workbench/sveltekit/src/routes/api/hook/+server.ts b/workbench/sveltekit/src/routes/api/hook/+server.ts
new file mode 100644
index 000000000..a0254e560
--- /dev/null
+++ b/workbench/sveltekit/src/routes/api/hook/+server.ts
@@ -0,0 +1,29 @@
+import { json, type RequestHandler } from '@sveltejs/kit';
+import { getHookByToken, resumeHook } from 'workflow/api';
+
+export const POST: RequestHandler = 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 json(null, { status: 404 });
+ }
+
+ await resumeHook(hook.token, {
+ ...data,
+ // @ts-expect-error metadata is not typed
+ customData: hook.metadata?.customData,
+ });
+
+ return json(hook);
+};
diff --git a/workbench/sveltekit/src/routes/api/trigger/+server.ts b/workbench/sveltekit/src/routes/api/trigger/+server.ts
new file mode 100644
index 000000000..98efaa98d
--- /dev/null
+++ b/workbench/sveltekit/src/routes/api/trigger/+server.ts
@@ -0,0 +1,153 @@
+import { json, type RequestHandler } from '@sveltejs/kit';
+import { getRun, start } from 'workflow/api';
+import { hydrateWorkflowArguments } from 'workflow/internal/serialization';
+import * as calcWorkflow from '../../../../workflows/0_calc';
+import * as batchingWorkflow from '../../../../workflows/6_batching';
+import * as duplicateE2e from '../../../../workflows/98_duplicate_case';
+import * as e2eWorkflows from '../../../../workflows/99_e2e';
+
+const WORKFLOW_MODULES = {
+ 'workflows/0_calc.ts': calcWorkflow,
+ 'workflows/6_batching.ts': batchingWorkflow,
+ 'workflows/98_duplicate_case.ts': duplicateE2e,
+ 'workflows/99_e2e.ts': e2eWorkflows,
+} as const;
+
+export const POST: RequestHandler = async ({ request }) => {
+ const url = new URL(request.url);
+ const workflowFile =
+ url.searchParams.get('workflowFile') || 'workflows/99_e2e.ts';
+ const workflowFn = url.searchParams.get('workflowFn') || 'simple';
+
+ console.log('calling workflow', { workflowFile, workflowFn });
+
+ const workflows =
+ WORKFLOW_MODULES[workflowFile as keyof typeof WORKFLOW_MODULES];
+ if (!workflows) {
+ return json(
+ { error: `Workflow file "${workflowFile}" not found` },
+ { status: 404 }
+ );
+ }
+
+ const workflow = workflows[workflowFn as keyof typeof workflows];
+ if (!workflow) {
+ return json(
+ {
+ error: `Workflow "${workflowFn}" not found in "${workflowFile}"`,
+ },
+ { status: 404 }
+ );
+ }
+
+ 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 "${workflowFile}/${workflowFn}" workflow with args: ${args}`
+ );
+
+ try {
+ const run = await start(workflow as any, args);
+ console.log('Run:', run);
+ return json(run);
+ } catch (err) {
+ console.error(`Failed to start!!`, err);
+ throw err;
+ }
+};
+
+export const GET: RequestHandler = async ({ 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',
+ },
+ })
+ : json(returnValue);
+ } catch (error) {
+ if (error instanceof Error) {
+ if (error.name === 'WorkflowRunNotCompletedError') {
+ return json(
+ {
+ ...error,
+ name: error.name,
+ message: error.message,
+ },
+ { status: 202 }
+ );
+ }
+
+ if (error.name === 'WorkflowRunFailedError') {
+ return json(
+ {
+ ...error,
+ name: error.name,
+ message: error.message,
+ },
+ { status: 400 }
+ );
+ }
+ }
+
+ console.error(
+ 'Unexpected error while getting workflow return value:',
+ error
+ );
+ return json(
+ {
+ error: 'Internal server error',
+ },
+ { status: 500 }
+ );
+ }
+};
diff --git a/workbench/sveltekit/static/robots.txt b/workbench/sveltekit/static/robots.txt
new file mode 100644
index 000000000..b6dd6670c
--- /dev/null
+++ b/workbench/sveltekit/static/robots.txt
@@ -0,0 +1,3 @@
+# allow crawling everything by default
+User-agent: *
+Disallow:
diff --git a/workbench/sveltekit/svelte.config.js b/workbench/sveltekit/svelte.config.js
new file mode 100644
index 000000000..5d665261c
--- /dev/null
+++ b/workbench/sveltekit/svelte.config.js
@@ -0,0 +1,23 @@
+import node from '@sveltejs/adapter-node';
+import vercel from '@sveltejs/adapter-vercel';
+import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';
+
+// Node adapter needed for ci tests
+const adapter = process.env.VERCEL_DEPLOYMENT_ID ? vercel() : node();
+
+/** @type {import('@sveltejs/kit').Config} */
+const config = {
+ // Consult https://svelte.dev/docs/kit/integrations
+ // for more information about preprocessors
+ preprocess: vitePreprocess(),
+ kit: {
+ 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 SvelteKit's default CSRF protection.
+ csrf: { trustedOrigins: ['*'] },
+ },
+};
+
+export default config;
diff --git a/workbench/sveltekit/tsconfig.json b/workbench/sveltekit/tsconfig.json
new file mode 100644
index 000000000..e3898cb22
--- /dev/null
+++ b/workbench/sveltekit/tsconfig.json
@@ -0,0 +1,19 @@
+{
+ "extends": "./.svelte-kit/tsconfig.json",
+ "compilerOptions": {
+ "allowJs": true,
+ "checkJs": true,
+ "esModuleInterop": true,
+ "forceConsistentCasingInFileNames": true,
+ "resolveJsonModule": true,
+ "skipLibCheck": true,
+ "sourceMap": true,
+ "strict": true,
+ "moduleResolution": "bundler"
+ }
+ // Path aliases are handled by https://svelte.dev/docs/kit/configuration#alias
+ // except $lib which is handled by https://svelte.dev/docs/kit/configuration#files
+ //
+ // To make changes to top-level options such as include and exclude, we recommend extending
+ // the generated config; see https://svelte.dev/docs/kit/configuration#typescript
+}
diff --git a/workbench/sveltekit/vite.config.ts b/workbench/sveltekit/vite.config.ts
new file mode 100644
index 000000000..aede7b139
--- /dev/null
+++ b/workbench/sveltekit/vite.config.ts
@@ -0,0 +1,8 @@
+import { sveltekit } from '@sveltejs/kit/vite';
+import { defineConfig } from 'vite';
+import devtoolsJson from 'vite-plugin-devtools-json';
+import { workflowPlugin } from 'workflow/sveltekit';
+
+export default defineConfig({
+ plugins: [workflowPlugin(), devtoolsJson(), sveltekit()],
+});
diff --git a/workbench/sveltekit/workflows/0_calc.ts b/workbench/sveltekit/workflows/0_calc.ts
new file mode 100644
index 000000000..3323945e6
--- /dev/null
+++ b/workbench/sveltekit/workflows/0_calc.ts
@@ -0,0 +1,15 @@
+// import { FatalError } from 'workflow';
+
+export async function calc(n: number) {
+ 'use workflow';
+ console.log('Simple workflow started');
+ n = await pow(n);
+ console.log('Simple workflow finished');
+ return n;
+}
+
+async function pow(a: number): Promise {
+ 'use step';
+ console.log('Running step pow with arg:', a);
+ return a * a;
+}
diff --git a/workbench/sveltekit/workflows/6_batching.ts b/workbench/sveltekit/workflows/6_batching.ts
new file mode 120000
index 000000000..75feb6fee
--- /dev/null
+++ b/workbench/sveltekit/workflows/6_batching.ts
@@ -0,0 +1 @@
+../../example/workflows/6_batching.ts
\ No newline at end of file
diff --git a/workbench/sveltekit/workflows/98_duplicate_case.ts b/workbench/sveltekit/workflows/98_duplicate_case.ts
new file mode 120000
index 000000000..49404a981
--- /dev/null
+++ b/workbench/sveltekit/workflows/98_duplicate_case.ts
@@ -0,0 +1 @@
+../../example/workflows/98_duplicate_case.ts
\ No newline at end of file
diff --git a/workbench/sveltekit/workflows/99_e2e.ts b/workbench/sveltekit/workflows/99_e2e.ts
new file mode 120000
index 000000000..a7eb151e6
--- /dev/null
+++ b/workbench/sveltekit/workflows/99_e2e.ts
@@ -0,0 +1 @@
+../../example/workflows/99_e2e.ts
\ No newline at end of file