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
5 changes: 5 additions & 0 deletions .changeset/add-type-safety.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@workflow/builders": patch
---

Add type safety for builder configurations with discriminated unions
9 changes: 8 additions & 1 deletion packages/builders/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
export { BaseBuilder } from './base-builder.js';
export { StandaloneBuilder } from './standalone.js';
export { VercelBuildOutputAPIBuilder } from './vercel-build-output-api.js';
export type { WorkflowConfig, BuildTarget } from './types.js';
export type {
WorkflowConfig,
BuildTarget,
StandaloneConfig,
VercelBuildOutputConfig,
NextConfig,
SvelteKitConfig,
} from './types.js';
export { validBuildTargets, isValidBuildTarget } from './types.js';
export type { WorkflowManifest } from './apply-swc-transform.js';
export { applySwcTransform } from './apply-swc-transform.js';
Expand Down
60 changes: 55 additions & 5 deletions packages/builders/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,13 @@ export const validBuildTargets = [
] as const;
export type BuildTarget = (typeof validBuildTargets)[number];

export interface WorkflowConfig {
/**
* Common configuration options shared across all builder types.
*/
interface BaseWorkflowConfig {
watch?: boolean;
dirs: string[];
workingDir: string;
buildTarget: BuildTarget;
stepsBundlePath: string;
workflowsBundlePath: string;
webhookBundlePath: string;

// Optionally generate a client library for workflow execution. The preferred
// method of using workflow is to use a loader within a framework (like
Expand All @@ -25,6 +24,57 @@ export interface WorkflowConfig {
workflowManifestPath?: string;
}

/**
* Configuration for standalone (CLI-based) builds.
*/
export interface StandaloneConfig extends BaseWorkflowConfig {
buildTarget: 'standalone';
stepsBundlePath: string;
workflowsBundlePath: string;
webhookBundlePath: string;
}

/**
* Configuration for Vercel Build Output API builds.
*/
export interface VercelBuildOutputConfig extends BaseWorkflowConfig {
buildTarget: 'vercel-build-output-api';
stepsBundlePath: string;
workflowsBundlePath: string;
webhookBundlePath: string;
}

/**
* Configuration for Next.js builds.
*/
export interface NextConfig extends BaseWorkflowConfig {
buildTarget: 'next';
// Next.js builder computes paths dynamically, so these are not used
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If they're not used then why include them?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that StandaloneBuilder extends BaseBuilder (which uses the WorkflowConfig in it's constructor). StandaloneBuilder then tries accessing properties like stepsBundlePath but TS doesn't know if those exist since we don't narrow the types.

i think we can do something (maybe in another pr?) to add types to each config for each builder class. like inside NextBuilder it would be protected config: NextConfig.

stepsBundlePath: string;
workflowsBundlePath: string;
webhookBundlePath: string;
}

/**
* Configuration for SvelteKit builds.
*/
export interface SvelteKitConfig extends BaseWorkflowConfig {
buildTarget: 'sveltekit';
// SvelteKit builder computes paths dynamically, so these are not used
stepsBundlePath: string;
workflowsBundlePath: string;
webhookBundlePath: string;
}

/**
* Discriminated union of all builder configuration types.
*/
export type WorkflowConfig =
| StandaloneConfig
| VercelBuildOutputConfig
| NextConfig
| SvelteKitConfig;

export function isValidBuildTarget(
target: string | undefined
): target is BuildTarget {
Expand Down
4 changes: 2 additions & 2 deletions packages/sveltekit/src/builder.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { constants } from 'node:fs';
import { access, mkdir, readFile, stat, writeFile } from 'node:fs/promises';
import { join, resolve } from 'node:path';
import { BaseBuilder, type WorkflowConfig } from '@workflow/builders';
import { BaseBuilder, type SvelteKitConfig } from '@workflow/builders';

// Helper function code for converting SvelteKit requests to standard Request objects
const SVELTEKIT_REQUEST_CONVERTER = `
Expand All @@ -18,7 +18,7 @@ async function convertSvelteKitRequest(request) {
`;

export class SvelteKitBuilder extends BaseBuilder {
constructor(config?: Partial<WorkflowConfig>) {
constructor(config?: Partial<SvelteKitConfig>) {
super({
...config,
dirs: ['workflows'],
Expand Down