Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .changeset/bitter-guests-flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---
---
46 changes: 46 additions & 0 deletions .github/workflows/docs-typecheck.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Docs Typecheck

on:
push:
branches:
- main
tags:
- "!*"
pull_request:

concurrency:
# Unique group for this workflow and branch
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
docs-typecheck:
name: Documentation Code Samples
runs-on: ubuntu-latest
timeout-minutes: 5
env:
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
steps:
- name: Checkout Repo
uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v3
with:
version: 10.14.0

- name: Setup Node.js 22.x
uses: actions/setup-node@v4
with:
node-version: 22.x
cache: "pnpm"

- name: Install Dependencies
run: pnpm install --frozen-lockfile

- name: Build packages
run: pnpm build

- name: Type-check documentation code samples
run: pnpm test:docs
2 changes: 2 additions & 0 deletions docs/content/docs/ai/human-in-the-loop.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ export const bookingApprovalHook = defineHook({

Create a tool that creates a hook instance using the tool call ID as the token. The UI will use this ID to submit the approval.

{/* @skip-typecheck: incomplete code sample */}
```typescript title="workflows/chat/steps/tools.ts" lineNumbers
import { z } from "zod";

Expand Down Expand Up @@ -236,6 +237,7 @@ export function BookingApproval({ toolCallId, input, output }: BookingApprovalPr

Use the component we just created to render the tool call and approval controls in your chat interface:

{/* @skip-typecheck: incomplete code sample */}
```typescript title="app/page.tsx" lineNumbers
// ... existing imports ...
import { BookingApproval } from "@/components/booking-approval";
Expand Down
3 changes: 3 additions & 0 deletions docs/content/docs/ai/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ OPENAI_API_KEY=...

Then modify your API endpoint to use the OpenAI provider:

{/* @skip-typecheck: incomplete code sample */}
```typescript title="app/api/chat/route.ts" lineNumbers
// ...
import { openai } from "@workflow/ai/openai"; // [!code highlight]
Expand Down Expand Up @@ -240,6 +241,7 @@ export default withWorkflow(nextConfig);

Move the agent logic into a separate function, which will serve as our workflow definition.

{/* @skip-typecheck: Shows two mutually exclusive model options */}
```typescript title="workflows/chat/workflow.ts" lineNumbers
import { DurableAgent } from "@workflow/ai/agent"; // [!code highlight]
import { getWritable } from "workflow"; // [!code highlight]
Expand Down Expand Up @@ -313,6 +315,7 @@ Key changes:

Mark all tool definitions with `"use step"` to make them durable. This enables automatic retries and observability for each tool call:

{/* @skip-typecheck: incomplete code sample */}
```typescript title="workflows/chat/steps/tools.ts​" lineNumbers
// ...

Expand Down
1 change: 1 addition & 0 deletions docs/content/docs/ai/resumable-streams.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Let's add stream resumption to our Flight Booking Agent that we build in the [Bu

Modify your chat endpoint to include the workflow run ID in a response header. The Run ID uniquely identifies the run's stream, so it allows the client to know which stream to reconnect to.

{/* @skip-typecheck: incomplete code sample */}
```typescript title="app/api/chat/route.ts" lineNumbers
// ... imports ...

Expand Down
3 changes: 3 additions & 0 deletions docs/content/docs/ai/sleep-and-delays.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ export const flightBookingTools = {

To round it off, extend the UI to display the tool call status. This can be done either by displaying the tool call information directly, or by emitting custom data parts to the stream (see [Streaming Updates from Tools](/docs/ai/streaming-updates-from-tools) for more details). In this case, since there aren't any fine-grained progress updates to show, we'll just display the tool call information directly:

{/* @skip-typecheck: incomplete code sample */}
```typescript title="app/page.tsx" lineNumbers
export default function ChatPage() {

Expand Down Expand Up @@ -194,6 +195,8 @@ export async function pollForResult(jobId: string) {
async function checkJobStatus(jobId: string) {
"use step";
// Check job status...
const response = await fetch(`/api/jobs/${jobId}`);
return response.json();
}
```

Expand Down
2 changes: 2 additions & 0 deletions docs/content/docs/ai/streaming-updates-from-tools.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ The `type` field must be a string starting with `data-` followed by your custom

Use [`getWritable()`](/docs/api-reference/workflow/get-writable) inside a step function to get a handle to the stream. This is the same stream that the LLM and other tools calls are writing to, so we can inject out own data packets directly.

{/* @skip-typecheck: incomplete code sample */}
```typescript title="workflows/chat/steps/tools.ts" lineNumbers
import { getWritable } from "workflow"; // [!code highlight]
import type { UIMessageChunk } from "ai";
Expand Down Expand Up @@ -88,6 +89,7 @@ Key points:

Update your chat component to detect and render the custom data parts. Data parts are stored in the message's `parts` array alongside text and tool invocation parts:

{/* @skip-typecheck: incomplete code sample */}
```typescript title="app/page.tsx" lineNumbers
{message.parts.map((part, partIndex) => {
// Render text parts
Expand Down
9 changes: 6 additions & 3 deletions docs/content/docs/api-reference/workflow/get-writable.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -274,10 +274,13 @@ async function streamTextStep(
}

reader.releaseLock();

// Close the stream
writer.close();
writer.releaseLock();

// Return the result for the workflow to use
return {
messages: await result.response.then((r) => r.messages),
finishReason: await result.finishReason,
};
}

async function endStream(writable: WritableStream<UIMessageChunk>) {
Expand Down
7 changes: 7 additions & 0 deletions docs/content/docs/deploying/world/local-world.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ The queue automatically detects your development server's port and adjusts the q

The local world provides a simple authentication implementation since no authentication is required or enforced in local development.

{/* @skip-typecheck: incomplete code sample */}
```typescript
getAuthHeaders(): Promise<Record<string, string>> {
return Promise.resolve({});
Expand All @@ -67,6 +68,7 @@ export WORKFLOW_LOCAL_DATA_DIR=./custom-workflow-data

**Programmatically:**

{/* @skip-typecheck: incomplete code sample */}
```typescript
import { createLocalWorld } from "@workflow/world-local";

Expand All @@ -79,6 +81,7 @@ By default, the local world **automatically detects** which port your applicatio

**Auto-detection example** (recommended):

{/* @skip-typecheck: incomplete code sample */}
```typescript
import { createLocalWorld } from "@workflow/world-local";

Expand All @@ -92,6 +95,7 @@ If auto-detection fails, the world will fall back to the `PORT` environment vari

You can override the auto-detected port by specifying it explicitly:

{/* @skip-typecheck: incomplete code sample */}
```typescript
import { createLocalWorld } from "@workflow/world-local";

Expand All @@ -116,6 +120,7 @@ export WORKFLOW_LOCAL_BASE_URL=https://local.example.com:3000

**Programmatically:**

{/* @skip-typecheck: incomplete code sample */}
```typescript
import { createLocalWorld } from "@workflow/world-local";

Expand Down Expand Up @@ -207,6 +212,7 @@ For production deployments, use the [Vercel World](/docs/deploying/world/vercel-

Creates a local world instance:

{/* @skip-typecheck: incomplete code sample */}
```typescript
function createLocalWorld(
args?: Partial<{
Expand All @@ -230,6 +236,7 @@ function createLocalWorld(

**Examples:**

{/* @skip-typecheck: incomplete code sample */}
```typescript
import { createLocalWorld } from "@workflow/world-local";

Expand Down
2 changes: 2 additions & 0 deletions docs/content/docs/deploying/world/vercel-world.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ The Vercel world implements security best practices:

Creates a Vercel world instance:

{/* @skip-typecheck: incomplete code sample */}
```typescript
function createVercelWorld(
config?: APIConfig
Expand All @@ -156,6 +157,7 @@ function createVercelWorld(

**Example:**

{/* @skip-typecheck: incomplete code sample */}
```typescript
import { createVercelWorld } from "@workflow/world-vercel";

Expand Down
1 change: 1 addition & 0 deletions docs/content/docs/errors/serialization-failed.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Functions, class instances, symbols, and other non-serializable types cannot be

### Passing Functions

{/* @skip-typecheck: incomplete code sample */}
```typescript lineNumbers
// Error - functions cannot be serialized
export async function processWorkflow() {
Expand Down
1 change: 1 addition & 0 deletions docs/content/docs/foundations/control-flow-patterns.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Common distributed control flow patterns are simple to implement in workflows an

The simplest way to orchestrate steps is to execute them one after another, where each step can be dependent on the previous step.

{/* @skip-typecheck: incomplete code sample */}
```typescript lineNumbers
export async function dataPipelineWorkflow(data: any) {
"use workflow";
Expand Down
1 change: 1 addition & 0 deletions docs/content/docs/foundations/hooks.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,7 @@ When using custom tokens:

Both hooks and webhooks support iteration, making them perfect for long-running event loops:

{/* @skip-typecheck: incomplete code sample */}
```typescript
const hook = createHook<Event>();

Expand Down
2 changes: 1 addition & 1 deletion docs/content/docs/foundations/starting-workflows.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ When you call `start()`, it returns a [`Run`](/docs/api-reference/workflow-api/s
import { start } from "workflow/api";
import { processOrder } from "./workflows/process-order";

const run = await start(processOrder, [orderId]);
const run = await start(processOrder, [/* orderId */]);

// The run object has properties you can await
console.log("Run ID:", run.runId);
Expand Down
1 change: 1 addition & 0 deletions docs/content/docs/foundations/streaming.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,7 @@ Streams are automatically closed when the workflow run completes, but explicitly

**Use typed streams for type safety:**

{/* @skip-typecheck: incomplete code sample */}
```typescript lineNumbers
const writable = getWritable<MyDataType>();
const writer = writable.getWriter();
Expand Down
1 change: 1 addition & 0 deletions docs/content/docs/foundations/workflows-and-steps.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ Step functions are primarily meant to be used inside a workflow.
Calling a step from outside a workflow or from another step will essentially run the step in the same process like a normal function (in other words, the `use step` directive is a no-op). This means you can reuse step functions in other parts of your codebase without needing to duplicate business logic.


{/* @skip-typecheck: incomplete code sample */}
```typescript lineNumbers
async function updateUser(userId: string) {
"use step";
Expand Down
1 change: 1 addition & 0 deletions docs/content/docs/getting-started/nuxt.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ This will also automatically enable the TypeScript plugin, which provides helpfu
<AccordionContent className="[&_p]:my-2">
The TypeScript plugin is enabled by default. If you need to disable it, you can configure it in your `nuxt.config.ts`:

{/* @skip-typecheck: incomplete code sample */}
```typescript title="nuxt.config.ts" lineNumbers
export default defineNuxtConfig({
modules: ["workflow/nuxt"],
Expand Down
7 changes: 7 additions & 0 deletions docs/content/docs/how-it-works/code-transform.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Workflows use special directives to mark code for transformation by the Workflow

Workflows use two directives to mark functions for special handling:

{/* @skip-typecheck: incomplete code sample */}
```typescript
export async function handleUserSignup(email: string) {
"use workflow"; // [!code highlight]
Expand Down Expand Up @@ -69,6 +70,7 @@ flowchart LR

**Input:**

{/* @skip-typecheck: incomplete code sample */}
```typescript
export async function createUser(email: string) {
"use step";
Expand All @@ -78,6 +80,7 @@ export async function createUser(email: string) {

**Output:**

{/* @skip-typecheck: incomplete code sample */}
```typescript
import { registerStepFunction } from "workflow/internal/private"; // [!code highlight]

Expand Down Expand Up @@ -106,6 +109,7 @@ registerStepFunction("step//workflows/user.js//createUser", createUser); // [!co

**Input:**

{/* @skip-typecheck: incomplete code sample */}
```typescript
export async function createUser(email: string) {
"use step";
Expand All @@ -121,6 +125,7 @@ export async function handleUserSignup(email: string) {

**Output:**

{/* @skip-typecheck: incomplete code sample */}
```typescript
export async function createUser(email: string) {
return globalThis[Symbol.for("WORKFLOW_USE_STEP")]("step//workflows/user.js//createUser")(email); // [!code highlight]
Expand Down Expand Up @@ -155,6 +160,7 @@ handleUserSignup.workflowId = "workflow//workflows/user.js//handleUserSignup"; /

**Input:**

{/* @skip-typecheck: incomplete code sample */}
```typescript
export async function handleUserSignup(email: string) {
"use workflow";
Expand All @@ -165,6 +171,7 @@ export async function handleUserSignup(email: string) {

**Output:**

{/* @skip-typecheck: incomplete code sample */}
```typescript
export async function handleUserSignup(email: string) {
throw new Error("You attempted to execute ..."); // [!code highlight]
Expand Down
5 changes: 5 additions & 0 deletions docs/content/docs/how-it-works/framework-integrations.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ Each file exports a `POST` function that accepts Web standard `Request` objects.

Client mode transforms your application code to provide better DX. Add a Bun plugin to apply this transformation at runtime:

{/* @skip-typecheck: incomplete code sample */}
```typescript title="workflow-plugin.ts" lineNumbers
import { plugin } from "bun";
import { transform } from "@swc/core";
Expand Down Expand Up @@ -127,6 +128,7 @@ preload = ["./workflow-plugin.ts"]

Wire up the generated handlers to HTTP endpoints using `Bun.serve()`:

{/* @skip-typecheck: incomplete code sample */}
```typescript title="server.ts" lineNumbers
import flow from "./.well-known/workflow/v1/flow.js";
import step from "./.well-known/workflow/v1/step.js";
Expand Down Expand Up @@ -215,6 +217,7 @@ This will default to scanning the `./workflows` top-level directory for workflow

**Option 2: Extend `BaseBuilder`** (recommended)

{/* @skip-typecheck: @workflow/cli internal module */}
```typescript lineNumbers
import { BaseBuilder } from "@workflow/cli/dist/lib/builders/base-builder";

Expand Down Expand Up @@ -253,6 +256,7 @@ If your framework supports virtual server routes and dev mode watching, make sur

Hook into your framework's build:

{/* @skip-typecheck: incomplete code sample */}
```typescript title="pseudocode.ts" lineNumbers
framework.hooks.hook("build:before", async () => {
await new MyFrameworkBuilder(framework).build();
Expand Down Expand Up @@ -308,6 +312,7 @@ Route the three endpoints to the generated handlers. The exact implementation de

In the bun example above, we left routing to the user. Essentially, the user has to serve routes like this:

{/* @skip-typecheck: incomplete code sample */}
```typescript title="server.ts" lineNumbers
import flow from "./.well-known/workflow/v1/flow.js";
import step from "./.well-known/workflow/v1/step.js";
Expand Down
Loading
Loading