* perf: add 5s delay to benchmark steps to simulate real work
* perf: add realistic workloads to benchmark steps
Add realistic workloads to benchmark step functions:
- doWork() - 1 second delay to simulate real computation
- stressTestStep() - 1 second delay to simulate real computation
- genBenchStream() - generates ~5KB of data in 50 chunks
- transformStream() - uppercases stream content (renamed from doubleNumbers)
Add "Slurp Time" metric to stream benchmarks measuring time from
first byte to complete stream consumption, complementing TTFB.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(e2e): increase dev test timeouts for Windows
Windows file watching and rebuilding is slower than macOS/Linux,
causing the 10s timeouts to fail. Increased to 30s to accommodate.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix(ci): treat community world failures as warnings, not errors
Community world tests/benchmarks are now non-blocking:
- Removed from has_failures check in both tests.yml and benchmarks.yml
- Added separate has_warnings output for community failures
- Split PR comment notices: ❌ for failures, ⚠️ for community warnings
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: apply PR review suggestions
- Fix chunk size calculation: chunkSize - 11 for ~100 bytes per chunk
- Remove redundant metadata check (always truthy)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
The publish-results job was being skipped because it depends on the
summary job which uses `if: always()`. When a job uses `always()`,
downstream jobs need to also use `always()` in their condition,
otherwise GitHub Actions skips them when any upstream job fails.
This was causing e2e-results.json to never be published to gh-pages.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: compare benchmarks against PR base branch instead of main
- Use github.event.pull_request.base.ref instead of hardcoded main
- Remove search_artifacts: true to ensure most recent baseline is used
- For stacked PRs, this compares against the parent PR's baseline
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: group failed e2e tests by category and app in summary
Instead of listing each failed test as a separate item, group them by:
1. Category (world): e.g., "Community Worlds", "Vercel Production"
2. App (framework): e.g., "mongodb", "turso", "nextjs-turbopack"
This makes the summary much more readable when there are many failures.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: ensure local E2E tests always produce JSON output
- Add 'fastify' to app detection list in aggregate-e2e-results.js
- Change && to ; so e2e tests run even if dev.test.ts fails
- This ensures local-dev, local-prod, and local-postgres categories
appear in the E2E summary comment
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: ensure local E2E tests always produce JSON output
- Add 'fastify' to app detection list in aggregate-e2e-results.js
- Change && to ; so e2e tests run even if dev.test.ts fails
- This ensures local-dev, local-prod, and local-postgres categories
appear in the E2E summary comment
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* feat: publish CI results to GitHub Pages for docs
- Add generate-docs-data.js script to create JSON summaries from CI artifacts
- Add publish-results job to tests.yml and benchmarks.yml workflows
- Update docs/lib/worlds-data.ts to fetch from GitHub Pages URLs
- Results published to https://vercel.github.io/workflow/ci/
This allows the docs worlds page to display actual test/benchmark
results without requiring a GITHUB_TOKEN.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: correct outputFile path for local E2E test artifacts
The --outputFile path was using ../../ which placed files outside the
repo because pnpm run test:e2e executes from workspace root, not from
the cd'd workbench directory. This prevented local-dev, local-prod, and
local-postgres test results from being uploaded as artifacts.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: show green checkmark for skipped tests instead of warning
Skipped tests are intentional and shouldn't show as warnings in the
E2E test summary comments.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* feat: use collapsible sections in benchmark PR comment
Wrap each benchmark, stream benchmarks section, and summary tables in
<details> toggles to make the PR comment more compact and readable.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* feat: add Vercel observability links to benchmark PR comments
- Store runId in benchmark timing data
- Add project-slug to Vercel benchmark matrix
- Pass WORKFLOW_VERCEL_PROJECT_SLUG env var to benchmarks
- Store Vercel metadata (teamSlug, projectSlug, environment) in timing files
- Generate observability deep links for each Vercel world benchmark
- Show observability links below Production (Vercel) tables
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* fix: use correct Vercel project slugs for observability links
- nextjs-turbopack → example-nextjs-workflow-turbopack
- nitro-v3 → workbench-nitro-workflow
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
---------
Co-authored-by: Claude <noreply@anthropic.com>
* perf: use Map for invocationsQueue (O(1) lookup/delete)
Replace array-based invocationsQueue with Map<string, QueueItem> for
O(1) lookup and delete operations. This eliminates the O(n²) complexity
from findIndex + splice patterns in step.ts, hook.ts, and sleep.ts.
Changes:
- private.ts: Change invocationsQueue type from QueueItem[] to Map
- workflow.ts: Initialize with new Map() instead of []
- global.ts: Accept Map or array in WorkflowSuspension, add single-pass counting
- step.ts: Use Map.set/has/delete instead of push/findIndex/splice
- hook.ts: Use Map.set/delete instead of push/findIndex/splice
- sleep.ts: Use Map.set/get/delete instead of push/find/findIndex/splice
This is Phase 1 of the high-concurrency workflow optimization plan.
See beads issue wrk-fyx for details.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
* Update packages/core/src/step.ts
Co-authored-by: Nathan Rajlich <n@n8.io>
---------
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Nathan Rajlich <n@n8.io>
nitro, sveltekit and astro and additional vite integrations will use `hotUpdate` for hmr in the future. this adds a `@workflow/vite` package to standardize hmr in workflows
Primary motivation here is to allow a `use workflow` function to be non-exported, meaning that it is called by `start()` in i.e. an API route within the same function:
- SWC plugin now emits `globalThis.__private_workflows.set(workflowId, fn)` directly after setting `workflowId`
- Non-exported workflow functions are now properly registered and can be invoked
- Removed runtime iteration over exports in the workflow bundle - registration happens at transform time
- Simplified virtual entry generation in base-builder
- Updated turbopack rules to include `*.mjs`, `*.mts`, `*.cjs`, `*.cts` in addition to existing extensions
- Fixed TypeScript detection for `.mts` and `.cts` files across all transform plugins
- Updated esbuild `resolveExtensions` to include `.mts` and `.cts`
- Updated the file watcher's `watchableExtensions` to include `.cts`
### TL;DR
Modified the SWC plugin to set the `workflowId` property for all workflow functions in workflow mode, regardless of whether they are exported.
### What changed?
- Removed conditional logic that only added the `workflowId` property to exported workflow functions in workflow mode
- Now all workflow functions will have the `workflowId` property set, regardless of export status
- Updated test fixtures to reflect this change
### How to test?
1. Create a workflow file with non-exported workflow functions
2. Verify that the compiled output includes `workflowId` properties for all workflow functions
3. Run the existing test suite to ensure all tests pass with the updated behavior
### Why make this change?
This change ensures consistent behavior for all workflow functions. Previously, only exported workflow functions received the `workflowId` property in workflow mode, which could lead to inconsistent behavior when referencing non-exported workflow functions within the same file. This change makes the behavior more predictable and ensures all workflow functions are properly identified.
Set `workflowId` property directly after function declarations in the SWC plugin.
### What changed?
Modified the SWC plugin to add the `workflowId` property immediately after workflow function declarations instead of collecting and adding them at the end of the file. This change applies to all transform modes (Client, Step, and Workflow).
The key changes include:
- Removed mode-specific conditions for adding `workflowId` assignments
- Simplified the code by adding `workflowId` inline for all workflow functions
- Cleared the `workflow_exports_to_expand` collection since it's no longer needed
### How to test?
1. Run the test suite to verify that all tests pass with the updated output format
2. Check the test fixtures to confirm that `workflowId` properties now appear directly after their respective function declarations
3. Verify that the behavior is consistent across all transform modes
### Why make this change?
This change improves code organization by placing the `workflowId` property closer to its associated function declaration, making the generated code more readable and maintainable. It also simplifies the plugin's logic by using a consistent approach across all transform modes rather than handling different modes separately.
Added support for workflow transformation with `export { fnName }` syntax.
### What changed?
Enhanced the SWC plugin to properly handle workflow functions that are exported using the named export syntax (`export { fnName }`). Previously, the plugin only properly handled workflow functions that were directly exported with `export function` or `export const`.
The implementation now:
- Collects names that are exported via `export { ... }` syntax in a first pass
- Applies the appropriate transformations to workflow functions that are later exported
- Ensures workflowId is correctly assigned to functions exported with this syntax
- Works across all transformation modes (Client, Step, and Workflow)
### How to test?
Test with files that use the named export syntax:
```javascript
async function workflowFunction() {
'use workflow';
// function body
}
export { workflowFunction };
```
Verify that the transformation correctly:
- Adds workflowId to the function
- Applies the appropriate mode-specific transformations
- Preserves the export statement
### Why make this change?
This change ensures consistent behavior across different export syntaxes. Previously, workflow functions exported with the `export { fnName }` syntax weren't properly transformed, which could lead to runtime errors or unexpected behavior. This enhancement provides developers with more flexibility in how they structure and export their workflow functions.
Add new benchmark workflows to reproduce reported issues with Promise.race
and Promise.all falling over when array sizes exceed a few hundred.
New workflows:
- promiseAllStressTestWorkflow(count) - Tests Promise.all with many concurrent steps
- promiseRaceStressTestLargeWorkflow(count) - Tests Promise.race with Map pattern
New benchmarks at 100, 500, and 1000 concurrent step scales for both
Promise.all and Promise.race patterns.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>