Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
0f62431
Workflows CFG extractor & build plumbing
karthikscale3 Nov 28, 2025
d537846
Adding changeset
karthikscale3 Nov 28, 2025
87a90b8
Merge branch 'main' of github.com:karthikscale3/workflow into karthik…
karthikscale3 Dec 1, 2025
e5f4cd8
Extend manifest.debug.json to include workflow CFG metadata and clean…
karthikscale3 Dec 1, 2025
68b23e0
Extend manifest.debug.json to include workflow CFG metadata and clean…
karthikscale3 Dec 1, 2025
1c45af6
Add unit test coverage for manifest
karthikscale3 Dec 1, 2025
8e21d56
fix
karthikscale3 Dec 2, 2025
b002d81
Add changeset
karthikscale3 Dec 2, 2025
252a0dd
Add changeset
karthikscale3 Dec 2, 2025
8a3f152
Add changeset
karthikscale3 Dec 2, 2025
03cf52c
Merge branch 'main' of github.com:karthikscale3/workflow into karthik…
karthikscale3 Dec 5, 2025
675a989
DCO Remediation Commit for Karthik Kalyanaraman <[email protected]>
karthikscale3 Dec 5, 2025
08f2811
Merge branch 'main' of github.com:karthikscale3/workflow into karthik…
karthikscale3 Dec 5, 2025
a0363a1
fix(builders): extract steps from single-statement control flow bodies
karthikscale3 Dec 5, 2025
2484c01
fix(builders): extract steps from single-statement control flow bodies
karthikscale3 Dec 5, 2025
ef7d741
Merge branch 'main' of github.com:karthikscale3/workflow into karthik…
karthikscale3 Dec 5, 2025
6609b39
Fix imports
karthikscale3 Dec 5, 2025
7437ea3
DCO Remediation Commit for Karthik Kalyanaraman <[email protected]>
karthikscale3 Dec 5, 2025
8208f3c
Merge
karthikscale3 Dec 11, 2025
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/smart-insects-smile.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@workflow/builders": patch
"@workflow/next": patch
---

Add CFG extractor for extracting workflow graph data from bundles
55 changes: 53 additions & 2 deletions packages/builders/src/base-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const EMIT_SOURCEMAPS_FOR_DEBUGGING =
*/
export abstract class BaseBuilder {
protected config: WorkflowConfig;
protected lastWorkflowManifest?: WorkflowManifest;

constructor(config: WorkflowConfig) {
this.config = config;
Expand Down Expand Up @@ -253,6 +254,7 @@ export abstract class BaseBuilder {
* Steps have full Node.js runtime access and handle side effects, API calls, etc.
*
* @param externalizeNonSteps - If true, only bundles step entry points and externalizes other code
* @returns Build context (for watch mode) and the collected workflow manifest
*/
protected async createStepsBundle({
inputFiles,
Expand All @@ -268,7 +270,10 @@ export abstract class BaseBuilder {
outfile: string;
format?: 'cjs' | 'esm';
externalizeNonSteps?: boolean;
}): Promise<esbuild.BuildContext | undefined> {
}): Promise<{
context: esbuild.BuildContext | undefined;
manifest: WorkflowManifest;
}> {
// These need to handle watching for dev to scan for
// new entries and changes to existing ones
const { discoveredSteps: stepFiles } = await this.discoverEntries(
Expand Down Expand Up @@ -389,10 +394,14 @@ export abstract class BaseBuilder {
// Create .gitignore in .swc directory
await this.createSwcGitignore();

// Store the manifest for later use (e.g., graph generation in watch mode)
this.lastWorkflowManifest = workflowManifest;

if (this.config.watch) {
return esbuildCtx;
return { context: esbuildCtx, manifest: workflowManifest };
}
await esbuildCtx.dispose();
return { context: undefined, manifest: workflowManifest };
}

/**
Expand Down Expand Up @@ -838,4 +847,46 @@ export const OPTIONS = handler;`;
// We're intentionally silently ignoring this error - creating .gitignore isn't critical
}
}

/**
* Creates a workflows manifest JSON file by extracting from the bundled workflow file.
* The manifest contains React Flow-compatible graph data for visualizing workflows.
*/
protected async createWorkflowsManifest({
workflowBundlePath,
outfile,
}: {
workflowBundlePath: string;
outfile: string;
}): Promise<void> {
const buildStart = Date.now();
console.log('Creating workflows manifest...');

try {
// Import the graph extractor
const { extractGraphFromBundle } = await import(
'./workflows-extractor.js'
);

// Extract graph from the bundled workflow file
const workflowsManifest =
await extractGraphFromBundle(workflowBundlePath);

// Write the workflows manifest
await this.ensureDirectory(outfile);
await writeFile(outfile, JSON.stringify(workflowsManifest, null, 2));

console.log(
`Created workflows manifest with ${
Object.keys(workflowsManifest.workflows).length
} workflow(s)`,
`${Date.now() - buildStart}ms`
);
} catch (error) {
console.warn(
'Failed to extract workflows from bundle:',
error instanceof Error ? error.message : String(error)
);
}
}
}
24 changes: 22 additions & 2 deletions packages/builders/src/standalone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ export class StandaloneBuilder extends BaseBuilder {
await this.buildWorkflowsBundle(options);
await this.buildWebhookFunction();

// Build workflows manifest from workflow bundle (post-bundle extraction)
const workflowBundlePath = this.resolvePath('.swc/workflows.js');
await this.buildWorkflowsManifest({ workflowBundlePath });

await this.createClientLibrary();
}

Expand All @@ -25,18 +29,20 @@ export class StandaloneBuilder extends BaseBuilder {
inputFiles: string[];
tsBaseUrl?: string;
tsPaths?: Record<string, string[]>;
}): Promise<void> {
}) {
console.log('Creating steps bundle at', this.config.stepsBundlePath);

const stepsBundlePath = this.resolvePath(this.config.stepsBundlePath);
await this.ensureDirectory(stepsBundlePath);

await this.createStepsBundle({
const { manifest } = await this.createStepsBundle({
outfile: stepsBundlePath,
inputFiles,
tsBaseUrl,
tsPaths,
});

return manifest;
}

private async buildWorkflowsBundle({
Expand Down Expand Up @@ -76,4 +82,18 @@ export class StandaloneBuilder extends BaseBuilder {
outfile: webhookBundlePath,
});
}

private async buildWorkflowsManifest({
workflowBundlePath,
}: {
workflowBundlePath: string;
}): Promise<void> {
const workflowsManifestPath = this.resolvePath('.swc/workflows.json');
await this.ensureDirectory(workflowsManifestPath);

await this.createWorkflowsManifest({
workflowBundlePath,
outfile: workflowsManifestPath,
});
}
}
Loading
Loading