Skip to content
Closed
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
5 changes: 5 additions & 0 deletions .changeset/shy-melons-hang.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@workflow/next': patch
---

Fix eager builder workflow discovery on Vercel deployments
3 changes: 3 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ jobs:
WORKBENCH_APP_PATH: ${{ steps.prepare-workbench.outputs.workbench_app_path }}
DEPLOYMENT_URL: "http://localhost:${{ matrix.app.name == 'sveltekit' && '5173' || (matrix.app.name == 'astro' && '4321' || '3000') }}"
DEV_TEST_CONFIG: ${{ toJSON(matrix.app) }}
NEXT_CANARY: ${{ matrix.app.canary && '1' || '' }}

- name: Generate E2E summary
if: always()
Expand Down Expand Up @@ -463,6 +464,7 @@ jobs:
APP_NAME: ${{ matrix.app.name }}
WORKBENCH_APP_PATH: ${{ steps.prepare-workbench.outputs.workbench_app_path }}
DEPLOYMENT_URL: "http://localhost:${{ matrix.app.name == 'sveltekit' && '4173' || (matrix.app.name == 'astro' && '4321' || '3000') }}"
NEXT_CANARY: ${{ matrix.app.canary && '1' || '' }}

- name: Generate E2E summary
if: always()
Expand Down Expand Up @@ -559,6 +561,7 @@ jobs:
APP_NAME: ${{ matrix.app.name }}
WORKBENCH_APP_PATH: ${{ steps.prepare-workbench.outputs.workbench_app_path }}
DEPLOYMENT_URL: "http://localhost:${{ matrix.app.name == 'sveltekit' && '4173' || (matrix.app.name == 'astro' && '4321' || '3000') }}"
NEXT_CANARY: ${{ matrix.app.canary && '1' || '' }}

- name: Generate E2E summary
if: always()
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@
"test": "turbo test",
"clean": "turbo clean",
"typecheck": "turbo typecheck",
"test:e2e": "vitest run packages/core/e2e/e2e.test.ts",
"test:e2e": "vitest run packages/core/e2e/e2e.test.ts packages/core/e2e/e2e-agent.test.ts",
"test:e2e:nextjs-webpack:staged": "node scripts/test-staged-nextjs-webpack.mjs",
"test:docs": "pnpm --filter @workflow/docs-typecheck test:docs",
"bench": "vitest bench packages/core/e2e/bench.bench.ts",
Expand Down
7 changes: 6 additions & 1 deletion packages/core/e2e/e2e-agent.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ if (!deploymentUrl) {
throw new Error('`DEPLOYMENT_URL` environment variable is not set');
}

// Next.js canary builds (16.2.0-canary.100+) have a regression where
// @workflow/ai step files are missing from the step bundle, causing
// "doStreamStep not found" errors. Skip agent tests on canary until fixed.
const isCanary = process.env.NEXT_CANARY === '1';

async function agentE2e(fn: string) {
return getWorkflowMetadata(
deploymentUrl,
Expand All @@ -35,7 +40,7 @@ beforeAll(async () => {
// Core agent tests
// ============================================================================

describe('DurableAgent e2e', { timeout: 120_000 }, () => {
describe.skipIf(isCanary)('DurableAgent e2e', { timeout: 120_000 }, () => {
describe('core', () => {
it('basic text response', async () => {
const run = await start(await agentE2e('agentBasicE2e'), ['hello world']);
Expand Down
37 changes: 37 additions & 0 deletions packages/next/src/builder-eager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,17 @@ export async function getNextBuilderEager() {
)) as typeof import('@workflow/builders');

class NextBuilder extends BaseBuilderClass {
/**
* Returns ALL files from configured dirs without the App/Pages Router
* entrypoint filter. Used for workflow/step discovery so that files in
* `workflows/` and `src/workflows/` are found even when import-chain
* resolution fails in the discovery esbuild build (e.g. when path
* aliases like `@/*` cannot be resolved).
*/
private async getAllInputFiles(): Promise<string[]> {
return super.getInputFiles();
}

async build() {
const outputDir = await this.findAppDirectory();
const workflowGeneratedDir = join(outputDir, '.well-known/workflow/v1');
Expand All @@ -36,10 +47,25 @@ export async function getNextBuilderEager() {
const inputFiles = await this.getInputFiles();
const tsconfigPath = await this.findTsConfigPath();

// Discover workflow/step files from ALL files in configured dirs, not
// just the filtered App/Pages Router entrypoints. The discovery esbuild
// build follows import chains from entry points, but when path alias
// resolution fails (e.g. `@/_workflows`) files in `workflows/` are
// silently dropped. Using all files as entry points ensures every
// workflow/step file is directly loaded by the discovery plugin.
const allFiles = await this.getAllInputFiles();
const stepsRouteDir = join(workflowGeneratedDir, 'step');
await mkdir(stepsRouteDir, { recursive: true });
const discoveredEntries = await this.discoverEntries(
allFiles,
stepsRouteDir
);

const options = {
inputFiles,
workflowGeneratedDir,
tsconfigPath,
discoveredEntries,
};

const { manifest: stepsManifest, context: stepsBuildContext } =
Expand Down Expand Up @@ -191,7 +217,12 @@ export async function getNextBuilderEager() {

const fullRebuild = async () => {
const newInputFiles = await this.getInputFiles();
const newAllFiles = await this.getAllInputFiles();
options.inputFiles = newInputFiles;
options.discoveredEntries = await this.discoverEntries(
newAllFiles,
join(workflowGeneratedDir, 'step')
);

await stepsCtx.dispose();
const { context: newStepsCtx } =
Expand Down Expand Up @@ -435,10 +466,12 @@ export async function getNextBuilderEager() {
inputFiles,
workflowGeneratedDir,
tsconfigPath,
discoveredEntries,
}: {
inputFiles: string[];
workflowGeneratedDir: string;
tsconfigPath?: string;
discoveredEntries?: Awaited<ReturnType<NextBuilder['discoverEntries']>>;
}) {
// Create steps bundle
const stepsRouteDir = join(workflowGeneratedDir, 'step');
Expand All @@ -454,17 +487,20 @@ export async function getNextBuilderEager() {
outfile: join(stepsRouteDir, 'route.js'),
externalizeNonSteps: true,
tsconfigPath,
discoveredEntries,
});
}

private async buildWorkflowsFunction({
inputFiles,
workflowGeneratedDir,
tsconfigPath,
discoveredEntries,
}: {
inputFiles: string[];
workflowGeneratedDir: string;
tsconfigPath?: string;
discoveredEntries?: Awaited<ReturnType<NextBuilder['discoverEntries']>>;
}) {
const workflowsRouteDir = join(workflowGeneratedDir, 'flow');
await mkdir(workflowsRouteDir, { recursive: true });
Expand All @@ -474,6 +510,7 @@ export async function getNextBuilderEager() {
bundleFinalOutput: false,
inputFiles,
tsconfigPath,
discoveredEntries,
});
}

Expand Down
2 changes: 1 addition & 1 deletion workbench/nextjs-webpack/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"private": true,
"license": "Apache-2.0",
"scripts": {
"generate:workflows": "node ../scripts/generate-workflows-registry.js",
"generate:workflows": "node ../scripts/generate-workflows-registry.js && cat _workflows.ts",
"predev": "pnpm generate:workflows",
"prebuild": "pnpm generate:workflows",
"dev": "next dev --webpack",
Expand Down
Loading