Skip to content

Commit 945a946

Browse files
pranaygpadriandlamvercel[bot]
authored
Normalize Workbenches (#283)
* Normalize Workbenches Normalize trigger scripts across workbenches fix: include hono in local build test test: include src dir for test test: add workflow dir config in test to fix sveltekit dev tests add temp 7_full in example wokrflow format fix(sveltekit): detecting workflow folders and customizable dir Remove 7_full and 1_simple error replace API symlink in webpack workbench Fix sveltekit and vite tests Fix sveltekit symlinks Test fixes Fix sveltekit workflows path Dont symlink routes in vite Include e2e tests for hono and vite fix error tests post normalization wip - attempted fixes * Add claude demo command * fix: normalize workbench tests (#292) * Proper stacktrace propogation in world Proper stacktrace propogation in world * Standardize the error type in the world spec * Normalize Workbenches Normalize trigger scripts across workbenches fix: include hono in local build test test: include src dir for test test: add workflow dir config in test to fix sveltekit dev tests add temp 7_full in example wokrflow format fix(sveltekit): detecting workflow folders and customizable dir Remove 7_full and 1_simple error replace API symlink in webpack workbench Fix sveltekit and vite tests Fix sveltekit symlinks Test fixes Fix sveltekit workflows path Dont symlink routes in vite Include e2e tests for hono and vite * fix error tests post normalization * fix(sveltekit): reading file on hmr delete * changeset * fix(vite): add resolve symlink script * fix(vite): missing building on hmr * test local builder in vite * test: increase timeout on hookWorkflow * test: ignore vite based apps in crossFileWorkflow * test: fix nitro based apps status codes * fix: intercept default vite spa handler on 404 workflow routes * fix: vite hook route returning 422 * test: use 422 for hookWorkflow expected * test: fix hono returning 404 * chore: add comment to middleware to clarify * make api route for duplicate case * revert * revert: nitro builder * add back nitro unhandled rejection logic * test: add hono * changeset * fix: unused method * fix: remove duplicate import * remove * chore: add comments to clarify * test remove vite symlink script --------- Co-authored-by: Pranay Prakash <[email protected]> * refactor: add top level resolve symlinks script * fix: cleanup builder directories (#319) * fix: add sveltekit server routes to builder * fix: remove root workflow dir check * fix missing root level workflow route * Fix: The constructor now hardcodes `dirs: ['src/routes', 'src/lib']` which silently ignores any user\-provided `dirs` option passed to the plugin\, breaking the documented API and removing support for custom workflow directories\. * Fix: The test expectations don\'t match the new implementation of `getWorkflowDirs()`\. The mock provides `scanDirs` which the new code no longer uses\, and the new implementation adds scanning of `routesDir` and `apiDir` instead\. * fix(nitro): use src dir --------- Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com> Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com> * refactor(nitro): use suppressUndefinedRejections * revert: sveltekit builder --------- Co-authored-by: Adrian <[email protected]> Co-authored-by: Vercel <vercel[bot]@users.noreply.github.com> Co-authored-by: vercel[bot] <35613825+vercel[bot]@users.noreply.github.com>
1 parent 9e179a8 commit 945a946

File tree

85 files changed

+952
-331
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

85 files changed

+952
-331
lines changed

.changeset/eager-lands-rhyme.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@workflow/nitro": patch
3+
---
4+
5+
Add Vite middleware to handle 404s in workflow routes from Nitro and silence undefined unhandled rejections

.changeset/five-planets-push.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@workflow/sveltekit": patch
3+
---
4+
5+
Fix SvelteKit plugin reading deleted files on HMR

.claude/commands/demo.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
description: Run the 7_full demo workflow
3+
allowed-tools: Bash(curl:*), Bash(npx workflow:*), Bash(pnpm dev)
4+
---
5+
6+
7+
Start the $ARUGMENTS workbench (default to the nextjs turboback workbench available in the workbenches directory). Run it in dev mode, and also start the workflow web UI (run `npx workflow web` inside the appropriate workbench directory).
8+
9+
Then trigger the 7_full.ts workflow example. you can see how to trigger a specific example by looking at the trigger API route for the workbench - it is probably just a POST request using bash (maybe curl) to this endpoint: <http://localhost:3000/api/trigger\?workflowFile\=workflows/7_full.ts\&workflowFn\=handleUserSignup>>

.claude/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"Bash(pnpm build:*)",
99
"Bash(pnpm typecheck:*)"
1010
],
11-
"deny": ["Bash(curl:*)", "Read(./.env)", "Read(./.env.*)"],
11+
"deny": ["Read(./.env)", "Read(./.env.*)"],
1212
"additionalDirectories": ["../workflow-server"]
1313
}
1414
}

.github/workflows/tests.yml

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ jobs:
7171
project-id: "prj_oTgiz3SGX2fpZuM6E0P38Ts8de6d"
7272
- name: "sveltekit"
7373
project-id: "prj_MqnBLm71ceXGSnm3Fs8i8gBnI23G"
74+
- name: "hono"
75+
project-id: "prj_p0GIEsfl53L7IwVbosPvi9rPSOYW"
7476
env:
7577
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
7678
TURBO_TEAM: ${{ vars.TURBO_TEAM }}
@@ -181,10 +183,7 @@ jobs:
181183
run: pnpm turbo run build --filter='!./workbench/*'
182184

183185
- name: Resolve symlinks
184-
run: |
185-
if [ -f "workbench/${{ matrix.app.name }}/resolve-symlinks.sh" ]; then
186-
cd workbench/${{ matrix.app.name }} && ./resolve-symlinks.sh
187-
fi
186+
run: ./scripts/resolve-symlinks.sh workbench/${{ matrix.app.name }}
188187

189188
- name: Run E2E Tests
190189
run: cd workbench/${{ matrix.app.name }} && pnpm dev & echo "starting tests in 10 seconds" && sleep 10 && pnpm vitest run packages/core/e2e/dev.test.ts && pnpm run test:e2e

packages/core/e2e/dev.test.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ export interface DevTestConfig {
1010
apiFileImportPath: string;
1111
/** The workflow file to modify for testing HMR. Defaults to '3_streams.ts' */
1212
testWorkflowFile?: string;
13+
/** The workflows directory relative to appPath. Defaults to 'workflows' */
14+
workflowsDir?: string;
1315
}
1416

1517
function getConfigFromEnv(): DevTestConfig | null {
@@ -39,6 +41,7 @@ export function createDevTests(config?: DevTestConfig) {
3941
finalConfig.generatedWorkflowPath
4042
);
4143
const testWorkflowFile = finalConfig.testWorkflowFile ?? '3_streams.ts';
44+
const workflowsDir = finalConfig.workflowsDir ?? 'workflows';
4245
const restoreFiles: Array<{ path: string; content: string }> = [];
4346

4447
afterEach(async () => {
@@ -55,7 +58,7 @@ export function createDevTests(config?: DevTestConfig) {
5558
});
5659

5760
test('should rebuild on workflow change', { timeout: 10_000 }, async () => {
58-
const workflowFile = path.join(appPath, 'workflows', testWorkflowFile);
61+
const workflowFile = path.join(appPath, workflowsDir, testWorkflowFile);
5962

6063
const content = await fs.readFile(workflowFile, 'utf8');
6164

@@ -83,7 +86,7 @@ export async function myNewWorkflow() {
8386
});
8487

8588
test('should rebuild on step change', { timeout: 10_000 }, async () => {
86-
const stepFile = path.join(appPath, 'workflows', testWorkflowFile);
89+
const stepFile = path.join(appPath, workflowsDir, testWorkflowFile);
8790

8891
const content = await fs.readFile(stepFile, 'utf8');
8992

@@ -114,7 +117,11 @@ export async function myNewStep() {
114117
'should rebuild on adding workflow file',
115118
{ timeout: 10_000 },
116119
async () => {
117-
const workflowFile = path.join(appPath, 'workflows', 'new-workflow.ts');
120+
const workflowFile = path.join(
121+
appPath,
122+
workflowsDir,
123+
'new-workflow.ts'
124+
);
118125

119126
await fs.writeFile(
120127
workflowFile,
@@ -132,7 +139,7 @@ export async function myNewStep() {
132139

133140
await fs.writeFile(
134141
apiFile,
135-
`import '${finalConfig.apiFileImportPath}/workflows/new-workflow';
142+
`import '${finalConfig.apiFileImportPath}/${workflowsDir}/new-workflow';
136143
${apiFileContent}`
137144
);
138145

packages/core/e2e/e2e.test.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,10 +90,11 @@ describe('e2e', () => {
9090
output: 133,
9191
});
9292
// In local vs. vercel backends, the workflow name is different, so we check for either,
93-
// since this test runs against both.
93+
// since this test runs against both. Also different workbenches have different directory structures.
9494
expect(json.workflowName).toBeOneOf([
9595
`workflow//example/${workflow.workflowFile}//${workflow.workflowFn}`,
9696
`workflow//${workflow.workflowFile}//${workflow.workflowFn}`,
97+
`workflow//src/${workflow.workflowFile}//${workflow.workflowFn}`,
9798
]);
9899
});
99100

@@ -154,7 +155,10 @@ describe('e2e', () => {
154155
method: 'POST',
155156
body: JSON.stringify({ token: 'invalid' }),
156157
});
157-
expect(res.status).toBe(404);
158+
// NOTE: For Nitro apps (Vite, Hono, etc.) in dev mode, status 404 does some
159+
// unexpected stuff and could return a Vite SPA fallback or can cause a Hono route to hang.
160+
// This is because Nitro passes the 404 requests to the dev server to handle.
161+
expect(res.status).toBeOneOf([404, 422]);
158162
body = await res.json();
159163
expect(body).toBeNull();
160164

@@ -578,14 +582,16 @@ describe('e2e', () => {
578582
expect(returnValue.cause).toHaveProperty('stack');
579583
expect(typeof returnValue.cause.stack).toBe('string');
580584

581-
// Known issue: SvelteKit dev mode has incorrect source map mappings for bundled imports.
585+
// Known issue: vite-based frameworks dev mode has incorrect source map mappings for bundled imports.
582586
// esbuild with bundle:true inlines helpers.ts but source maps incorrectly map to 99_e2e.ts
583587
// This works correctly in production and other frameworks.
584588
// TODO: Investigate esbuild source map generation for bundled modules
585-
const isSvelteKitDevMode =
586-
process.env.APP_NAME === 'sveltekit' && isLocalDeployment();
589+
const isViteBasedFrameworkDevMode =
590+
(process.env.APP_NAME === 'sveltekit' ||
591+
process.env.APP_NAME === 'vite') &&
592+
isLocalDeployment();
587593

588-
if (!isSvelteKitDevMode) {
594+
if (!isViteBasedFrameworkDevMode) {
589595
// Stack trace should include frames from the helper module (helpers.ts)
590596
expect(returnValue.cause.stack).toContain('helpers.ts');
591597
}

packages/core/e2e/local-build.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ describe.each([
1212
'vite',
1313
'sveltekit',
1414
'nuxt',
15+
'hono',
1516
])('e2e', (project) => {
1617
test('builds without errors', { timeout: 180_000 }, async () => {
1718
// skip if we're targeting specific app to test

packages/nitro/src/builders.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,19 +63,25 @@ export class LocalBuilder extends BaseBuilder {
6363
inputFiles,
6464
});
6565

66+
const webhookRouteFile = join(this.#outDir, 'webhook.mjs');
67+
6668
await this.createWebhookBundle({
67-
outfile: join(this.#outDir, 'webhook.mjs'),
69+
outfile: webhookRouteFile,
6870
bundle: false,
71+
suppressUndefinedRejections: true,
6972
});
7073
}
7174
}
7275

7376
export function getWorkflowDirs(nitro: Nitro) {
77+
const srcDir = nitro.options.srcDir || nitro.options.rootDir;
78+
7479
return unique(
7580
[
7681
...(nitro.options.workflow?.dirs ?? []),
77-
join(nitro.options.rootDir, 'workflows'),
78-
...nitro.options.scanDirs.map((dir) => join(dir, 'workflows')),
82+
join(srcDir, 'workflows'),
83+
join(srcDir, nitro.options.routesDir || 'routes'),
84+
join(srcDir, nitro.options.apiDir || 'api'),
7985
].map((dir) => resolve(nitro.options.rootDir, dir))
8086
).sort();
8187
}

packages/nitro/src/index.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,14 @@ function addVirtualHandler(nitro: Nitro, route: string, buildPath: string) {
9191
// Nitro v3+ (native web handlers)
9292
nitro.options.virtual[`#${buildPath}`] = /* js */ `
9393
import { POST } from "${join(nitro.options.buildDir, buildPath)}";
94-
export default ({ req }) => POST(req);
94+
export default async ({ req }) => {
95+
try {
96+
return await POST(req);
97+
} catch (error) {
98+
console.error('Handler error:', error);
99+
return new Response('Internal Server Error', { status: 500 });
100+
}
101+
};
95102
`;
96103
}
97104
}

0 commit comments

Comments
 (0)