@gobing-ai/ts-dual-workflow-engine 0.2.8 → 0.2.9
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 +8 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +165 -16
- package/dist/schema.d.ts +28 -12
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +57 -16
- package/dist/state-machine.d.ts.map +1 -1
- package/dist/state-machine.js +17 -4
- package/dist/transition-flow.d.ts.map +1 -1
- package/dist/transition-flow.js +17 -3
- package/dist/types.d.ts +16 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -4
- package/schemas/state-machine-workflow.schema.json +19 -11
- package/schemas/transition-flow-workflow.schema.json +19 -11
- package/src/config.ts +183 -18
- package/src/schema.ts +94 -49
- package/src/state-machine.ts +23 -1
- package/src/transition-flow.ts +23 -3
- package/src/types.ts +16 -0
package/dist/types.d.ts
CHANGED
|
@@ -19,6 +19,8 @@ export interface GuardDef {
|
|
|
19
19
|
/** One state in a state-machine workflow. */
|
|
20
20
|
export interface StateDef {
|
|
21
21
|
readonly id: string;
|
|
22
|
+
/** Optional human description of what this state does. */
|
|
23
|
+
readonly description?: string;
|
|
22
24
|
readonly onEnter?: readonly ActionDef[];
|
|
23
25
|
readonly onExit?: readonly ActionDef[];
|
|
24
26
|
}
|
|
@@ -26,6 +28,8 @@ export interface StateDef {
|
|
|
26
28
|
export interface TransitionDef {
|
|
27
29
|
readonly from: string;
|
|
28
30
|
readonly to: string;
|
|
31
|
+
/** Optional human description of when/why this transition is taken. */
|
|
32
|
+
readonly description?: string;
|
|
29
33
|
readonly trigger?: string;
|
|
30
34
|
readonly guard?: GuardDef;
|
|
31
35
|
}
|
|
@@ -33,6 +37,10 @@ export interface TransitionDef {
|
|
|
33
37
|
export interface StateMachineWorkflowDef {
|
|
34
38
|
readonly kind?: 'state-machine';
|
|
35
39
|
readonly name: string;
|
|
40
|
+
/** Optional behavior-free document version tag. */
|
|
41
|
+
readonly version?: string;
|
|
42
|
+
/** Optional human description of the workflow's purpose. */
|
|
43
|
+
readonly description?: string;
|
|
36
44
|
readonly initialState: string;
|
|
37
45
|
readonly terminalStates?: readonly string[];
|
|
38
46
|
readonly iterationBound?: number;
|
|
@@ -44,6 +52,8 @@ export interface StateMachineWorkflowDef {
|
|
|
44
52
|
/** Transition-flow node definition. */
|
|
45
53
|
export interface FlowNodeDef {
|
|
46
54
|
readonly id: string;
|
|
55
|
+
/** Optional human description of what this node does. */
|
|
56
|
+
readonly description?: string;
|
|
47
57
|
readonly type?: 'action' | 'gate' | 'parallel' | 'decision';
|
|
48
58
|
readonly action?: ActionDef;
|
|
49
59
|
}
|
|
@@ -51,12 +61,18 @@ export interface FlowNodeDef {
|
|
|
51
61
|
export interface FlowEdgeDef {
|
|
52
62
|
readonly from: string;
|
|
53
63
|
readonly to: string;
|
|
64
|
+
/** Optional human description of when/why this edge is taken. */
|
|
65
|
+
readonly description?: string;
|
|
54
66
|
readonly condition?: GuardDef;
|
|
55
67
|
}
|
|
56
68
|
/** Transition-flow workflow definition. */
|
|
57
69
|
export interface TransitionFlowWorkflowDef {
|
|
58
70
|
readonly kind: 'transition-flow';
|
|
59
71
|
readonly name: string;
|
|
72
|
+
/** Optional behavior-free document version tag. */
|
|
73
|
+
readonly version?: string;
|
|
74
|
+
/** Optional human description of the workflow's purpose. */
|
|
75
|
+
readonly description?: string;
|
|
60
76
|
readonly initialNode: string;
|
|
61
77
|
readonly terminalNodes?: readonly string[];
|
|
62
78
|
readonly iterationBound?: number;
|
package/dist/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gobing-ai/ts-dual-workflow-engine",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"description": "@gobing-ai/ts-dual-workflow-engine — State-machine and transition-flow workflow runtime.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"typescript",
|
|
@@ -48,9 +48,9 @@
|
|
|
48
48
|
"release": "echo 'Manual publish is disabled. Releases go through GitHub Actions via Trusted Publishing — push a tag: git tag @gobing-ai/ts-dual-workflow-engine-v<version> && git push --tags' && exit 1"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@gobing-ai/ts-db": "^0.2.
|
|
52
|
-
"@gobing-ai/ts-runtime": "^0.2.
|
|
53
|
-
"
|
|
51
|
+
"@gobing-ai/ts-db": "^0.2.9",
|
|
52
|
+
"@gobing-ai/ts-runtime": "^0.2.9",
|
|
53
|
+
"@gobing-ai/ts-utils": "^0.2.9",
|
|
54
54
|
"zod": "^4.1.0"
|
|
55
55
|
},
|
|
56
56
|
"devDependencies": {
|
|
@@ -8,16 +8,22 @@
|
|
|
8
8
|
"properties": {
|
|
9
9
|
"$schema": { "type": "string" },
|
|
10
10
|
"kind": { "const": "state-machine" },
|
|
11
|
-
"name": { "type": "string" },
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
11
|
+
"name": { "type": "string", "minLength": 1 },
|
|
12
|
+
"version": { "type": "string" },
|
|
13
|
+
"description": { "type": "string" },
|
|
14
|
+
"initialState": { "type": "string", "minLength": 1 },
|
|
15
|
+
"terminalStates": { "type": "array", "items": { "type": "string", "minLength": 1 } },
|
|
16
|
+
"iterationBound": { "type": "integer", "exclusiveMinimum": 0 },
|
|
17
|
+
"vars": {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"propertyNames": { "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" },
|
|
20
|
+
"additionalProperties": { "type": "string" }
|
|
21
|
+
},
|
|
16
22
|
"env": {
|
|
17
23
|
"type": "object",
|
|
18
24
|
"additionalProperties": false,
|
|
19
25
|
"properties": {
|
|
20
|
-
"allow": { "type": "array", "items": { "type": "string" } }
|
|
26
|
+
"allow": { "type": "array", "items": { "type": "string", "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" } }
|
|
21
27
|
}
|
|
22
28
|
},
|
|
23
29
|
"states": {
|
|
@@ -27,7 +33,8 @@
|
|
|
27
33
|
"additionalProperties": false,
|
|
28
34
|
"required": ["id"],
|
|
29
35
|
"properties": {
|
|
30
|
-
"id": { "type": "string" },
|
|
36
|
+
"id": { "type": "string", "minLength": 1 },
|
|
37
|
+
"description": { "type": "string" },
|
|
31
38
|
"onEnter": { "type": "array", "items": { "$ref": "#/$defs/action" } },
|
|
32
39
|
"onExit": { "type": "array", "items": { "$ref": "#/$defs/action" } }
|
|
33
40
|
}
|
|
@@ -40,8 +47,9 @@
|
|
|
40
47
|
"additionalProperties": false,
|
|
41
48
|
"required": ["from", "to"],
|
|
42
49
|
"properties": {
|
|
43
|
-
"from": { "type": "string" },
|
|
44
|
-
"to": { "type": "string" },
|
|
50
|
+
"from": { "type": "string", "minLength": 1 },
|
|
51
|
+
"to": { "type": "string", "minLength": 1 },
|
|
52
|
+
"description": { "type": "string" },
|
|
45
53
|
"trigger": { "type": "string" },
|
|
46
54
|
"guard": { "$ref": "#/$defs/guard" }
|
|
47
55
|
}
|
|
@@ -54,7 +62,7 @@
|
|
|
54
62
|
"additionalProperties": false,
|
|
55
63
|
"required": ["kind"],
|
|
56
64
|
"properties": {
|
|
57
|
-
"kind": { "type": "string" },
|
|
65
|
+
"kind": { "type": "string", "minLength": 1 },
|
|
58
66
|
"options": { "type": "object", "additionalProperties": true }
|
|
59
67
|
}
|
|
60
68
|
},
|
|
@@ -63,7 +71,7 @@
|
|
|
63
71
|
"additionalProperties": false,
|
|
64
72
|
"required": ["kind"],
|
|
65
73
|
"properties": {
|
|
66
|
-
"kind": { "type": "string" },
|
|
74
|
+
"kind": { "type": "string", "minLength": 1 },
|
|
67
75
|
"options": { "type": "object", "additionalProperties": true }
|
|
68
76
|
}
|
|
69
77
|
}
|
|
@@ -8,16 +8,22 @@
|
|
|
8
8
|
"properties": {
|
|
9
9
|
"$schema": { "type": "string" },
|
|
10
10
|
"kind": { "const": "transition-flow" },
|
|
11
|
-
"name": { "type": "string" },
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
15
|
-
"
|
|
11
|
+
"name": { "type": "string", "minLength": 1 },
|
|
12
|
+
"version": { "type": "string" },
|
|
13
|
+
"description": { "type": "string" },
|
|
14
|
+
"initialNode": { "type": "string", "minLength": 1 },
|
|
15
|
+
"terminalNodes": { "type": "array", "items": { "type": "string", "minLength": 1 } },
|
|
16
|
+
"iterationBound": { "type": "integer", "exclusiveMinimum": 0 },
|
|
17
|
+
"vars": {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"propertyNames": { "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" },
|
|
20
|
+
"additionalProperties": { "type": "string" }
|
|
21
|
+
},
|
|
16
22
|
"env": {
|
|
17
23
|
"type": "object",
|
|
18
24
|
"additionalProperties": false,
|
|
19
25
|
"properties": {
|
|
20
|
-
"allow": { "type": "array", "items": { "type": "string" } }
|
|
26
|
+
"allow": { "type": "array", "items": { "type": "string", "pattern": "^[A-Za-z_][A-Za-z0-9_]*$" } }
|
|
21
27
|
}
|
|
22
28
|
},
|
|
23
29
|
"nodes": {
|
|
@@ -27,7 +33,8 @@
|
|
|
27
33
|
"additionalProperties": false,
|
|
28
34
|
"required": ["id"],
|
|
29
35
|
"properties": {
|
|
30
|
-
"id": { "type": "string" },
|
|
36
|
+
"id": { "type": "string", "minLength": 1 },
|
|
37
|
+
"description": { "type": "string" },
|
|
31
38
|
"type": { "enum": ["action", "gate", "parallel", "decision"] },
|
|
32
39
|
"action": { "$ref": "#/$defs/action" }
|
|
33
40
|
}
|
|
@@ -40,8 +47,9 @@
|
|
|
40
47
|
"additionalProperties": false,
|
|
41
48
|
"required": ["from", "to"],
|
|
42
49
|
"properties": {
|
|
43
|
-
"from": { "type": "string" },
|
|
44
|
-
"to": { "type": "string" },
|
|
50
|
+
"from": { "type": "string", "minLength": 1 },
|
|
51
|
+
"to": { "type": "string", "minLength": 1 },
|
|
52
|
+
"description": { "type": "string" },
|
|
45
53
|
"condition": { "$ref": "#/$defs/guard" }
|
|
46
54
|
}
|
|
47
55
|
}
|
|
@@ -53,7 +61,7 @@
|
|
|
53
61
|
"additionalProperties": false,
|
|
54
62
|
"required": ["kind"],
|
|
55
63
|
"properties": {
|
|
56
|
-
"kind": { "type": "string" },
|
|
64
|
+
"kind": { "type": "string", "minLength": 1 },
|
|
57
65
|
"options": { "type": "object", "additionalProperties": true }
|
|
58
66
|
}
|
|
59
67
|
},
|
|
@@ -62,7 +70,7 @@
|
|
|
62
70
|
"additionalProperties": false,
|
|
63
71
|
"required": ["kind"],
|
|
64
72
|
"properties": {
|
|
65
|
-
"kind": { "type": "string" },
|
|
73
|
+
"kind": { "type": "string", "minLength": 1 },
|
|
66
74
|
"options": { "type": "object", "additionalProperties": true }
|
|
67
75
|
}
|
|
68
76
|
}
|
package/src/config.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { loadStructuredConfig } from '@gobing-ai/ts-runtime';
|
|
2
|
-
import { parse } from 'yaml';
|
|
1
|
+
import { loadStructuredConfig, parseYamlObject } from '@gobing-ai/ts-runtime';
|
|
3
2
|
import { WorkflowValidationError } from './errors';
|
|
4
|
-
import {
|
|
3
|
+
import { StateMachineWorkflowDefSchema, TransitionFlowWorkflowDefSchema } from './schema';
|
|
5
4
|
import type { WorkflowDef } from './types';
|
|
6
5
|
|
|
7
6
|
export interface WorkflowLoadOptions {
|
|
@@ -13,7 +12,7 @@ export interface WorkflowLoadOptions {
|
|
|
13
12
|
|
|
14
13
|
/** Load a workflow definition from YAML or JSON text. */
|
|
15
14
|
export function loadWorkflowDefFromText(text: string, source = '<inline>'): WorkflowDef {
|
|
16
|
-
const parsed = source.endsWith('.json') ? JSON.parse(text) :
|
|
15
|
+
const parsed = source.endsWith('.json') ? JSON.parse(text) : parseYamlObject(text);
|
|
17
16
|
return parseWorkflowDef(parsed, source);
|
|
18
17
|
}
|
|
19
18
|
|
|
@@ -32,23 +31,71 @@ export function validateWorkflowDef(workflow: WorkflowDef): void {
|
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
function validateStateMachine(workflow: Extract<WorkflowDef, { kind?: 'state-machine' }>): void {
|
|
35
|
-
const
|
|
34
|
+
const errors: string[] = [];
|
|
35
|
+
const ids = workflow.states.map((state) => state.id);
|
|
36
|
+
const states = new Set(ids);
|
|
37
|
+
const terminals = new Set(workflow.terminalStates ?? []);
|
|
38
|
+
|
|
39
|
+
// Duplicate state ids.
|
|
40
|
+
for (const id of duplicates(ids)) errors.push(`State "${id}" is declared more than once`);
|
|
41
|
+
|
|
42
|
+
// Initial state must be declared and must not be terminal.
|
|
36
43
|
if (!states.has(workflow.initialState)) {
|
|
37
|
-
|
|
44
|
+
errors.push(`Initial state "${workflow.initialState}" is not declared`);
|
|
45
|
+
} else if (terminals.has(workflow.initialState)) {
|
|
46
|
+
errors.push(`Initial state "${workflow.initialState}" must not be a terminal state`);
|
|
38
47
|
}
|
|
48
|
+
|
|
49
|
+
// Terminal states must be declared.
|
|
50
|
+
for (const terminal of terminals) {
|
|
51
|
+
if (!states.has(terminal)) errors.push(`Terminal state "${terminal}" is not declared`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Transition endpoints must be declared.
|
|
55
|
+
type Transition = (typeof workflow.transitions)[number];
|
|
56
|
+
const outboundByState = new Map<string, Transition[]>();
|
|
39
57
|
for (const transition of workflow.transitions) {
|
|
40
|
-
if (!states.has(transition.from))
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
58
|
+
if (!states.has(transition.from)) errors.push(`Transition source "${transition.from}" is not declared`);
|
|
59
|
+
if (!states.has(transition.to)) errors.push(`Transition target "${transition.to}" is not declared`);
|
|
60
|
+
const list = outboundByState.get(transition.from) ?? [];
|
|
61
|
+
list.push(transition);
|
|
62
|
+
outboundByState.set(transition.from, list);
|
|
44
63
|
}
|
|
64
|
+
|
|
65
|
+
// Explicit terminal states must not declare outgoing transitions. Note: a state
|
|
66
|
+
// with NO transitions is treated as an implicit terminal by the driver (it stops
|
|
67
|
+
// there), so a missing transition is NOT an error; only an *explicit* terminal
|
|
68
|
+
// that also declares transitions is contradictory.
|
|
69
|
+
for (const state of workflow.states) {
|
|
70
|
+
const outbound = outboundByState.get(state.id) ?? [];
|
|
71
|
+
if (terminals.has(state.id)) {
|
|
72
|
+
if (outbound.length > 0) errors.push(`Terminal state "${state.id}" must not declare transitions`);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
// An unguarded transition is unconditional, so anything after it is dead —
|
|
76
|
+
// it must be declared last among a state's outgoing transitions.
|
|
77
|
+
const ungatedIndex = outbound.findIndex((transition) => transition.guard === undefined);
|
|
78
|
+
if (ungatedIndex !== -1 && ungatedIndex < outbound.length - 1) {
|
|
79
|
+
errors.push(
|
|
80
|
+
`State "${state.id}" has an unguarded transition that is not last; later transitions are unreachable`,
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Template variable references must resolve against declared vars / env allowlist.
|
|
86
|
+
errors.push(...checkVariableReferences(collectActionOptions(workflow), workflow.vars, workflow.env));
|
|
87
|
+
|
|
88
|
+
throwIfErrors(workflow, errors);
|
|
45
89
|
}
|
|
46
90
|
|
|
47
91
|
function parseWorkflowDef(parsed: unknown, source: string): WorkflowDef {
|
|
48
|
-
|
|
92
|
+
// Parse against the specific dialect schema (not the union) so errors carry the
|
|
93
|
+
// exact failing field path instead of the union's generic "Invalid input".
|
|
94
|
+
const schema = selectWorkflowSchema(parsed);
|
|
95
|
+
const result = schema.safeParse(parsed);
|
|
49
96
|
if (!result.success) {
|
|
50
97
|
throw new WorkflowValidationError(
|
|
51
|
-
`Workflow definition failed schema validation for ${source}: ${result.error.issues
|
|
98
|
+
`Workflow definition failed schema validation for ${source}: ${formatWorkflowIssues(result.error.issues)}`,
|
|
52
99
|
result.error.issues,
|
|
53
100
|
);
|
|
54
101
|
}
|
|
@@ -56,13 +103,131 @@ function parseWorkflowDef(parsed: unknown, source: string): WorkflowDef {
|
|
|
56
103
|
return result.data as WorkflowDef;
|
|
57
104
|
}
|
|
58
105
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
106
|
+
/** Pick the dialect schema by the shape of the input so diagnostics are field-precise. */
|
|
107
|
+
function selectWorkflowSchema(
|
|
108
|
+
parsed: unknown,
|
|
109
|
+
): typeof StateMachineWorkflowDefSchema | typeof TransitionFlowWorkflowDefSchema {
|
|
110
|
+
if (parsed !== null && typeof parsed === 'object') {
|
|
111
|
+
const value = parsed as Record<string, unknown>;
|
|
112
|
+
if (value.kind === 'transition-flow' || 'nodes' in value || 'edges' in value || 'initialNode' in value) {
|
|
113
|
+
return TransitionFlowWorkflowDefSchema;
|
|
114
|
+
}
|
|
63
115
|
}
|
|
116
|
+
return StateMachineWorkflowDefSchema;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Render Zod issues as `path: message` fragments so the offending field is obvious. */
|
|
120
|
+
function formatWorkflowIssues(issues: readonly { path: PropertyKey[]; message: string }[]): string {
|
|
121
|
+
return issues
|
|
122
|
+
.map((issue) => {
|
|
123
|
+
const path = issue.path.map(String).join('.');
|
|
124
|
+
return path.length > 0 ? `${path}: ${issue.message}` : issue.message;
|
|
125
|
+
})
|
|
126
|
+
.join('; ');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function validateTransitionFlow(workflow: Extract<WorkflowDef, { kind: 'transition-flow' }>): void {
|
|
130
|
+
const errors: string[] = [];
|
|
131
|
+
const ids = workflow.nodes.map((node) => node.id);
|
|
132
|
+
const nodes = new Set(ids);
|
|
133
|
+
|
|
134
|
+
// Duplicate node ids.
|
|
135
|
+
for (const id of duplicates(ids)) errors.push(`Node "${id}" is declared more than once`);
|
|
136
|
+
|
|
137
|
+
// Initial node must be declared.
|
|
138
|
+
if (!nodes.has(workflow.initialNode)) errors.push(`Initial node "${workflow.initialNode}" is not declared`);
|
|
139
|
+
|
|
140
|
+
// Edge endpoints must be declared; duplicate from→to edges are ambiguous.
|
|
141
|
+
const seenEdges = new Set<string>();
|
|
64
142
|
for (const edge of workflow.edges) {
|
|
65
|
-
if (!nodes.has(edge.from))
|
|
66
|
-
if (!nodes.has(edge.to))
|
|
143
|
+
if (!nodes.has(edge.from)) errors.push(`Edge source "${edge.from}" is not declared`);
|
|
144
|
+
if (!nodes.has(edge.to)) errors.push(`Edge target "${edge.to}" is not declared`);
|
|
145
|
+
const key = `${edge.from}→${edge.to}`;
|
|
146
|
+
if (seenEdges.has(key)) errors.push(`Duplicate edge "${key}"`);
|
|
147
|
+
seenEdges.add(key);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Template variable references must resolve.
|
|
151
|
+
errors.push(...checkVariableReferences(collectActionOptions(workflow), workflow.vars, workflow.env));
|
|
152
|
+
|
|
153
|
+
throwIfErrors(workflow, errors);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Return the ids that appear more than once, in first-seen order. */
|
|
157
|
+
function duplicates(ids: readonly string[]): string[] {
|
|
158
|
+
const seen = new Set<string>();
|
|
159
|
+
const dupes = new Set<string>();
|
|
160
|
+
for (const id of ids) {
|
|
161
|
+
if (seen.has(id)) dupes.add(id);
|
|
162
|
+
seen.add(id);
|
|
163
|
+
}
|
|
164
|
+
return [...dupes];
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/** Gather every action `options` object across a workflow's states/nodes for template scanning. */
|
|
168
|
+
function collectActionOptions(workflow: WorkflowDef): Record<string, unknown>[] {
|
|
169
|
+
const optionSets: Record<string, unknown>[] = [];
|
|
170
|
+
const actions =
|
|
171
|
+
workflow.kind === 'transition-flow'
|
|
172
|
+
? workflow.nodes.flatMap((node) => (node.action ? [node.action] : []))
|
|
173
|
+
: workflow.states.flatMap((state) => [...(state.onEnter ?? []), ...(state.onExit ?? [])]);
|
|
174
|
+
for (const action of actions) {
|
|
175
|
+
if (action.options) optionSets.push(action.options);
|
|
176
|
+
}
|
|
177
|
+
return optionSets;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** Reserved template namespaces always available at runtime (not user-declared vars). */
|
|
181
|
+
const RUNTIME_TEMPLATE_NAMESPACES = new Set([
|
|
182
|
+
'workflow',
|
|
183
|
+
'runId',
|
|
184
|
+
'task',
|
|
185
|
+
'state',
|
|
186
|
+
'node',
|
|
187
|
+
'iteration',
|
|
188
|
+
'run',
|
|
189
|
+
'runtime',
|
|
190
|
+
]);
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Check `${...}` template references inside action options resolve to something:
|
|
194
|
+
* a declared `vars.X`, an env-allowlisted `env.X`, or a reserved runtime namespace.
|
|
195
|
+
*/
|
|
196
|
+
function checkVariableReferences(
|
|
197
|
+
optionSets: readonly Record<string, unknown>[],
|
|
198
|
+
vars: Record<string, string> | undefined,
|
|
199
|
+
env: { allow?: readonly string[] } | undefined,
|
|
200
|
+
): string[] {
|
|
201
|
+
const declaredVars = new Set(Object.keys(vars ?? {}));
|
|
202
|
+
const allowedEnv = new Set(env?.allow ?? []);
|
|
203
|
+
const errors: string[] = [];
|
|
204
|
+
const reference = /\$\{([^}]+)\}/g;
|
|
205
|
+
for (const options of optionSets) {
|
|
206
|
+
for (const value of Object.values(options)) {
|
|
207
|
+
if (typeof value !== 'string') continue;
|
|
208
|
+
for (const match of value.matchAll(reference)) {
|
|
209
|
+
const expr = (match[1] ?? '').trim();
|
|
210
|
+
const [namespace, name] = expr.includes('.') ? expr.split('.', 2) : [expr, undefined];
|
|
211
|
+
if (namespace === 'vars') {
|
|
212
|
+
if (name !== undefined && !declaredVars.has(name)) {
|
|
213
|
+
errors.push(`Unknown variable reference "\${${expr}}" (no var "${name}" declared)`);
|
|
214
|
+
}
|
|
215
|
+
} else if (namespace === 'env') {
|
|
216
|
+
if (name !== undefined && !allowedEnv.has(name)) {
|
|
217
|
+
errors.push(`Env reference "\${${expr}}" is not in env.allow`);
|
|
218
|
+
}
|
|
219
|
+
} else if (namespace !== undefined && !RUNTIME_TEMPLATE_NAMESPACES.has(namespace)) {
|
|
220
|
+
errors.push(`Unknown template reference "\${${expr}}"`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
return errors;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/** Throw a single aggregated validation error when any invariant failed. */
|
|
229
|
+
function throwIfErrors(workflow: WorkflowDef, errors: readonly string[]): void {
|
|
230
|
+
if (errors.length > 0) {
|
|
231
|
+
throw new WorkflowValidationError(`Workflow "${workflow.name}" is invalid: ${errors.join('; ')}`, errors);
|
|
67
232
|
}
|
|
68
233
|
}
|
package/src/schema.ts
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
|
|
3
|
+
/** Identifier names reserved for runtime template namespaces; not allowed as user vars. */
|
|
4
|
+
const RESERVED_VAR_NAMES = new Set(['task', 'state', 'node', 'iteration', 'run', 'runtime']);
|
|
5
|
+
|
|
6
|
+
/** Valid identifier pattern for variable and env names. */
|
|
7
|
+
const IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
8
|
+
|
|
9
|
+
/** User variables: identifier-keyed string map; reserved runtime names are rejected. */
|
|
10
|
+
const VarsSchema = z.record(z.string(), z.string()).superRefine((vars, ctx) => {
|
|
11
|
+
for (const key of Object.keys(vars)) {
|
|
12
|
+
if (!IDENTIFIER.test(key)) {
|
|
13
|
+
ctx.addIssue({ code: 'custom', message: `Invalid variable name "${key}" (must be a valid identifier)` });
|
|
14
|
+
}
|
|
15
|
+
if (RESERVED_VAR_NAMES.has(key)) {
|
|
16
|
+
ctx.addIssue({ code: 'custom', message: `Variable name "${key}" is reserved for runtime use` });
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
/** Environment allowlist: identifier-named env vars exposed to templates. */
|
|
22
|
+
const EnvSchema = z.object({
|
|
23
|
+
allow: z.array(z.string().regex(IDENTIFIER, 'env.allow entries must be valid identifiers')).optional(),
|
|
24
|
+
});
|
|
25
|
+
|
|
3
26
|
/** Zod schema for workflow action definitions. */
|
|
4
27
|
export const ActionDefSchema = z.object({
|
|
5
28
|
kind: z.string().min(1),
|
|
@@ -13,57 +36,79 @@ export const GuardDefSchema = z.object({
|
|
|
13
36
|
});
|
|
14
37
|
|
|
15
38
|
/** Zod schema for state-machine workflow definitions. */
|
|
16
|
-
export const StateMachineWorkflowDefSchema = z
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
z.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
export const StateMachineWorkflowDefSchema = z
|
|
40
|
+
.object({
|
|
41
|
+
$schema: z.string().optional(),
|
|
42
|
+
kind: z.literal('state-machine').optional(),
|
|
43
|
+
name: z.string().min(1),
|
|
44
|
+
// Optional, behavior-free document version tag (accepted for forward/backward compat).
|
|
45
|
+
version: z.string().optional(),
|
|
46
|
+
description: z.string().optional(),
|
|
47
|
+
initialState: z.string().min(1),
|
|
48
|
+
terminalStates: z.array(z.string().min(1)).optional(),
|
|
49
|
+
iterationBound: z.number().int().positive().optional(),
|
|
50
|
+
vars: VarsSchema.optional(),
|
|
51
|
+
env: EnvSchema.optional(),
|
|
52
|
+
states: z.array(
|
|
53
|
+
z
|
|
54
|
+
.object({
|
|
55
|
+
id: z.string().min(1),
|
|
56
|
+
description: z.string().optional(),
|
|
57
|
+
onEnter: z.array(ActionDefSchema).optional(),
|
|
58
|
+
onExit: z.array(ActionDefSchema).optional(),
|
|
59
|
+
})
|
|
60
|
+
.strict(),
|
|
61
|
+
),
|
|
62
|
+
transitions: z.array(
|
|
63
|
+
z
|
|
64
|
+
.object({
|
|
65
|
+
from: z.string().min(1),
|
|
66
|
+
to: z.string().min(1),
|
|
67
|
+
description: z.string().optional(),
|
|
68
|
+
trigger: z.string().optional(),
|
|
69
|
+
guard: GuardDefSchema.optional(),
|
|
70
|
+
})
|
|
71
|
+
.strict(),
|
|
72
|
+
),
|
|
73
|
+
})
|
|
74
|
+
.strict();
|
|
41
75
|
|
|
42
76
|
/** Zod schema for transition-flow workflow definitions. */
|
|
43
|
-
export const TransitionFlowWorkflowDefSchema = z
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
z.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
77
|
+
export const TransitionFlowWorkflowDefSchema = z
|
|
78
|
+
.object({
|
|
79
|
+
$schema: z.string().optional(),
|
|
80
|
+
kind: z.literal('transition-flow'),
|
|
81
|
+
name: z.string().min(1),
|
|
82
|
+
// Optional, behavior-free document version tag (accepted for forward/backward compat).
|
|
83
|
+
version: z.string().optional(),
|
|
84
|
+
description: z.string().optional(),
|
|
85
|
+
initialNode: z.string().min(1),
|
|
86
|
+
terminalNodes: z.array(z.string().min(1)).optional(),
|
|
87
|
+
iterationBound: z.number().int().positive().optional(),
|
|
88
|
+
vars: VarsSchema.optional(),
|
|
89
|
+
env: EnvSchema.optional(),
|
|
90
|
+
nodes: z.array(
|
|
91
|
+
z
|
|
92
|
+
.object({
|
|
93
|
+
id: z.string().min(1),
|
|
94
|
+
description: z.string().optional(),
|
|
95
|
+
type: z.enum(['action', 'gate', 'parallel', 'decision']).optional(),
|
|
96
|
+
action: ActionDefSchema.optional(),
|
|
97
|
+
})
|
|
98
|
+
.strict(),
|
|
99
|
+
),
|
|
100
|
+
edges: z.array(
|
|
101
|
+
z
|
|
102
|
+
.object({
|
|
103
|
+
from: z.string().min(1),
|
|
104
|
+
to: z.string().min(1),
|
|
105
|
+
description: z.string().optional(),
|
|
106
|
+
condition: GuardDefSchema.optional(),
|
|
107
|
+
})
|
|
108
|
+
.strict(),
|
|
109
|
+
),
|
|
110
|
+
})
|
|
111
|
+
.strict();
|
|
67
112
|
|
|
68
113
|
/** Zod schema for either supported workflow definition shape. */
|
|
69
114
|
export const WorkflowDefSchema = z.union([StateMachineWorkflowDefSchema, TransitionFlowWorkflowDefSchema]);
|