@sapiom/orchestration 0.1.0
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/LICENSE +21 -0
- package/README.md +55 -0
- package/dist/cjs/build-manifest.d.ts +15 -0
- package/dist/cjs/build-manifest.js +146 -0
- package/dist/cjs/context.d.ts +45 -0
- package/dist/cjs/context.js +21 -0
- package/dist/cjs/directives.d.ts +89 -0
- package/dist/cjs/directives.js +56 -0
- package/dist/cjs/errors.d.ts +19 -0
- package/dist/cjs/errors.js +49 -0
- package/dist/cjs/index.d.ts +17 -0
- package/dist/cjs/index.js +41 -0
- package/dist/cjs/introspection.d.ts +11 -0
- package/dist/cjs/introspection.js +62 -0
- package/dist/cjs/manifest.d.ts +56 -0
- package/dist/cjs/manifest.js +27 -0
- package/dist/cjs/step.d.ts +42 -0
- package/dist/cjs/step.js +15 -0
- package/dist/cjs/workflow.d.ts +10 -0
- package/dist/cjs/workflow.js +38 -0
- package/dist/esm/build-manifest.d.ts +15 -0
- package/dist/esm/build-manifest.js +141 -0
- package/dist/esm/context.d.ts +45 -0
- package/dist/esm/context.js +17 -0
- package/dist/esm/directives.d.ts +89 -0
- package/dist/esm/directives.js +43 -0
- package/dist/esm/errors.d.ts +19 -0
- package/dist/esm/errors.js +42 -0
- package/dist/esm/index.d.ts +17 -0
- package/dist/esm/index.js +9 -0
- package/dist/esm/introspection.d.ts +11 -0
- package/dist/esm/introspection.js +56 -0
- package/dist/esm/manifest.d.ts +56 -0
- package/dist/esm/manifest.js +24 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/step.d.ts +42 -0
- package/dist/esm/step.js +12 -0
- package/dist/esm/workflow.d.ts +10 -0
- package/dist/esm/workflow.js +33 -0
- package/dist/tsconfig.cjs.tsbuildinfo +1 -0
- package/dist/tsconfig.esm.tsbuildinfo +1 -0
- package/package.json +71 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { z } from 'zod/v4';
|
|
2
|
+
export declare const MANIFEST_PROTOCOL: 1;
|
|
3
|
+
export type ManifestTransition = {
|
|
4
|
+
readonly kind: 'continue';
|
|
5
|
+
readonly target: string;
|
|
6
|
+
} | {
|
|
7
|
+
readonly kind: 'terminate';
|
|
8
|
+
} | {
|
|
9
|
+
readonly kind: 'fail';
|
|
10
|
+
} | {
|
|
11
|
+
readonly kind: 'pause';
|
|
12
|
+
readonly signal: string;
|
|
13
|
+
readonly resumeStep: string;
|
|
14
|
+
};
|
|
15
|
+
export interface WorkflowStepManifest {
|
|
16
|
+
readonly timeoutMs: number | null;
|
|
17
|
+
readonly inputSchema: Record<string, unknown> | null;
|
|
18
|
+
readonly transitions: readonly ManifestTransition[];
|
|
19
|
+
}
|
|
20
|
+
export interface WorkflowManifest {
|
|
21
|
+
readonly protocol: typeof MANIFEST_PROTOCOL;
|
|
22
|
+
readonly name: string;
|
|
23
|
+
readonly entry: string;
|
|
24
|
+
readonly sdkVersion: string;
|
|
25
|
+
readonly artifact: {
|
|
26
|
+
readonly sha256: string;
|
|
27
|
+
readonly entryFile: string;
|
|
28
|
+
};
|
|
29
|
+
readonly steps: Readonly<Record<string, WorkflowStepManifest>>;
|
|
30
|
+
}
|
|
31
|
+
export declare const workflowManifestSchema: z.ZodObject<{
|
|
32
|
+
protocol: z.ZodLiteral<1>;
|
|
33
|
+
name: z.ZodString;
|
|
34
|
+
entry: z.ZodString;
|
|
35
|
+
sdkVersion: z.ZodString;
|
|
36
|
+
artifact: z.ZodObject<{
|
|
37
|
+
sha256: z.ZodString;
|
|
38
|
+
entryFile: z.ZodString;
|
|
39
|
+
}, z.core.$strip>;
|
|
40
|
+
steps: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
41
|
+
timeoutMs: z.ZodUnion<readonly [z.ZodNumber, z.ZodNull]>;
|
|
42
|
+
inputSchema: z.ZodUnion<readonly [z.ZodRecord<z.ZodString, z.ZodUnknown>, z.ZodNull]>;
|
|
43
|
+
transitions: z.ZodArray<z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
44
|
+
kind: z.ZodLiteral<"continue">;
|
|
45
|
+
target: z.ZodString;
|
|
46
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
47
|
+
kind: z.ZodLiteral<"terminate">;
|
|
48
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
49
|
+
kind: z.ZodLiteral<"fail">;
|
|
50
|
+
}, z.core.$strip>, z.ZodObject<{
|
|
51
|
+
kind: z.ZodLiteral<"pause">;
|
|
52
|
+
signal: z.ZodString;
|
|
53
|
+
resumeStep: z.ZodString;
|
|
54
|
+
}, z.core.$strip>]>>;
|
|
55
|
+
}, z.core.$strip>>;
|
|
56
|
+
}, z.core.$strip>;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.workflowManifestSchema = exports.MANIFEST_PROTOCOL = void 0;
|
|
4
|
+
const v4_1 = require("zod/v4");
|
|
5
|
+
exports.MANIFEST_PROTOCOL = 1;
|
|
6
|
+
const manifestTransitionSchema = v4_1.z.discriminatedUnion('kind', [
|
|
7
|
+
v4_1.z.object({ kind: v4_1.z.literal('continue'), target: v4_1.z.string().min(1) }),
|
|
8
|
+
v4_1.z.object({ kind: v4_1.z.literal('terminate') }),
|
|
9
|
+
v4_1.z.object({ kind: v4_1.z.literal('fail') }),
|
|
10
|
+
v4_1.z.object({ kind: v4_1.z.literal('pause'), signal: v4_1.z.string().min(1), resumeStep: v4_1.z.string().min(1) }),
|
|
11
|
+
]);
|
|
12
|
+
const workflowStepManifestSchema = v4_1.z.object({
|
|
13
|
+
timeoutMs: v4_1.z.union([v4_1.z.number().int().positive(), v4_1.z.null()]),
|
|
14
|
+
inputSchema: v4_1.z.union([v4_1.z.record(v4_1.z.string(), v4_1.z.unknown()), v4_1.z.null()]),
|
|
15
|
+
transitions: v4_1.z.array(manifestTransitionSchema),
|
|
16
|
+
});
|
|
17
|
+
exports.workflowManifestSchema = v4_1.z.object({
|
|
18
|
+
protocol: v4_1.z.literal(exports.MANIFEST_PROTOCOL),
|
|
19
|
+
name: v4_1.z.string().min(1),
|
|
20
|
+
entry: v4_1.z.string().min(1),
|
|
21
|
+
sdkVersion: v4_1.z.string().min(1),
|
|
22
|
+
artifact: v4_1.z.object({
|
|
23
|
+
sha256: v4_1.z.string().min(1),
|
|
24
|
+
entryFile: v4_1.z.string().min(1),
|
|
25
|
+
}),
|
|
26
|
+
steps: v4_1.z.record(v4_1.z.string(), workflowStepManifestSchema),
|
|
27
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { ZodType } from 'zod/v4';
|
|
2
|
+
import type { OrchestrationExecutionContext } from './context.js';
|
|
3
|
+
import type { Fail, Goto, NextStepDirective, Pause, Retry, Terminate } from './directives.js';
|
|
4
|
+
export interface Step<TIn = unknown, TOut = unknown, TShared extends Record<string, unknown> = Record<string, unknown>> {
|
|
5
|
+
readonly name: string;
|
|
6
|
+
readonly inputSchema?: ZodType<TIn>;
|
|
7
|
+
readonly timeoutMs?: number;
|
|
8
|
+
run(input: TIn, ctx: OrchestrationExecutionContext<TShared>): Promise<StepResult<TOut>>;
|
|
9
|
+
}
|
|
10
|
+
export interface StepResult<TOut = unknown> {
|
|
11
|
+
readonly output: TOut;
|
|
12
|
+
readonly next: NextStepDirective;
|
|
13
|
+
}
|
|
14
|
+
export type Allowed<Next extends readonly string[], Term extends boolean, CanFail extends boolean, PauseTo extends string> = Goto<Next[number]> | (Term extends true ? Terminate : never) | (CanFail extends true ? Fail : never) | ([PauseTo] extends [never] ? never : Pause<PauseTo>) | Retry;
|
|
15
|
+
export interface StepDefinition<TShared extends Record<string, unknown> = Record<string, unknown>> {
|
|
16
|
+
readonly name: string;
|
|
17
|
+
readonly next: readonly string[];
|
|
18
|
+
readonly terminal: boolean;
|
|
19
|
+
readonly canFail: boolean;
|
|
20
|
+
readonly pause?: {
|
|
21
|
+
readonly signal: string;
|
|
22
|
+
readonly resumeStep: string;
|
|
23
|
+
};
|
|
24
|
+
readonly inputSchema?: ZodType<unknown>;
|
|
25
|
+
readonly timeoutMs?: number;
|
|
26
|
+
run(input: unknown, ctx: OrchestrationExecutionContext<TShared>): Promise<NextStepDirective>;
|
|
27
|
+
}
|
|
28
|
+
export declare function defineStep<TIn = unknown, TShared extends Record<string, unknown> = Record<string, unknown>, const Next extends readonly string[] = readonly [], const Term extends boolean = false, const CanFail extends boolean = false, const PauseDecl extends {
|
|
29
|
+
signal: string;
|
|
30
|
+
resumeStep: string;
|
|
31
|
+
} | undefined = undefined>(def: {
|
|
32
|
+
name: string;
|
|
33
|
+
next?: Next;
|
|
34
|
+
terminal?: Term;
|
|
35
|
+
canFail?: CanFail;
|
|
36
|
+
pause?: PauseDecl;
|
|
37
|
+
inputSchema?: ZodType<TIn>;
|
|
38
|
+
timeoutMs?: number;
|
|
39
|
+
run: (input: TIn, ctx: OrchestrationExecutionContext<TShared>) => Promise<Allowed<Next, Term, CanFail, PauseDecl extends {
|
|
40
|
+
resumeStep: infer R extends string;
|
|
41
|
+
} ? R : never>>;
|
|
42
|
+
}): StepDefinition<TShared>;
|
package/dist/cjs/step.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defineStep = defineStep;
|
|
4
|
+
function defineStep(def) {
|
|
5
|
+
return {
|
|
6
|
+
name: def.name,
|
|
7
|
+
next: def.next ?? [],
|
|
8
|
+
terminal: def.terminal ?? false,
|
|
9
|
+
canFail: def.canFail ?? false,
|
|
10
|
+
...(def.pause ? { pause: def.pause } : {}),
|
|
11
|
+
...(def.inputSchema ? { inputSchema: def.inputSchema } : {}),
|
|
12
|
+
...(def.timeoutMs !== undefined ? { timeoutMs: def.timeoutMs } : {}),
|
|
13
|
+
run: def.run,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { StepDefinition } from './step.js';
|
|
2
|
+
export declare const ORCHESTRATION_DEFINITION_BRAND: unique symbol;
|
|
3
|
+
export interface OrchestrationDefinition<TInput = unknown, TShared extends Record<string, unknown> = Record<string, unknown>> {
|
|
4
|
+
readonly name: string;
|
|
5
|
+
readonly entry: string;
|
|
6
|
+
readonly steps: Readonly<Record<string, StepDefinition<TShared>>>;
|
|
7
|
+
readonly __inputType?: TInput;
|
|
8
|
+
}
|
|
9
|
+
export declare function isOrchestrationDefinition(val: unknown): val is OrchestrationDefinition;
|
|
10
|
+
export declare function defineOrchestration<TInput = unknown, TShared extends Record<string, unknown> = Record<string, unknown>>(def: OrchestrationDefinition<TInput, TShared>): OrchestrationDefinition<TInput, TShared>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ORCHESTRATION_DEFINITION_BRAND = void 0;
|
|
4
|
+
exports.isOrchestrationDefinition = isOrchestrationDefinition;
|
|
5
|
+
exports.defineOrchestration = defineOrchestration;
|
|
6
|
+
const errors_js_1 = require("./errors.js");
|
|
7
|
+
exports.ORCHESTRATION_DEFINITION_BRAND = Symbol.for('sapiom.orchestration.definition');
|
|
8
|
+
function isOrchestrationDefinition(val) {
|
|
9
|
+
if (val === null || typeof val !== 'object')
|
|
10
|
+
return false;
|
|
11
|
+
return val[exports.ORCHESTRATION_DEFINITION_BRAND] === 1;
|
|
12
|
+
}
|
|
13
|
+
function defineOrchestration(def) {
|
|
14
|
+
if (!def.name) {
|
|
15
|
+
throw new Error('Workflow definition must have a non-empty name');
|
|
16
|
+
}
|
|
17
|
+
if (!def.entry) {
|
|
18
|
+
throw new Error(`Workflow '${def.name}' must declare an entry step`);
|
|
19
|
+
}
|
|
20
|
+
if (!def.steps[def.entry]) {
|
|
21
|
+
throw new errors_js_1.UnknownStepError(def.entry);
|
|
22
|
+
}
|
|
23
|
+
for (const [key, step] of Object.entries(def.steps)) {
|
|
24
|
+
if (!step) {
|
|
25
|
+
throw new Error(`Workflow '${def.name}' has null/undefined step at key '${key}'`);
|
|
26
|
+
}
|
|
27
|
+
if (step.name !== key) {
|
|
28
|
+
throw new Error(`Workflow '${def.name}' step name mismatch at key '${key}': step.name='${step.name}'`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
Object.defineProperty(def, exports.ORCHESTRATION_DEFINITION_BRAND, {
|
|
32
|
+
value: 1,
|
|
33
|
+
enumerable: false,
|
|
34
|
+
writable: false,
|
|
35
|
+
configurable: false,
|
|
36
|
+
});
|
|
37
|
+
return def;
|
|
38
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type WorkflowManifest } from './manifest.js';
|
|
2
|
+
import type { OrchestrationDefinition } from './workflow.js';
|
|
3
|
+
export declare function buildManifest(def: OrchestrationDefinition, opts: {
|
|
4
|
+
sdkVersion: string;
|
|
5
|
+
artifact: {
|
|
6
|
+
sha256: string;
|
|
7
|
+
entryFile: string;
|
|
8
|
+
};
|
|
9
|
+
}): WorkflowManifest;
|
|
10
|
+
export interface GraphValidation {
|
|
11
|
+
readonly errors: string[];
|
|
12
|
+
readonly warnings: string[];
|
|
13
|
+
}
|
|
14
|
+
export declare function validateGraph(manifest: WorkflowManifest): GraphValidation;
|
|
15
|
+
export declare function assertValidGraph(manifest: WorkflowManifest): string[];
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { zodToJsonSchema } from './introspection.js';
|
|
2
|
+
import { MANIFEST_PROTOCOL } from './manifest.js';
|
|
3
|
+
export function buildManifest(def, opts) {
|
|
4
|
+
const steps = {};
|
|
5
|
+
for (const [stepName, step] of Object.entries(def.steps)) {
|
|
6
|
+
let inputSchema = null;
|
|
7
|
+
if (step.inputSchema) {
|
|
8
|
+
const raw = zodToJsonSchema(step.inputSchema);
|
|
9
|
+
inputSchema = dropDefaultedFromRequired(raw);
|
|
10
|
+
}
|
|
11
|
+
steps[stepName] = {
|
|
12
|
+
timeoutMs: step.timeoutMs ?? null,
|
|
13
|
+
inputSchema,
|
|
14
|
+
transitions: transitionsFor(step),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
protocol: MANIFEST_PROTOCOL,
|
|
19
|
+
name: def.name,
|
|
20
|
+
entry: def.entry,
|
|
21
|
+
sdkVersion: opts.sdkVersion,
|
|
22
|
+
artifact: opts.artifact,
|
|
23
|
+
steps,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function transitionsFor(step) {
|
|
27
|
+
const transitions = [];
|
|
28
|
+
for (const target of step.next ?? []) {
|
|
29
|
+
transitions.push({ kind: 'continue', target });
|
|
30
|
+
}
|
|
31
|
+
if (step.pause) {
|
|
32
|
+
transitions.push({ kind: 'pause', signal: step.pause.signal, resumeStep: step.pause.resumeStep });
|
|
33
|
+
}
|
|
34
|
+
if (step.terminal)
|
|
35
|
+
transitions.push({ kind: 'terminate' });
|
|
36
|
+
if (step.canFail)
|
|
37
|
+
transitions.push({ kind: 'fail' });
|
|
38
|
+
return transitions;
|
|
39
|
+
}
|
|
40
|
+
export function validateGraph(manifest) {
|
|
41
|
+
const errors = [];
|
|
42
|
+
const names = new Set(Object.keys(manifest.steps));
|
|
43
|
+
if (!names.has(manifest.entry)) {
|
|
44
|
+
errors.push(`entry step '${manifest.entry}' is not in the steps map`);
|
|
45
|
+
}
|
|
46
|
+
const forward = buildForwardEdges(manifest, names, errors);
|
|
47
|
+
const warnings = [];
|
|
48
|
+
if (names.has(manifest.entry)) {
|
|
49
|
+
const reachable = bfs([manifest.entry], forward);
|
|
50
|
+
for (const name of names) {
|
|
51
|
+
if (!reachable.has(name))
|
|
52
|
+
warnings.push(`step '${name}' is unreachable from entry '${manifest.entry}'`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
warnings.push(...terminalReachabilityWarnings(manifest, names, forward));
|
|
56
|
+
return { errors, warnings };
|
|
57
|
+
}
|
|
58
|
+
export function assertValidGraph(manifest) {
|
|
59
|
+
const { errors, warnings } = validateGraph(manifest);
|
|
60
|
+
if (errors.length > 0) {
|
|
61
|
+
throw new Error(`Invalid workflow graph for '${manifest.name}':\n - ${errors.join('\n - ')}`);
|
|
62
|
+
}
|
|
63
|
+
return warnings;
|
|
64
|
+
}
|
|
65
|
+
function buildForwardEdges(manifest, names, errors) {
|
|
66
|
+
const forward = new Map();
|
|
67
|
+
for (const [name, step] of Object.entries(manifest.steps)) {
|
|
68
|
+
const targets = new Set();
|
|
69
|
+
for (const t of step.transitions) {
|
|
70
|
+
if (t.kind === 'continue') {
|
|
71
|
+
if (names.has(t.target))
|
|
72
|
+
targets.add(t.target);
|
|
73
|
+
else
|
|
74
|
+
errors.push(`step '${name}' has a continue target '${t.target}' that is not in the steps map`);
|
|
75
|
+
}
|
|
76
|
+
else if (t.kind === 'pause' && names.has(t.resumeStep)) {
|
|
77
|
+
targets.add(t.resumeStep);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
if (step.transitions.length === 0) {
|
|
81
|
+
errors.push(`step '${name}' is a dead-end: it declares no transitions (next/terminal/canFail/pause)`);
|
|
82
|
+
}
|
|
83
|
+
forward.set(name, targets);
|
|
84
|
+
}
|
|
85
|
+
return forward;
|
|
86
|
+
}
|
|
87
|
+
function terminalReachabilityWarnings(manifest, names, forward) {
|
|
88
|
+
const sinks = Object.entries(manifest.steps)
|
|
89
|
+
.filter(([, s]) => s.transitions.some((t) => t.kind === 'terminate' || t.kind === 'fail'))
|
|
90
|
+
.map(([name]) => name);
|
|
91
|
+
if (sinks.length === 0) {
|
|
92
|
+
return ['no step can terminate or fail — the workflow has no terminal state'];
|
|
93
|
+
}
|
|
94
|
+
const reverse = new Map();
|
|
95
|
+
for (const name of names)
|
|
96
|
+
reverse.set(name, new Set());
|
|
97
|
+
for (const [name, targets] of forward) {
|
|
98
|
+
for (const t of targets)
|
|
99
|
+
reverse.get(t)?.add(name);
|
|
100
|
+
}
|
|
101
|
+
const canReach = bfs(sinks, reverse);
|
|
102
|
+
const warnings = [];
|
|
103
|
+
for (const name of names) {
|
|
104
|
+
if (!canReach.has(name)) {
|
|
105
|
+
warnings.push(`step '${name}' cannot reach any terminate/fail (possible unbounded loop or missing terminal)`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return warnings;
|
|
109
|
+
}
|
|
110
|
+
function bfs(starts, edges) {
|
|
111
|
+
const seen = new Set();
|
|
112
|
+
const queue = [...starts];
|
|
113
|
+
while (queue.length > 0) {
|
|
114
|
+
const cur = queue.shift();
|
|
115
|
+
if (seen.has(cur))
|
|
116
|
+
continue;
|
|
117
|
+
seen.add(cur);
|
|
118
|
+
for (const next of edges.get(cur) ?? [])
|
|
119
|
+
if (!seen.has(next))
|
|
120
|
+
queue.push(next);
|
|
121
|
+
}
|
|
122
|
+
return seen;
|
|
123
|
+
}
|
|
124
|
+
function dropDefaultedFromRequired(schema) {
|
|
125
|
+
const required = schema.required;
|
|
126
|
+
const properties = schema.properties;
|
|
127
|
+
if (!Array.isArray(required) || !properties || typeof properties !== 'object') {
|
|
128
|
+
return schema;
|
|
129
|
+
}
|
|
130
|
+
const props = properties;
|
|
131
|
+
const filtered = required.filter((key) => {
|
|
132
|
+
if (typeof key !== 'string')
|
|
133
|
+
return true;
|
|
134
|
+
const prop = props[key];
|
|
135
|
+
return !(prop && typeof prop === 'object' && 'default' in prop);
|
|
136
|
+
});
|
|
137
|
+
if (filtered.length === required.length) {
|
|
138
|
+
return schema;
|
|
139
|
+
}
|
|
140
|
+
return { ...schema, required: filtered };
|
|
141
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { NextStepDirective } from './directives.js';
|
|
2
|
+
export interface StepLogger {
|
|
3
|
+
info(message: string, meta?: Record<string, unknown>): void;
|
|
4
|
+
warn(message: string, meta?: Record<string, unknown>): void;
|
|
5
|
+
error(message: string, meta?: Record<string, unknown>): void;
|
|
6
|
+
debug(message: string, meta?: Record<string, unknown>): void;
|
|
7
|
+
}
|
|
8
|
+
export type FinishedStepStatus = 'dispatched' | 'succeeded' | 'failed';
|
|
9
|
+
export interface OrchestrationExecutionContext<TShared extends Record<string, unknown> = Record<string, unknown>> {
|
|
10
|
+
readonly executionId: string;
|
|
11
|
+
readonly workflowName: string;
|
|
12
|
+
readonly organizationId: string | null;
|
|
13
|
+
readonly tenantId: string | null;
|
|
14
|
+
readonly input: unknown;
|
|
15
|
+
readonly shared: TypedContextStore<TShared>;
|
|
16
|
+
readonly history: readonly StepExecutionRecord[];
|
|
17
|
+
readonly attempts: number;
|
|
18
|
+
readonly logger: StepLogger;
|
|
19
|
+
}
|
|
20
|
+
export interface TypedContextStore<TShared extends Record<string, unknown>> {
|
|
21
|
+
get<K extends keyof TShared>(key: K): TShared[K] | undefined;
|
|
22
|
+
set<K extends keyof TShared>(key: K, value: TShared[K]): void;
|
|
23
|
+
has<K extends keyof TShared>(key: K): boolean;
|
|
24
|
+
snapshot(): Partial<TShared>;
|
|
25
|
+
}
|
|
26
|
+
export interface StepExecutionRecord {
|
|
27
|
+
readonly stepName: string;
|
|
28
|
+
readonly attempt: number;
|
|
29
|
+
readonly status: FinishedStepStatus;
|
|
30
|
+
readonly input: unknown;
|
|
31
|
+
readonly output: unknown;
|
|
32
|
+
readonly error: unknown;
|
|
33
|
+
readonly nextDirective: NextStepDirective | null;
|
|
34
|
+
readonly sharedStateAfter: Record<string, unknown> | null;
|
|
35
|
+
readonly startedAt: Date;
|
|
36
|
+
readonly finishedAt: Date;
|
|
37
|
+
}
|
|
38
|
+
export declare class InMemoryContextStore<TShared extends Record<string, unknown>> implements TypedContextStore<TShared> {
|
|
39
|
+
private state;
|
|
40
|
+
constructor(initial?: Partial<TShared>);
|
|
41
|
+
get<K extends keyof TShared>(key: K): TShared[K] | undefined;
|
|
42
|
+
set<K extends keyof TShared>(key: K, value: TShared[K]): void;
|
|
43
|
+
has<K extends keyof TShared>(key: K): boolean;
|
|
44
|
+
snapshot(): Partial<TShared>;
|
|
45
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export class InMemoryContextStore {
|
|
2
|
+
constructor(initial = {}) {
|
|
3
|
+
this.state = { ...initial };
|
|
4
|
+
}
|
|
5
|
+
get(key) {
|
|
6
|
+
return this.state[key];
|
|
7
|
+
}
|
|
8
|
+
set(key, value) {
|
|
9
|
+
this.state[key] = value;
|
|
10
|
+
}
|
|
11
|
+
has(key) {
|
|
12
|
+
return key in this.state;
|
|
13
|
+
}
|
|
14
|
+
snapshot() {
|
|
15
|
+
return { ...this.state };
|
|
16
|
+
}
|
|
17
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
export declare const DIRECTIVE_KIND: {
|
|
2
|
+
readonly CONTINUE: "continue";
|
|
3
|
+
readonly RETRY: "retry";
|
|
4
|
+
readonly PAUSE_UNTIL_SIGNAL: "pause_until_signal";
|
|
5
|
+
readonly TERMINATE: "terminate";
|
|
6
|
+
readonly FAIL: "fail";
|
|
7
|
+
};
|
|
8
|
+
export type DirectiveKind = (typeof DIRECTIVE_KIND)[keyof typeof DIRECTIVE_KIND];
|
|
9
|
+
export type NextStepDirective = ContinueDirective | RetryDirective | PauseUntilSignalDirective | TerminateDirective | FailDirective;
|
|
10
|
+
export interface ContinueDirective {
|
|
11
|
+
readonly kind: typeof DIRECTIVE_KIND.CONTINUE;
|
|
12
|
+
readonly stepName: string;
|
|
13
|
+
readonly input?: unknown;
|
|
14
|
+
}
|
|
15
|
+
export interface RetryDirective {
|
|
16
|
+
readonly kind: typeof DIRECTIVE_KIND.RETRY;
|
|
17
|
+
readonly delayMs?: number;
|
|
18
|
+
readonly reason?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface PauseUntilSignalDirective {
|
|
21
|
+
readonly kind: typeof DIRECTIVE_KIND.PAUSE_UNTIL_SIGNAL;
|
|
22
|
+
readonly signal: {
|
|
23
|
+
readonly name: string;
|
|
24
|
+
readonly correlationId?: string;
|
|
25
|
+
};
|
|
26
|
+
readonly timeoutMs?: number;
|
|
27
|
+
readonly resumeStep?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface TerminateDirective {
|
|
30
|
+
readonly kind: typeof DIRECTIVE_KIND.TERMINATE;
|
|
31
|
+
readonly reason?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface FailDirective {
|
|
34
|
+
readonly kind: typeof DIRECTIVE_KIND.FAIL;
|
|
35
|
+
readonly reason?: string;
|
|
36
|
+
}
|
|
37
|
+
export declare function isContinue(d: NextStepDirective): d is ContinueDirective;
|
|
38
|
+
export declare function isRetry(d: NextStepDirective): d is RetryDirective;
|
|
39
|
+
export declare function isPause(d: NextStepDirective): d is PauseUntilSignalDirective;
|
|
40
|
+
export declare function isTerminate(d: NextStepDirective): d is TerminateDirective;
|
|
41
|
+
export declare function isFail(d: NextStepDirective): d is FailDirective;
|
|
42
|
+
export interface Goto<Target extends string> {
|
|
43
|
+
readonly kind: typeof DIRECTIVE_KIND.CONTINUE;
|
|
44
|
+
readonly stepName: Target;
|
|
45
|
+
readonly input?: unknown;
|
|
46
|
+
}
|
|
47
|
+
export interface Terminate {
|
|
48
|
+
readonly kind: typeof DIRECTIVE_KIND.TERMINATE;
|
|
49
|
+
readonly output?: unknown;
|
|
50
|
+
readonly reason?: string;
|
|
51
|
+
}
|
|
52
|
+
export interface Fail {
|
|
53
|
+
readonly kind: typeof DIRECTIVE_KIND.FAIL;
|
|
54
|
+
readonly reason?: string;
|
|
55
|
+
readonly output?: unknown;
|
|
56
|
+
}
|
|
57
|
+
export interface Pause<Resume extends string> {
|
|
58
|
+
readonly kind: typeof DIRECTIVE_KIND.PAUSE_UNTIL_SIGNAL;
|
|
59
|
+
readonly signal: {
|
|
60
|
+
readonly name: string;
|
|
61
|
+
readonly correlationId?: string;
|
|
62
|
+
};
|
|
63
|
+
readonly resumeStep?: Resume;
|
|
64
|
+
readonly timeoutMs?: number;
|
|
65
|
+
readonly output?: unknown;
|
|
66
|
+
}
|
|
67
|
+
export interface Retry {
|
|
68
|
+
readonly kind: typeof DIRECTIVE_KIND.RETRY;
|
|
69
|
+
readonly delayMs?: number;
|
|
70
|
+
readonly reason?: string;
|
|
71
|
+
}
|
|
72
|
+
export declare function goto<const Target extends string>(target: Target, output?: unknown): Goto<Target>;
|
|
73
|
+
export declare function terminate(output?: unknown, opts?: {
|
|
74
|
+
reason?: string;
|
|
75
|
+
}): Terminate;
|
|
76
|
+
export declare function fail(reason?: string, opts?: {
|
|
77
|
+
output?: unknown;
|
|
78
|
+
}): Fail;
|
|
79
|
+
export declare function pauseUntilSignal<const Resume extends string>(args: {
|
|
80
|
+
signal: string;
|
|
81
|
+
resumeStep?: Resume;
|
|
82
|
+
correlationId?: string;
|
|
83
|
+
timeoutMs?: number;
|
|
84
|
+
output?: unknown;
|
|
85
|
+
}): Pause<Resume>;
|
|
86
|
+
export declare function retry(opts?: {
|
|
87
|
+
delayMs?: number;
|
|
88
|
+
reason?: string;
|
|
89
|
+
}): Retry;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export const DIRECTIVE_KIND = {
|
|
2
|
+
CONTINUE: 'continue',
|
|
3
|
+
RETRY: 'retry',
|
|
4
|
+
PAUSE_UNTIL_SIGNAL: 'pause_until_signal',
|
|
5
|
+
TERMINATE: 'terminate',
|
|
6
|
+
FAIL: 'fail',
|
|
7
|
+
};
|
|
8
|
+
export function isContinue(d) {
|
|
9
|
+
return d.kind === DIRECTIVE_KIND.CONTINUE;
|
|
10
|
+
}
|
|
11
|
+
export function isRetry(d) {
|
|
12
|
+
return d.kind === DIRECTIVE_KIND.RETRY;
|
|
13
|
+
}
|
|
14
|
+
export function isPause(d) {
|
|
15
|
+
return d.kind === DIRECTIVE_KIND.PAUSE_UNTIL_SIGNAL;
|
|
16
|
+
}
|
|
17
|
+
export function isTerminate(d) {
|
|
18
|
+
return d.kind === DIRECTIVE_KIND.TERMINATE;
|
|
19
|
+
}
|
|
20
|
+
export function isFail(d) {
|
|
21
|
+
return d.kind === DIRECTIVE_KIND.FAIL;
|
|
22
|
+
}
|
|
23
|
+
export function goto(target, output) {
|
|
24
|
+
return { kind: DIRECTIVE_KIND.CONTINUE, stepName: target, input: output };
|
|
25
|
+
}
|
|
26
|
+
export function terminate(output, opts) {
|
|
27
|
+
return { kind: DIRECTIVE_KIND.TERMINATE, output, reason: opts?.reason };
|
|
28
|
+
}
|
|
29
|
+
export function fail(reason, opts) {
|
|
30
|
+
return { kind: DIRECTIVE_KIND.FAIL, reason, output: opts?.output };
|
|
31
|
+
}
|
|
32
|
+
export function pauseUntilSignal(args) {
|
|
33
|
+
return {
|
|
34
|
+
kind: DIRECTIVE_KIND.PAUSE_UNTIL_SIGNAL,
|
|
35
|
+
signal: { name: args.signal, correlationId: args.correlationId },
|
|
36
|
+
resumeStep: args.resumeStep,
|
|
37
|
+
timeoutMs: args.timeoutMs,
|
|
38
|
+
output: args.output,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export function retry(opts) {
|
|
42
|
+
return { kind: DIRECTIVE_KIND.RETRY, delayMs: opts?.delayMs, reason: opts?.reason };
|
|
43
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { $ZodIssue } from 'zod/v4/core';
|
|
2
|
+
export declare class WorkflowError extends Error {
|
|
3
|
+
constructor(message: string);
|
|
4
|
+
}
|
|
5
|
+
export declare class StepInputValidationError extends WorkflowError {
|
|
6
|
+
readonly stepName: string;
|
|
7
|
+
readonly issues: readonly $ZodIssue[];
|
|
8
|
+
constructor(stepName: string, issues: readonly $ZodIssue[]);
|
|
9
|
+
}
|
|
10
|
+
export declare class UnknownStepError extends WorkflowError {
|
|
11
|
+
readonly stepName: string;
|
|
12
|
+
constructor(stepName: string);
|
|
13
|
+
}
|
|
14
|
+
export declare class DisallowedTransitionError extends WorkflowError {
|
|
15
|
+
readonly stepName: string;
|
|
16
|
+
readonly directiveKind: string;
|
|
17
|
+
readonly target?: string;
|
|
18
|
+
constructor(stepName: string, directiveKind: string, target?: string);
|
|
19
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export class WorkflowError extends Error {
|
|
2
|
+
constructor(message) {
|
|
3
|
+
super(message);
|
|
4
|
+
this.name = 'WorkflowError';
|
|
5
|
+
}
|
|
6
|
+
}
|
|
7
|
+
export class StepInputValidationError extends WorkflowError {
|
|
8
|
+
constructor(stepName, issues) {
|
|
9
|
+
super(`Input for step '${stepName}' failed validation: ${formatIssues(issues)}`);
|
|
10
|
+
this.name = 'StepInputValidationError';
|
|
11
|
+
this.stepName = stepName;
|
|
12
|
+
this.issues = issues;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function formatIssues(issues) {
|
|
16
|
+
if (issues.length === 0)
|
|
17
|
+
return 'unknown validation error';
|
|
18
|
+
return issues
|
|
19
|
+
.map((issue) => {
|
|
20
|
+
const path = issue.path.length > 0 ? issue.path.join('.') : '(root)';
|
|
21
|
+
return `${path}: ${issue.message}`;
|
|
22
|
+
})
|
|
23
|
+
.join('; ');
|
|
24
|
+
}
|
|
25
|
+
export class UnknownStepError extends WorkflowError {
|
|
26
|
+
constructor(stepName) {
|
|
27
|
+
super(`Unknown step: ${stepName}`);
|
|
28
|
+
this.name = 'UnknownStepError';
|
|
29
|
+
this.stepName = stepName;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export class DisallowedTransitionError extends WorkflowError {
|
|
33
|
+
constructor(stepName, directiveKind, target) {
|
|
34
|
+
super(`Step '${stepName}' returned a '${directiveKind}' directive` +
|
|
35
|
+
(target ? ` to '${target}'` : '') +
|
|
36
|
+
` that is not in its declared transitions`);
|
|
37
|
+
this.name = 'DisallowedTransitionError';
|
|
38
|
+
this.stepName = stepName;
|
|
39
|
+
this.directiveKind = directiveKind;
|
|
40
|
+
this.target = target;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export { DIRECTIVE_KIND, isContinue, isRetry, isPause, isTerminate, isFail } from './directives.js';
|
|
2
|
+
export type { DirectiveKind, NextStepDirective, ContinueDirective, RetryDirective, PauseUntilSignalDirective, TerminateDirective, FailDirective, } from './directives.js';
|
|
3
|
+
export { goto, terminate, fail, pauseUntilSignal, retry } from './directives.js';
|
|
4
|
+
export type { Goto, Terminate, Fail, Pause, Retry } from './directives.js';
|
|
5
|
+
export { defineStep } from './step.js';
|
|
6
|
+
export type { Step, StepResult, StepDefinition, Allowed } from './step.js';
|
|
7
|
+
export type { OrchestrationExecutionContext, TypedContextStore, StepExecutionRecord, StepLogger, FinishedStepStatus, } from './context.js';
|
|
8
|
+
export { InMemoryContextStore } from './context.js';
|
|
9
|
+
export type { OrchestrationDefinition } from './workflow.js';
|
|
10
|
+
export { defineOrchestration, isOrchestrationDefinition, ORCHESTRATION_DEFINITION_BRAND } from './workflow.js';
|
|
11
|
+
export { WorkflowError, UnknownStepError, StepInputValidationError, DisallowedTransitionError } from './errors.js';
|
|
12
|
+
export { zodToJsonSchema, exampleFromJsonSchema, stepInputContract, workflowInputContract } from './introspection.js';
|
|
13
|
+
export type { StepInputContract, WorkflowInputContract } from './introspection.js';
|
|
14
|
+
export { MANIFEST_PROTOCOL, workflowManifestSchema } from './manifest.js';
|
|
15
|
+
export type { WorkflowManifest, WorkflowStepManifest, ManifestTransition } from './manifest.js';
|
|
16
|
+
export { buildManifest, validateGraph, assertValidGraph } from './build-manifest.js';
|
|
17
|
+
export type { GraphValidation } from './build-manifest.js';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { DIRECTIVE_KIND, isContinue, isRetry, isPause, isTerminate, isFail } from './directives.js';
|
|
2
|
+
export { goto, terminate, fail, pauseUntilSignal, retry } from './directives.js';
|
|
3
|
+
export { defineStep } from './step.js';
|
|
4
|
+
export { InMemoryContextStore } from './context.js';
|
|
5
|
+
export { defineOrchestration, isOrchestrationDefinition, ORCHESTRATION_DEFINITION_BRAND } from './workflow.js';
|
|
6
|
+
export { WorkflowError, UnknownStepError, StepInputValidationError, DisallowedTransitionError } from './errors.js';
|
|
7
|
+
export { zodToJsonSchema, exampleFromJsonSchema, stepInputContract, workflowInputContract } from './introspection.js';
|
|
8
|
+
export { MANIFEST_PROTOCOL, workflowManifestSchema } from './manifest.js';
|
|
9
|
+
export { buildManifest, validateGraph, assertValidGraph } from './build-manifest.js';
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { z } from 'zod/v4';
|
|
2
|
+
import type { OrchestrationDefinition } from './workflow.js';
|
|
3
|
+
export interface StepInputContract {
|
|
4
|
+
readonly jsonSchema: Record<string, unknown>;
|
|
5
|
+
readonly example: unknown;
|
|
6
|
+
}
|
|
7
|
+
export type WorkflowInputContract = StepInputContract;
|
|
8
|
+
export declare function zodToJsonSchema(schema: z.ZodType): Record<string, unknown>;
|
|
9
|
+
export declare function stepInputContract(def: OrchestrationDefinition<unknown, Record<string, unknown>>, stepName: string): StepInputContract | null;
|
|
10
|
+
export declare function workflowInputContract(def: OrchestrationDefinition<unknown, Record<string, unknown>>): StepInputContract | null;
|
|
11
|
+
export declare function exampleFromJsonSchema(jsonSchema: Record<string, unknown>): unknown;
|