@contractspec/integration.workflow-devkit 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -19,6 +19,17 @@ Workflow DevKit compiler and runtime bridges for ContractSpec `WorkflowSpec`.
19
19
  - `@contractspec/integration.workflow-devkit/agent-adapter`
20
20
  - `@contractspec/integration.workflow-devkit/next`
21
21
 
22
+ ## Workflow vs step boundary
23
+
24
+ Workflow DevKit code runs across two different execution contexts:
25
+
26
+ - Workflow functions (`"use workflow"`) orchestrate control flow and must stay deterministic.
27
+ - Step functions (`"use step"`) perform the actual Node.js work.
28
+
29
+ This matches the Workflow docs: workflow functions do not have full Node.js access, while step functions do. Keep Node.js core modules, SDK clients, database access, and other side effects inside `use step` helpers. The workflow function itself should only compose Workflow primitives and call step helpers.
30
+
31
+ For `WorkflowSpec` authoring in Workflow-hosted apps, import the safe authoring API from `@contractspec/lib.contracts-spec/workflow/spec`, not the broad `@contractspec/lib.contracts-spec/workflow` barrel.
32
+
22
33
  ## Next.js example
23
34
 
24
35
  ```ts
@@ -52,6 +63,8 @@ import { onboardingWorkflow } from "./onboarding.workflow";
52
63
  export async function runOnboarding(input: Record<string, unknown>, bridge: WorkflowDevkitRuntimeBridge) {
53
64
  "use workflow";
54
65
 
66
+ // Keep this function deterministic. Node.js work belongs in `use step`
67
+ // helpers that your bridge dispatches to.
55
68
  return runWorkflowSpecWithWorkflowDevkit({
56
69
  spec: onboardingWorkflow,
57
70
  initialData: input,
@@ -1,80 +1,2 @@
1
1
  // @bun
2
- // src/agent-adapter.ts
3
- class InMemoryWorkflowDevkitCheckpointStore {
4
- envelopes = new Map;
5
- async delete(sessionId) {
6
- this.envelopes.delete(sessionId);
7
- }
8
- async load(sessionId) {
9
- return this.envelopes.get(sessionId) ?? null;
10
- }
11
- async save(envelope) {
12
- this.envelopes.set(envelope.sessionId, envelope);
13
- }
14
- }
15
-
16
- class InMemoryWorkflowDevkitSuspensionStore {
17
- states = new Map;
18
- async clear(sessionId) {
19
- this.states.delete(sessionId);
20
- }
21
- async get(sessionId) {
22
- return this.states.get(sessionId) ?? null;
23
- }
24
- async set(sessionId, state) {
25
- this.states.set(sessionId, state);
26
- }
27
- }
28
- function createWorkflowDevkitAgentRuntimeAdapterBundle(options = {}) {
29
- const checkpointStore = options.checkpointStore ?? new InMemoryWorkflowDevkitCheckpointStore;
30
- const suspensionStore = options.suspensionStore ?? new InMemoryWorkflowDevkitSuspensionStore;
31
- const resolveSessionToken = options.resolveSessionToken ?? defaultWorkflowDevkitAgentToken;
32
- return {
33
- key: "workflow-devkit",
34
- checkpoint: {
35
- delete(sessionId) {
36
- return checkpointStore.delete(sessionId);
37
- },
38
- load(sessionId) {
39
- return checkpointStore.load(sessionId);
40
- },
41
- save(envelope) {
42
- return checkpointStore.save(envelope);
43
- }
44
- },
45
- suspendResume: {
46
- async resume(params) {
47
- await suspensionStore.set(params.sessionId, {
48
- input: params.input,
49
- metadata: params.metadata,
50
- reason: "resumed",
51
- resumedAt: new Date,
52
- suspendedAt: new Date
53
- });
54
- if (options.workflowApi) {
55
- await options.workflowApi.resumeHook(resolveSessionToken(params.sessionId), {
56
- input: params.input,
57
- metadata: params.metadata,
58
- sessionId: params.sessionId
59
- });
60
- }
61
- },
62
- async suspend(params) {
63
- await suspensionStore.set(params.sessionId, {
64
- metadata: params.metadata,
65
- reason: params.reason,
66
- suspendedAt: new Date
67
- });
68
- }
69
- }
70
- };
71
- }
72
- function defaultWorkflowDevkitAgentToken(sessionId) {
73
- return `agent-session:${sessionId}`;
74
- }
75
- export {
76
- defaultWorkflowDevkitAgentToken,
77
- createWorkflowDevkitAgentRuntimeAdapterBundle,
78
- InMemoryWorkflowDevkitSuspensionStore,
79
- InMemoryWorkflowDevkitCheckpointStore
80
- };
2
+ class x{envelopes=new Map;async delete(b){this.envelopes.delete(b)}async load(b){return this.envelopes.get(b)??null}async save(b){this.envelopes.set(b.sessionId,b)}}class z{states=new Map;async clear(b){this.states.delete(b)}async get(b){return this.states.get(b)??null}async set(b,j){this.states.set(b,j)}}function C(b={}){let j=b.checkpointStore??new x,q=b.suspensionStore??new z,A=b.resolveSessionToken??B;return{key:"workflow-devkit",checkpoint:{delete(g){return j.delete(g)},load(g){return j.load(g)},save(g){return j.save(g)}},suspendResume:{async resume(g){if(await q.set(g.sessionId,{input:g.input,metadata:g.metadata,reason:"resumed",resumedAt:new Date,suspendedAt:new Date}),b.workflowApi)await b.workflowApi.resumeHook(A(g.sessionId),{input:g.input,metadata:g.metadata,sessionId:g.sessionId})},async suspend(g){await q.set(g.sessionId,{metadata:g.metadata,reason:g.reason,suspendedAt:new Date})}}}}function B(b){return`agent-session:${b}`}export{B as defaultWorkflowDevkitAgentToken,C as createWorkflowDevkitAgentRuntimeAdapterBundle,z as InMemoryWorkflowDevkitSuspensionStore,x as InMemoryWorkflowDevkitCheckpointStore};
@@ -1,79 +1 @@
1
- // src/agent-adapter.ts
2
- class InMemoryWorkflowDevkitCheckpointStore {
3
- envelopes = new Map;
4
- async delete(sessionId) {
5
- this.envelopes.delete(sessionId);
6
- }
7
- async load(sessionId) {
8
- return this.envelopes.get(sessionId) ?? null;
9
- }
10
- async save(envelope) {
11
- this.envelopes.set(envelope.sessionId, envelope);
12
- }
13
- }
14
-
15
- class InMemoryWorkflowDevkitSuspensionStore {
16
- states = new Map;
17
- async clear(sessionId) {
18
- this.states.delete(sessionId);
19
- }
20
- async get(sessionId) {
21
- return this.states.get(sessionId) ?? null;
22
- }
23
- async set(sessionId, state) {
24
- this.states.set(sessionId, state);
25
- }
26
- }
27
- function createWorkflowDevkitAgentRuntimeAdapterBundle(options = {}) {
28
- const checkpointStore = options.checkpointStore ?? new InMemoryWorkflowDevkitCheckpointStore;
29
- const suspensionStore = options.suspensionStore ?? new InMemoryWorkflowDevkitSuspensionStore;
30
- const resolveSessionToken = options.resolveSessionToken ?? defaultWorkflowDevkitAgentToken;
31
- return {
32
- key: "workflow-devkit",
33
- checkpoint: {
34
- delete(sessionId) {
35
- return checkpointStore.delete(sessionId);
36
- },
37
- load(sessionId) {
38
- return checkpointStore.load(sessionId);
39
- },
40
- save(envelope) {
41
- return checkpointStore.save(envelope);
42
- }
43
- },
44
- suspendResume: {
45
- async resume(params) {
46
- await suspensionStore.set(params.sessionId, {
47
- input: params.input,
48
- metadata: params.metadata,
49
- reason: "resumed",
50
- resumedAt: new Date,
51
- suspendedAt: new Date
52
- });
53
- if (options.workflowApi) {
54
- await options.workflowApi.resumeHook(resolveSessionToken(params.sessionId), {
55
- input: params.input,
56
- metadata: params.metadata,
57
- sessionId: params.sessionId
58
- });
59
- }
60
- },
61
- async suspend(params) {
62
- await suspensionStore.set(params.sessionId, {
63
- metadata: params.metadata,
64
- reason: params.reason,
65
- suspendedAt: new Date
66
- });
67
- }
68
- }
69
- };
70
- }
71
- function defaultWorkflowDevkitAgentToken(sessionId) {
72
- return `agent-session:${sessionId}`;
73
- }
74
- export {
75
- defaultWorkflowDevkitAgentToken,
76
- createWorkflowDevkitAgentRuntimeAdapterBundle,
77
- InMemoryWorkflowDevkitSuspensionStore,
78
- InMemoryWorkflowDevkitCheckpointStore
79
- };
1
+ class x{envelopes=new Map;async delete(b){this.envelopes.delete(b)}async load(b){return this.envelopes.get(b)??null}async save(b){this.envelopes.set(b.sessionId,b)}}class z{states=new Map;async clear(b){this.states.delete(b)}async get(b){return this.states.get(b)??null}async set(b,j){this.states.set(b,j)}}function C(b={}){let j=b.checkpointStore??new x,q=b.suspensionStore??new z,A=b.resolveSessionToken??B;return{key:"workflow-devkit",checkpoint:{delete(g){return j.delete(g)},load(g){return j.load(g)},save(g){return j.save(g)}},suspendResume:{async resume(g){if(await q.set(g.sessionId,{input:g.input,metadata:g.metadata,reason:"resumed",resumedAt:new Date,suspendedAt:new Date}),b.workflowApi)await b.workflowApi.resumeHook(A(g.sessionId),{input:g.input,metadata:g.metadata,sessionId:g.sessionId})},async suspend(g){await q.set(g.sessionId,{metadata:g.metadata,reason:g.reason,suspendedAt:new Date})}}}}function B(b){return`agent-session:${b}`}export{B as defaultWorkflowDevkitAgentToken,C as createWorkflowDevkitAgentRuntimeAdapterBundle,z as InMemoryWorkflowDevkitSuspensionStore,x as InMemoryWorkflowDevkitCheckpointStore};
@@ -1,100 +1 @@
1
- // src/chat-routes.ts
2
- import { createUIMessageStreamResponse } from "ai";
3
- import { getRun, resumeHook, start } from "workflow/api";
4
- var WORKFLOW_RUN_ID_HEADER = "x-workflow-run-id";
5
- var WORKFLOW_STREAM_TAIL_INDEX_HEADER = "x-workflow-stream-tail-index";
6
- function createWorkflowDevkitStartRoute(options) {
7
- const workflowApi = options.workflowApi ?? { start };
8
- return async (request) => {
9
- const body = await request.json();
10
- const args = await options.buildArgs(body, request);
11
- const run = await workflowApi.start(options.workflow, args);
12
- if (options.createResponse) {
13
- return options.createResponse({ body, run });
14
- }
15
- if (!run.readable) {
16
- return new Response(JSON.stringify({ runId: run.runId }), {
17
- headers: {
18
- "Content-Type": "application/json",
19
- [WORKFLOW_RUN_ID_HEADER]: run.runId
20
- }
21
- });
22
- }
23
- return createUIMessageStreamResponse({
24
- headers: {
25
- [WORKFLOW_RUN_ID_HEADER]: run.runId
26
- },
27
- stream: run.readable
28
- });
29
- };
30
- }
31
- function createWorkflowDevkitFollowUpRoute(options) {
32
- const workflowApi = options.workflowApi ?? { resumeHook };
33
- return async (request, context) => {
34
- const runId = context.params.runId ?? context.params.id;
35
- if (!runId) {
36
- return new Response(JSON.stringify({ error: "Missing run id" }), {
37
- status: 400
38
- });
39
- }
40
- const body = await request.json();
41
- const token = options.resolveToken({ body, request, runId });
42
- const payload = options.buildPayload ? await options.buildPayload(body, request) : body;
43
- await workflowApi.resumeHook(token, payload);
44
- return new Response(JSON.stringify({ ok: true }), {
45
- headers: {
46
- "Content-Type": "application/json",
47
- [WORKFLOW_RUN_ID_HEADER]: runId
48
- }
49
- });
50
- };
51
- }
52
- function createWorkflowDevkitStreamRoute(options = {}) {
53
- const workflowApi = options.workflowApi ?? { getRun };
54
- return async (request, context) => {
55
- const runId = context.params.runId ?? context.params.id;
56
- if (!runId) {
57
- return new Response(JSON.stringify({ error: "Missing run id" }), {
58
- status: 400
59
- });
60
- }
61
- const run = workflowApi.getRun(runId);
62
- if (!run) {
63
- return new Response(JSON.stringify({ error: "Workflow run not found" }), {
64
- status: 404
65
- });
66
- }
67
- const startIndex = readStartIndex(request.url);
68
- const readable = run.getReadable ? run.getReadable({ startIndex }) : run.readable;
69
- if (!readable) {
70
- return new Response(JSON.stringify({ error: "Run has no readable stream" }), {
71
- status: 404
72
- });
73
- }
74
- const tailIndex = await readable.getTailIndex?.();
75
- return createUIMessageStreamResponse({
76
- headers: {
77
- ...tailIndex !== undefined ? {
78
- [WORKFLOW_STREAM_TAIL_INDEX_HEADER]: String(tailIndex)
79
- } : {}
80
- },
81
- stream: readable
82
- });
83
- };
84
- }
85
- function readStartIndex(url) {
86
- const parsed = new URL(url);
87
- const rawStartIndex = parsed.searchParams.get("startIndex");
88
- if (!rawStartIndex) {
89
- return;
90
- }
91
- const parsedStartIndex = Number(rawStartIndex);
92
- return Number.isFinite(parsedStartIndex) ? parsedStartIndex : undefined;
93
- }
94
- export {
95
- createWorkflowDevkitStreamRoute,
96
- createWorkflowDevkitStartRoute,
97
- createWorkflowDevkitFollowUpRoute,
98
- WORKFLOW_STREAM_TAIL_INDEX_HEADER,
99
- WORKFLOW_RUN_ID_HEADER
100
- };
1
+ import{createUIMessageStreamResponse as V}from"ai";import{getRun as Y,resumeHook as Z,start as $}from"workflow/api";var P="x-workflow-run-id",F="x-workflow-stream-tail-index";function X(j){let C=j.workflowApi??{start:$};return async(v)=>{let z=await v.json(),B=await j.buildArgs(z,v),h=await C.start(j.workflow,B);if(j.createResponse)return j.createResponse({body:z,run:h});if(!h.readable)return new Response(JSON.stringify({runId:h.runId}),{headers:{"Content-Type":"application/json",[P]:h.runId}});return V({headers:{[P]:h.runId},stream:h.readable})}}function m(j){let C=j.workflowApi??{resumeHook:Z};return async(v,z)=>{let B=z.params.runId??z.params.id;if(!B)return new Response(JSON.stringify({error:"Missing run id"}),{status:400});let h=await v.json(),J=j.resolveToken({body:h,request:v,runId:B}),G=j.buildPayload?await j.buildPayload(h,v):h;return await C.resumeHook(J,G),new Response(JSON.stringify({ok:!0}),{headers:{"Content-Type":"application/json",[P]:B}})}}function D(j={}){let C=j.workflowApi??{getRun:Y};return async(v,z)=>{let B=z.params.runId??z.params.id;if(!B)return new Response(JSON.stringify({error:"Missing run id"}),{status:400});let h=C.getRun(B);if(!h)return new Response(JSON.stringify({error:"Workflow run not found"}),{status:404});let J=H(v.url),G=h.getReadable?h.getReadable({startIndex:J}):h.readable;if(!G)return new Response(JSON.stringify({error:"Run has no readable stream"}),{status:404});let Q=await G.getTailIndex?.();return V({headers:{...Q!==void 0?{[F]:String(Q)}:{}},stream:G})}}function H(j){let v=new URL(j).searchParams.get("startIndex");if(!v)return;let z=Number(v);return Number.isFinite(z)?z:void 0}export{D as createWorkflowDevkitStreamRoute,X as createWorkflowDevkitStartRoute,m as createWorkflowDevkitFollowUpRoute,F as WORKFLOW_STREAM_TAIL_INDEX_HEADER,P as WORKFLOW_RUN_ID_HEADER};
@@ -1,117 +1,7 @@
1
- // src/helpers.ts
2
- import { evaluateExpression } from "@contractspec/lib.contracts-spec/workflow/expression";
3
- function inferWorkflowDevkitBehavior(step) {
4
- return step.runtime?.workflowDevkit?.behavior ?? step.type;
5
- }
6
- function resolveWorkflowDevkitEntryStepId(spec) {
7
- const entryStepId = spec.definition.entryStepId ?? spec.definition.steps[0]?.id;
8
- if (!entryStepId) {
9
- throw new Error(`Workflow ${spec.meta.key}.v${spec.meta.version} does not define an entry step.`);
10
- }
11
- return entryStepId;
12
- }
13
- function resolveWorkflowDevkitRunIdentity(spec, runIdentity) {
14
- if (runIdentity) {
15
- return runIdentity;
16
- }
17
- const strategy = spec.runtime?.workflowDevkit?.runIdentity?.strategy ?? "meta-key-version";
18
- const prefix = spec.runtime?.workflowDevkit?.runIdentity?.prefix;
19
- const baseIdentity = strategy === "meta-key-version" ? `${spec.meta.key}.v${spec.meta.version}` : `${spec.meta.key}.v${spec.meta.version}`;
20
- return prefix ? `${prefix}:${baseIdentity}` : baseIdentity;
21
- }
22
- function resolveWorkflowDevkitWaitToken(spec, step, runIdentity) {
23
- const runtime = step.runtime?.workflowDevkit;
24
- if (!runtime) {
25
- return;
26
- }
27
- const explicitToken = runtime.hookWait?.token ?? runtime.webhookWait?.token ?? runtime.approvalWait?.token ?? runtime.streamSession?.followUpToken;
28
- if (explicitToken) {
29
- return explicitToken;
30
- }
31
- const tokenStrategy = spec.runtime?.workflowDevkit?.hookTokens?.strategy ?? "deterministic";
32
- const prefix = spec.runtime?.workflowDevkit?.hookTokens?.prefix ?? spec.meta.key;
33
- const stableStepId = sanitizeIdentifier(step.id);
34
- if (tokenStrategy === "session-scoped") {
35
- const resolvedRunIdentity = sanitizeIdentifier(resolveWorkflowDevkitRunIdentity(spec, runIdentity));
36
- return `${prefix}:${resolvedRunIdentity}:${stableStepId}`;
37
- }
38
- if (tokenStrategy === "step-scoped") {
39
- return `${prefix}:v${spec.meta.version}:${stableStepId}`;
40
- }
41
- return `${prefix}:${stableStepId}`;
42
- }
43
- function resolveWorkflowDevkitNextStepId(spec, step, data, input, output) {
44
- const transitions = spec.definition.transitions.filter((transition) => transition.from === step.id);
45
- for (const transition of transitions) {
46
- if (evaluateExpression(transition.condition, {
47
- data,
48
- input,
49
- output
50
- })) {
51
- return transition.to;
52
- }
53
- }
54
- return null;
55
- }
56
- function mergeWorkflowDevkitData(current, input, output) {
57
- const next = { ...current };
58
- if (isRecord(input)) {
59
- Object.assign(next, input);
60
- }
61
- if (isRecord(output)) {
62
- Object.assign(next, output);
63
- }
64
- return next;
65
- }
66
- function sanitizeIdentifier(value) {
67
- return value.replace(/[^a-zA-Z0-9_-]+/g, "-");
68
- }
69
- function isRecord(value) {
70
- return value != null && typeof value === "object" && !Array.isArray(value);
71
- }
1
+ import{evaluateExpression as Y}from"@contractspec/lib.contracts-spec/workflow/expression";function Q(j){return j.runtime?.workflowDevkit?.behavior??j.type}function U(j){let q=j.definition.entryStepId??j.definition.steps[0]?.id;if(!q)throw Error(`Workflow ${j.meta.key}.v${j.meta.version} does not define an entry step.`);return q}function Z(j,q){if(q)return q;let C=j.runtime?.workflowDevkit?.runIdentity?.strategy??"meta-key-version",A=j.runtime?.workflowDevkit?.runIdentity?.prefix,J=C==="meta-key-version"?`${j.meta.key}.v${j.meta.version}`:`${j.meta.key}.v${j.meta.version}`;return A?`${A}:${J}`:J}function V(j,q,C){let A=q.runtime?.workflowDevkit;if(!A)return;let J=A.hookWait?.token??A.webhookWait?.token??A.approvalWait?.token??A.streamSession?.followUpToken;if(J)return J;let L=j.runtime?.workflowDevkit?.hookTokens?.strategy??"deterministic",H=j.runtime?.workflowDevkit?.hookTokens?.prefix??j.meta.key,M=K(q.id);if(L==="session-scoped"){let X=K(Z(j,C));return`${H}:${X}:${M}`}if(L==="step-scoped")return`${H}:v${j.meta.version}:${M}`;return`${H}:${M}`}function h(j,q,C,A,J){let L=j.definition.transitions.filter((H)=>H.from===q.id);for(let H of L)if(Y(H.condition,{data:C,input:A,output:J}))return H.to;return null}function D(j,q,C){let A={...j};if(O(q))Object.assign(A,q);if(O(C))Object.assign(A,C);return A}function K(j){return j.replace(/[^a-zA-Z0-9_-]+/g,"-")}function O(j){return j!=null&&typeof j==="object"&&!Array.isArray(j)}function _(j){return{entryStepId:U(j),hostTarget:j.runtime?.workflowDevkit?.hostTarget??"generic",hookTokenStrategy:j.runtime?.workflowDevkit?.hookTokens?.strategy??"deterministic",integrationMode:j.runtime?.workflowDevkit?.integrationMode??"manual",runIdentityStrategy:j.runtime?.workflowDevkit?.runIdentity?.strategy??"meta-key-version",specKey:j.meta.key,specVersion:j.meta.version,steps:j.definition.steps.map((q)=>({behavior:Q(q),id:q.id,label:q.label,operationRef:q.action?.operation?`${q.action.operation.key}.v${q.action.operation.version}`:void 0,runtime:q.runtime?.workflowDevkit,transitions:j.definition.transitions.filter((C)=>C.from===q.id).map((C)=>({condition:C.condition,to:C.to})),type:q.type,waitToken:V(j,q)}))}}function z(j,q){let C=_(j),A=q.workflowFunctionName??`${K(q.exportName)}WorkflowDevkit`;return{genericBootstrap:$(q.exportName,A),manifest:JSON.stringify(C,null,2),nextFollowUpRoute:G(A),nextStartRoute:E(A),nextStreamRoute:B(),workflowModule:P(q.exportName,q.specImportPath,A)}}function $(j,q){return`import { ${q} } from "./${K(j)}.workflow-devkit";
72
2
 
73
- // src/compiler.ts
74
- function compileWorkflowSpecToWorkflowDevkit(spec) {
75
- return {
76
- entryStepId: resolveWorkflowDevkitEntryStepId(spec),
77
- hostTarget: spec.runtime?.workflowDevkit?.hostTarget ?? "generic",
78
- hookTokenStrategy: spec.runtime?.workflowDevkit?.hookTokens?.strategy ?? "deterministic",
79
- integrationMode: spec.runtime?.workflowDevkit?.integrationMode ?? "manual",
80
- runIdentityStrategy: spec.runtime?.workflowDevkit?.runIdentity?.strategy ?? "meta-key-version",
81
- specKey: spec.meta.key,
82
- specVersion: spec.meta.version,
83
- steps: spec.definition.steps.map((step) => ({
84
- behavior: inferWorkflowDevkitBehavior(step),
85
- id: step.id,
86
- label: step.label,
87
- operationRef: step.action?.operation ? `${step.action.operation.key}.v${step.action.operation.version}` : undefined,
88
- runtime: step.runtime?.workflowDevkit,
89
- transitions: spec.definition.transitions.filter((transition) => transition.from === step.id).map((transition) => ({
90
- condition: transition.condition,
91
- to: transition.to
92
- })),
93
- type: step.type,
94
- waitToken: resolveWorkflowDevkitWaitToken(spec, step)
95
- }))
96
- };
97
- }
98
- function generateWorkflowDevkitArtifacts(spec, options) {
99
- const compilation = compileWorkflowSpecToWorkflowDevkit(spec);
100
- const workflowFunctionName = options.workflowFunctionName ?? `${sanitizeIdentifier(options.exportName)}WorkflowDevkit`;
101
- return {
102
- genericBootstrap: createGenericBootstrapTemplate(options.exportName, workflowFunctionName),
103
- manifest: JSON.stringify(compilation, null, 2),
104
- nextFollowUpRoute: createFollowUpRouteTemplate(workflowFunctionName),
105
- nextStartRoute: createStartRouteTemplate(workflowFunctionName),
106
- nextStreamRoute: createStreamRouteTemplate(),
107
- workflowModule: createWorkflowModuleTemplate(options.exportName, options.specImportPath, workflowFunctionName)
108
- };
109
- }
110
- function createGenericBootstrapTemplate(exportName, workflowFunctionName) {
111
- return `import { ${workflowFunctionName} } from "./${sanitizeIdentifier(exportName)}.workflow-devkit";
112
-
113
- export const ${sanitizeIdentifier(exportName)}WorkflowDevkitBootstrap = {
114
- workflow: ${workflowFunctionName},
3
+ export const ${K(j)}WorkflowDevkitBootstrap = {
4
+ workflow: ${q},
115
5
  createBridge() {
116
6
  return {
117
7
  executeAutomationStep: async () => {
@@ -120,46 +10,37 @@ export const ${sanitizeIdentifier(exportName)}WorkflowDevkitBootstrap = {
120
10
  };
121
11
  },
122
12
  };
123
- `;
124
- }
125
- function createStartRouteTemplate(workflowFunctionName) {
126
- return `import { createWorkflowDevkitStartRoute } from "@contractspec/integration.workflow-devkit";
127
- import { ${workflowFunctionName} } from "./workflow";
13
+ `}function E(j){return`import { createWorkflowDevkitStartRoute } from "@contractspec/integration.workflow-devkit";
14
+ import { ${j} } from "./workflow";
128
15
 
129
16
  export const POST = createWorkflowDevkitStartRoute({
130
- workflow: ${workflowFunctionName},
17
+ workflow: ${j},
131
18
  async buildArgs(body) {
132
19
  return [body];
133
20
  },
134
21
  });
135
- `;
136
- }
137
- function createFollowUpRouteTemplate(workflowFunctionName) {
138
- return `import { createWorkflowDevkitFollowUpRoute } from "@contractspec/integration.workflow-devkit";
22
+ `}function G(j){return`import { createWorkflowDevkitFollowUpRoute } from "@contractspec/integration.workflow-devkit";
139
23
 
140
24
  export const POST = createWorkflowDevkitFollowUpRoute({
141
25
  resolveToken({ runId }) {
142
- return \`${workflowFunctionName}:\${runId}\`;
26
+ return \`${j}:\${runId}\`;
143
27
  },
144
28
  });
145
- `;
146
- }
147
- function createStreamRouteTemplate() {
148
- return `import { createWorkflowDevkitStreamRoute } from "@contractspec/integration.workflow-devkit";
29
+ `}function B(){return`import { createWorkflowDevkitStreamRoute } from "@contractspec/integration.workflow-devkit";
149
30
 
150
31
  export const GET = createWorkflowDevkitStreamRoute();
151
- `;
152
- }
153
- function createWorkflowModuleTemplate(exportName, specImportPath, workflowFunctionName) {
154
- return `import { createHook, createWebhook, sleep } from "workflow";
32
+ `}function P(j,q,C){return`import { createHook, createWebhook, sleep } from "workflow";
155
33
  import { runWorkflowSpecWithWorkflowDevkit } from "@contractspec/integration.workflow-devkit";
156
- import { ${exportName} } from "${specImportPath}";
34
+ import { ${j} } from "${q}";
157
35
 
158
- export async function ${workflowFunctionName}(input = {}, bridge = {}) {
36
+ export async function ${C}(input = {}, bridge = {}) {
159
37
  "use workflow";
160
38
 
39
+ // Keep the workflow function deterministic.
40
+ // Any Node.js or side-effectful logic should live in "use step" helpers that
41
+ // your bridge calls, not in this orchestrator itself.
161
42
  return runWorkflowSpecWithWorkflowDevkit({
162
- spec: ${exportName},
43
+ spec: ${j},
163
44
  initialData: input,
164
45
  bridge,
165
46
  primitives: {
@@ -169,9 +50,4 @@ export async function ${workflowFunctionName}(input = {}, bridge = {}) {
169
50
  },
170
51
  });
171
52
  }
172
- `;
173
- }
174
- export {
175
- generateWorkflowDevkitArtifacts,
176
- compileWorkflowSpecToWorkflowDevkit
177
- };
53
+ `}export{z as generateWorkflowDevkitArtifacts,_ as compileWorkflowSpecToWorkflowDevkit};
@@ -1,80 +1 @@
1
- // src/helpers.ts
2
- import { evaluateExpression } from "@contractspec/lib.contracts-spec/workflow/expression";
3
- function inferWorkflowDevkitBehavior(step) {
4
- return step.runtime?.workflowDevkit?.behavior ?? step.type;
5
- }
6
- function resolveWorkflowDevkitEntryStepId(spec) {
7
- const entryStepId = spec.definition.entryStepId ?? spec.definition.steps[0]?.id;
8
- if (!entryStepId) {
9
- throw new Error(`Workflow ${spec.meta.key}.v${spec.meta.version} does not define an entry step.`);
10
- }
11
- return entryStepId;
12
- }
13
- function resolveWorkflowDevkitRunIdentity(spec, runIdentity) {
14
- if (runIdentity) {
15
- return runIdentity;
16
- }
17
- const strategy = spec.runtime?.workflowDevkit?.runIdentity?.strategy ?? "meta-key-version";
18
- const prefix = spec.runtime?.workflowDevkit?.runIdentity?.prefix;
19
- const baseIdentity = strategy === "meta-key-version" ? `${spec.meta.key}.v${spec.meta.version}` : `${spec.meta.key}.v${spec.meta.version}`;
20
- return prefix ? `${prefix}:${baseIdentity}` : baseIdentity;
21
- }
22
- function resolveWorkflowDevkitWaitToken(spec, step, runIdentity) {
23
- const runtime = step.runtime?.workflowDevkit;
24
- if (!runtime) {
25
- return;
26
- }
27
- const explicitToken = runtime.hookWait?.token ?? runtime.webhookWait?.token ?? runtime.approvalWait?.token ?? runtime.streamSession?.followUpToken;
28
- if (explicitToken) {
29
- return explicitToken;
30
- }
31
- const tokenStrategy = spec.runtime?.workflowDevkit?.hookTokens?.strategy ?? "deterministic";
32
- const prefix = spec.runtime?.workflowDevkit?.hookTokens?.prefix ?? spec.meta.key;
33
- const stableStepId = sanitizeIdentifier(step.id);
34
- if (tokenStrategy === "session-scoped") {
35
- const resolvedRunIdentity = sanitizeIdentifier(resolveWorkflowDevkitRunIdentity(spec, runIdentity));
36
- return `${prefix}:${resolvedRunIdentity}:${stableStepId}`;
37
- }
38
- if (tokenStrategy === "step-scoped") {
39
- return `${prefix}:v${spec.meta.version}:${stableStepId}`;
40
- }
41
- return `${prefix}:${stableStepId}`;
42
- }
43
- function resolveWorkflowDevkitNextStepId(spec, step, data, input, output) {
44
- const transitions = spec.definition.transitions.filter((transition) => transition.from === step.id);
45
- for (const transition of transitions) {
46
- if (evaluateExpression(transition.condition, {
47
- data,
48
- input,
49
- output
50
- })) {
51
- return transition.to;
52
- }
53
- }
54
- return null;
55
- }
56
- function mergeWorkflowDevkitData(current, input, output) {
57
- const next = { ...current };
58
- if (isRecord(input)) {
59
- Object.assign(next, input);
60
- }
61
- if (isRecord(output)) {
62
- Object.assign(next, output);
63
- }
64
- return next;
65
- }
66
- function sanitizeIdentifier(value) {
67
- return value.replace(/[^a-zA-Z0-9_-]+/g, "-");
68
- }
69
- function isRecord(value) {
70
- return value != null && typeof value === "object" && !Array.isArray(value);
71
- }
72
- export {
73
- sanitizeIdentifier,
74
- resolveWorkflowDevkitWaitToken,
75
- resolveWorkflowDevkitRunIdentity,
76
- resolveWorkflowDevkitNextStepId,
77
- resolveWorkflowDevkitEntryStepId,
78
- mergeWorkflowDevkitData,
79
- inferWorkflowDevkitBehavior
80
- };
1
+ import{evaluateExpression as L}from"@contractspec/lib.contracts-spec/workflow/expression";function O(h){return h.runtime?.workflowDevkit?.behavior??h.type}function P(h){let q=h.definition.entryStepId??h.definition.steps[0]?.id;if(!q)throw Error(`Workflow ${h.meta.key}.v${h.meta.version} does not define an entry step.`);return q}function M(h,q){if(q)return q;let A=h.runtime?.workflowDevkit?.runIdentity?.strategy??"meta-key-version",j=h.runtime?.workflowDevkit?.runIdentity?.prefix,C=A==="meta-key-version"?`${h.meta.key}.v${h.meta.version}`:`${h.meta.key}.v${h.meta.version}`;return j?`${j}:${C}`:C}function Q(h,q,A){let j=q.runtime?.workflowDevkit;if(!j)return;let C=j.hookWait?.token??j.webhookWait?.token??j.approvalWait?.token??j.streamSession?.followUpToken;if(C)return C;let F=h.runtime?.workflowDevkit?.hookTokens?.strategy??"deterministic",B=h.runtime?.workflowDevkit?.hookTokens?.prefix??h.meta.key,G=H(q.id);if(F==="session-scoped"){let K=H(M(h,A));return`${B}:${K}:${G}`}if(F==="step-scoped")return`${B}:v${h.meta.version}:${G}`;return`${B}:${G}`}function U(h,q,A,j,C){let F=h.definition.transitions.filter((B)=>B.from===q.id);for(let B of F)if(L(B.condition,{data:A,input:j,output:C}))return B.to;return null}function V(h,q,A){let j={...h};if(J(q))Object.assign(j,q);if(J(A))Object.assign(j,A);return j}function H(h){return h.replace(/[^a-zA-Z0-9_-]+/g,"-")}function J(h){return h!=null&&typeof h==="object"&&!Array.isArray(h)}export{H as sanitizeIdentifier,Q as resolveWorkflowDevkitWaitToken,M as resolveWorkflowDevkitRunIdentity,U as resolveWorkflowDevkitNextStepId,P as resolveWorkflowDevkitEntryStepId,V as mergeWorkflowDevkitData,O as inferWorkflowDevkitBehavior};