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
6 changes: 6 additions & 0 deletions .changeset/nitro-monorepo-project-root.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@workflow/nitro": patch
"@workflow/nuxt": patch
---

Use Nitro's workspace root for workflow module resolution so Nitro and Nuxt monorepo apps can import sibling workspace packages without extra config.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ When enabled, the module:
- Registers the workflow runtime routes under `/.well-known/workflow/v1/`.
- Serves a redirect to the local observability dashboard at `/_workflow` in development.
- Configures Vercel function rules (queue triggers and `maxDuration`) for the workflow routes when deploying to Vercel.
- Uses Nitro's `workspaceDir` as the workflow project root so monorepo apps can import sibling workspace packages without extra workflow config.

## Module Options

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ When enabled, the module:
- Registers the [`workflow/nitro`](/docs/api-reference/workflow-nitro) module on Nuxt's Nitro server, which transforms `"use workflow"` and `"use step"` directives, builds the workflow bundles, and registers the workflow runtime routes under `/.well-known/workflow/v1/`.
- Configures Vite to bundle (rather than externalize) the Workflow SDK packages in SSR mode so workflow code is transformed correctly.
- Enables the `workflow` TypeScript plugin by default for IDE IntelliSense.
- Uses Nuxt/Nitro's detected `workspaceDir` so monorepo apps can import sibling workspace packages without extra workflow config.

## Module Options

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ When enabled, the module:
- Registers the workflow runtime routes under `/.well-known/workflow/v1/`.
- Serves a redirect to the local observability dashboard at `/_workflow` in development.
- Configures Vercel function rules (queue triggers and `maxDuration`) for the workflow routes when deploying to Vercel.
- Uses Nitro's `workspaceDir` as the workflow project root so monorepo apps can import sibling workspace packages without extra workflow config.

## Module Options

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ When enabled, the module:
- Registers the [`workflow/nitro`](/docs/api-reference/workflow-nitro) module on Nuxt's Nitro server, which transforms `"use workflow"` and `"use step"` directives, builds the workflow bundles, and registers the workflow runtime routes under `/.well-known/workflow/v1/`.
- Configures Vite to bundle (rather than externalize) the Workflow SDK packages in SSR mode so workflow code is transformed correctly.
- Enables the `workflow` TypeScript plugin by default for IDE IntelliSense.
- Uses Nuxt/Nitro's detected `workspaceDir` so monorepo apps can import sibling workspace packages without extra workflow config.

## Module Options

Expand Down
24 changes: 17 additions & 7 deletions packages/core/e2e/dev.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,10 @@ export function createDevTests(config?: DevTestConfig) {
appPath,
'app/.well-known/workflow/v1/manifest.json'
);
// Next canary webpack can queue Workflow rediscovery behind route
// compilation long enough that the default budget races test cleanup.
const hmrRediscoveryTimeoutMs = finalConfig.canary ? 120_000 : 50_000;
const flowRouteHmrFuzzTimeoutMs = finalConfig.canary ? 360_000 : 240_000;
const readManifestStepFunctionNames = async (): Promise<string[]> => {
const manifestJson = await fs.readFile(workflowManifestPath, 'utf8');
const manifest = JSON.parse(manifestJson) as {
Expand Down Expand Up @@ -182,6 +186,12 @@ export function createDevTests(config?: DevTestConfig) {
expected: ExpectedHmrLogCount | undefined
) => {
if (typeof expected === 'number') {
// Canary webpack can emit duplicate watcher events for one edit; keep
// stable exact while treating canary counts as lower bounds.
if (finalConfig.canary) {
expect(actual).toBeGreaterThanOrEqual(expected);
return;
}
expect(actual).toBe(expected);
return;
}
Expand All @@ -203,7 +213,7 @@ export function createDevTests(config?: DevTestConfig) {
}
await pollUntil({
description: 'dev server HMR logs to match expected rebuild counts',
timeoutMs: 50_000,
timeoutMs: hmrRediscoveryTimeoutMs,
intervalMs: 250,
check: async () => {
const log = (await readDevServerLog()).slice(cursor);
Expand Down Expand Up @@ -821,7 +831,7 @@ ${apiFileContent}`

test.runIf(shouldRunNextFlowRouteHmrTests)(
'should follow Next flow-route HMR rebuild rules for body-only changes',
{ timeout: 240_000 },
{ timeout: flowRouteHmrFuzzTimeoutMs },
async () => {
assert(deploymentUrl);
setupWorld(deploymentUrl);
Expand Down Expand Up @@ -954,7 +964,7 @@ ${apiFileContent}`

await pollUntil({
description: 'HMR fuzz fixture to appear in the Next manifest',
timeoutMs: 50_000,
timeoutMs: hmrRediscoveryTimeoutMs,
check: async () => {
await prewarm();
expect(await readManifestStepFunctionNames()).toContain(
Expand Down Expand Up @@ -1223,7 +1233,7 @@ export async function hmrFuzzAddedStep() {
assert: async () => {
await pollUntil({
description: 'added step definition to appear in manifest',
timeoutMs: 50_000,
timeoutMs: hmrRediscoveryTimeoutMs,
intervalMs: 500,
check: async () => {
await prewarm();
Expand Down Expand Up @@ -1264,7 +1274,7 @@ export async function hmrFuzzAddedWorkflow() {
assert: async () => {
await pollUntil({
description: 'added workflow definition to appear in manifest',
timeoutMs: 50_000,
timeoutMs: hmrRediscoveryTimeoutMs,
intervalMs: 500,
check: async () => {
await prewarm();
Expand Down Expand Up @@ -1297,7 +1307,7 @@ ${apiFileContent}`
assert: async () => {
await pollUntil({
description: 'added workflow file to appear in manifest',
timeoutMs: 50_000,
timeoutMs: hmrRediscoveryTimeoutMs,
intervalMs: 500,
check: async () => {
await prewarm();
Expand All @@ -1323,7 +1333,7 @@ ${apiFileContent}`
assert: async () => {
await pollUntil({
description: 'removed workflow file to disappear from manifest',
timeoutMs: 50_000,
timeoutMs: hmrRediscoveryTimeoutMs,
intervalMs: 500,
check: async () => {
await prewarm();
Expand Down
2 changes: 2 additions & 0 deletions packages/nitro/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# workflow/nitro

The docs have moved! Refer to them [here](https://workflow-sdk.dev/)

The Nitro module uses Nitro's `workspaceDir` as the workflow project root, so monorepo apps can import sibling workspace packages without extra workflow config.
6 changes: 6 additions & 0 deletions packages/nitro/src/builders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,16 @@ function getNitroStringExternals(nitro: Nitro): string[] | undefined {
return strings && strings.length > 0 ? strings : undefined;
}

function getNitroProjectRoot(nitro: Nitro): string {
return nitro.options.workspaceDir ?? nitro.options.rootDir;
}

export class VercelBuilder extends VercelBuildOutputAPIBuilder {
constructor(nitro: Nitro) {
super({
...createBaseBuilderConfig({
workingDir: nitro.options.rootDir,
projectRoot: getNitroProjectRoot(nitro),
dirs: ['.'], // Different apps that use nitro have different directories
runtime: nitro.options.workflow?.runtime,
sourcemap: nitro.options.workflow?.sourcemap,
Expand Down Expand Up @@ -60,6 +65,7 @@ export class LocalBuilder extends BaseBuilder {
super({
...createBaseBuilderConfig({
workingDir: nitro.options.rootDir,
projectRoot: getNitroProjectRoot(nitro),
watch: nitro.options.dev,
dirs: ['.'], // Different apps that use nitro have different directories
sourcemap: nitro.options.workflow?.sourcemap,
Expand Down
12 changes: 12 additions & 0 deletions packages/nitro/src/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ type StubOptions = {
majorVersion?: number;
dev?: boolean;
preset?: string;
workspaceDir?: string;
workflow?: { runtime?: string };
externals?: {
external?: Array<string | RegExp | ((id: string) => boolean)>;
Expand All @@ -20,6 +21,7 @@ function createNitroStub({
majorVersion,
dev = false,
preset = 'node-server',
workspaceDir = '/tmp/project',
workflow = {},
externals,
vercel,
Expand All @@ -38,6 +40,7 @@ function createNitroStub({
typescript: {},
vercel: vercel ?? {},
virtual: {},
workspaceDir,
workflow,
},
hooks: {
Expand Down Expand Up @@ -320,6 +323,15 @@ describe('@workflow/nitro externals forwarding', () => {
expect(builder.config.externalPackages).toBeUndefined();
});

it('uses nitro workspaceDir as the workflow projectRoot', () => {
const nitro = createNitroStub({
routing: true,
workspaceDir: '/tmp',
});
const builder = new Builder(nitro) as any;
expect(builder.config.projectRoot).toBe('/tmp');
});

it('forwards string entries from nitro.options.externals.external', () => {
const nitro = createNitroStub({
routing: true,
Expand Down
1 change: 1 addition & 0 deletions packages/nuxt/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@

Nuxt module for [Workflow SDK](https://workflow-sdk.dev).

Monorepo workspace package imports work without extra workflow config because `workflow/nuxt` runs through Nitro's detected `workspaceDir`.
Loading