@jarenjs/flow 0.72.3 → 0.73.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/README.md +60 -3
- package/dist/types/dag.d.ts +6 -4
- package/dist/types/errors.d.ts +10 -0
- package/dist/types/fsm.d.ts +24 -0
- package/dist/types/index.d.ts +18 -0
- package/dist/types/statechart.d.ts +82 -0
- package/dist/types/workflow.d.ts +97 -0
- package/docs/APP-INTEGRATION.md +8 -0
- package/docs/FLOW-FORMAT.md +67 -11
- package/docs/STATECHART-FORMAT.md +186 -0
- package/docs/WORKFLOW-FORMAT.md +260 -0
- package/package.json +4 -4
- package/schemas/jaren-statechart-state.draft-07.schema.json +72 -0
- package/schemas/jaren-statechart-state.schema.json +72 -0
- package/schemas/jaren-statechart.authoring.schema.json +216 -0
- package/schemas/jaren-statechart.draft-07.schema.json +216 -0
- package/schemas/jaren-statechart.schema.json +216 -0
- package/schemas/jaren-workflow.authoring.schema.json +282 -0
- package/schemas/jaren-workflow.draft-07.schema.json +288 -0
- package/schemas/jaren-workflow.schema.json +288 -0
- package/src/dag.js +52 -24
- package/src/errors.js +10 -0
- package/src/fsm.js +2 -2
- package/src/index.js +11 -0
- package/src/statechart.js +420 -0
- package/src/workflow.js +461 -0
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://jarenjs.dev/schemas/jaren-workflow/0.2",
|
|
4
|
+
"title": "Jaren composed workflow 0.2",
|
|
5
|
+
"description": "Nested work, choices, waits and bounded control lower onto jaren-fsm 0.2 and jaren-dag 0.1. Distinct from the historical Mermaid jaren-workflow 0.1 projection. Every automatic cycle must cross a declared visit limit; every task needs an implementation version.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"$workflow": {
|
|
9
|
+
"const": "0.2"
|
|
10
|
+
},
|
|
11
|
+
"revision": {
|
|
12
|
+
"type": "string",
|
|
13
|
+
"minLength": 1
|
|
14
|
+
},
|
|
15
|
+
"initial": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"minLength": 1
|
|
18
|
+
},
|
|
19
|
+
"states": {
|
|
20
|
+
"type": "object",
|
|
21
|
+
"minProperties": 1,
|
|
22
|
+
"propertyNames": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"minLength": 1
|
|
25
|
+
},
|
|
26
|
+
"additionalProperties": {
|
|
27
|
+
"$ref": "#/$defs/state"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"required": [
|
|
32
|
+
"$workflow",
|
|
33
|
+
"revision",
|
|
34
|
+
"initial",
|
|
35
|
+
"states"
|
|
36
|
+
],
|
|
37
|
+
"additionalProperties": false,
|
|
38
|
+
"$defs": {
|
|
39
|
+
"queryDocument": {
|
|
40
|
+
"$ref": "https://jarenjs.dev/schemas/jaren-query/0.1"
|
|
41
|
+
},
|
|
42
|
+
"dagDocument": {
|
|
43
|
+
"$ref": "https://jarenjs.dev/schemas/jaren-dag/0.1"
|
|
44
|
+
},
|
|
45
|
+
"body": {
|
|
46
|
+
"type": "object",
|
|
47
|
+
"properties": {
|
|
48
|
+
"initial": {
|
|
49
|
+
"type": "string",
|
|
50
|
+
"minLength": 1
|
|
51
|
+
},
|
|
52
|
+
"states": {
|
|
53
|
+
"type": "object",
|
|
54
|
+
"minProperties": 1,
|
|
55
|
+
"propertyNames": {
|
|
56
|
+
"type": "string",
|
|
57
|
+
"minLength": 1
|
|
58
|
+
},
|
|
59
|
+
"additionalProperties": {
|
|
60
|
+
"$ref": "#/$defs/state"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
"required": [
|
|
65
|
+
"initial",
|
|
66
|
+
"states"
|
|
67
|
+
],
|
|
68
|
+
"additionalProperties": false
|
|
69
|
+
},
|
|
70
|
+
"state": {
|
|
71
|
+
"anyOf": [
|
|
72
|
+
{
|
|
73
|
+
"type": "object",
|
|
74
|
+
"properties": {
|
|
75
|
+
"work": {
|
|
76
|
+
"anyOf": [
|
|
77
|
+
{
|
|
78
|
+
"type": "object",
|
|
79
|
+
"properties": {
|
|
80
|
+
"task": {
|
|
81
|
+
"type": "string",
|
|
82
|
+
"minLength": 1
|
|
83
|
+
},
|
|
84
|
+
"version": {
|
|
85
|
+
"type": "string",
|
|
86
|
+
"minLength": 1
|
|
87
|
+
},
|
|
88
|
+
"with": {
|
|
89
|
+
"$ref": "#/$defs/queryDocument"
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
"required": [
|
|
93
|
+
"task",
|
|
94
|
+
"version"
|
|
95
|
+
],
|
|
96
|
+
"additionalProperties": false
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
"type": "object",
|
|
100
|
+
"properties": {
|
|
101
|
+
"dag": {
|
|
102
|
+
"$ref": "#/$defs/dagDocument"
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
"required": [
|
|
106
|
+
"dag"
|
|
107
|
+
],
|
|
108
|
+
"additionalProperties": false
|
|
109
|
+
}
|
|
110
|
+
]
|
|
111
|
+
},
|
|
112
|
+
"input": {
|
|
113
|
+
"$ref": "#/$defs/queryDocument"
|
|
114
|
+
},
|
|
115
|
+
"then": {
|
|
116
|
+
"type": "string",
|
|
117
|
+
"minLength": 1
|
|
118
|
+
},
|
|
119
|
+
"catch": {
|
|
120
|
+
"type": "string",
|
|
121
|
+
"minLength": 1
|
|
122
|
+
},
|
|
123
|
+
"limit": {
|
|
124
|
+
"type": "integer",
|
|
125
|
+
"minimum": 1,
|
|
126
|
+
"maximum": 9007199254740991
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
"required": [
|
|
130
|
+
"work",
|
|
131
|
+
"then"
|
|
132
|
+
],
|
|
133
|
+
"additionalProperties": false
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
"type": "object",
|
|
137
|
+
"properties": {
|
|
138
|
+
"choose": {
|
|
139
|
+
"type": "array",
|
|
140
|
+
"items": {
|
|
141
|
+
"type": "object",
|
|
142
|
+
"properties": {
|
|
143
|
+
"guard": {
|
|
144
|
+
"$ref": "#/$defs/queryDocument"
|
|
145
|
+
},
|
|
146
|
+
"to": {
|
|
147
|
+
"type": "string",
|
|
148
|
+
"minLength": 1
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
"required": [
|
|
152
|
+
"guard",
|
|
153
|
+
"to"
|
|
154
|
+
],
|
|
155
|
+
"additionalProperties": false
|
|
156
|
+
},
|
|
157
|
+
"minItems": 1
|
|
158
|
+
},
|
|
159
|
+
"otherwise": {
|
|
160
|
+
"type": "string",
|
|
161
|
+
"minLength": 1
|
|
162
|
+
},
|
|
163
|
+
"limit": {
|
|
164
|
+
"type": "integer",
|
|
165
|
+
"minimum": 1,
|
|
166
|
+
"maximum": 9007199254740991
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
"required": [
|
|
170
|
+
"choose",
|
|
171
|
+
"otherwise"
|
|
172
|
+
],
|
|
173
|
+
"additionalProperties": false
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
"type": "object",
|
|
177
|
+
"properties": {
|
|
178
|
+
"on": {
|
|
179
|
+
"type": "array",
|
|
180
|
+
"items": {
|
|
181
|
+
"type": "object",
|
|
182
|
+
"properties": {
|
|
183
|
+
"event": {
|
|
184
|
+
"type": "string",
|
|
185
|
+
"minLength": 1
|
|
186
|
+
},
|
|
187
|
+
"to": {
|
|
188
|
+
"type": "string",
|
|
189
|
+
"minLength": 1
|
|
190
|
+
},
|
|
191
|
+
"guard": {
|
|
192
|
+
"$ref": "#/$defs/queryDocument"
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
"required": [
|
|
196
|
+
"event",
|
|
197
|
+
"to"
|
|
198
|
+
],
|
|
199
|
+
"additionalProperties": false
|
|
200
|
+
},
|
|
201
|
+
"minItems": 0
|
|
202
|
+
},
|
|
203
|
+
"after": {
|
|
204
|
+
"type": "object",
|
|
205
|
+
"properties": {
|
|
206
|
+
"ms": {
|
|
207
|
+
"type": "number",
|
|
208
|
+
"minimum": 0
|
|
209
|
+
},
|
|
210
|
+
"to": {
|
|
211
|
+
"type": "string",
|
|
212
|
+
"minLength": 1
|
|
213
|
+
}
|
|
214
|
+
},
|
|
215
|
+
"required": [
|
|
216
|
+
"ms",
|
|
217
|
+
"to"
|
|
218
|
+
],
|
|
219
|
+
"additionalProperties": false
|
|
220
|
+
},
|
|
221
|
+
"limit": {
|
|
222
|
+
"type": "integer",
|
|
223
|
+
"minimum": 1,
|
|
224
|
+
"maximum": 9007199254740991
|
|
225
|
+
}
|
|
226
|
+
},
|
|
227
|
+
"required": [
|
|
228
|
+
"on"
|
|
229
|
+
],
|
|
230
|
+
"additionalProperties": false,
|
|
231
|
+
"anyOf": [
|
|
232
|
+
{
|
|
233
|
+
"properties": {
|
|
234
|
+
"on": {
|
|
235
|
+
"minItems": 1
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
"required": [
|
|
241
|
+
"after"
|
|
242
|
+
]
|
|
243
|
+
}
|
|
244
|
+
]
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
"type": "object",
|
|
248
|
+
"properties": {
|
|
249
|
+
"flow": {
|
|
250
|
+
"$ref": "#/$defs/body"
|
|
251
|
+
},
|
|
252
|
+
"then": {
|
|
253
|
+
"type": "string",
|
|
254
|
+
"minLength": 1
|
|
255
|
+
},
|
|
256
|
+
"limit": {
|
|
257
|
+
"type": "integer",
|
|
258
|
+
"minimum": 1,
|
|
259
|
+
"maximum": 9007199254740991
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
"required": [
|
|
263
|
+
"flow",
|
|
264
|
+
"then"
|
|
265
|
+
],
|
|
266
|
+
"additionalProperties": false
|
|
267
|
+
},
|
|
268
|
+
{
|
|
269
|
+
"type": "object",
|
|
270
|
+
"properties": {
|
|
271
|
+
"final": {
|
|
272
|
+
"const": true
|
|
273
|
+
},
|
|
274
|
+
"limit": {
|
|
275
|
+
"type": "integer",
|
|
276
|
+
"minimum": 1,
|
|
277
|
+
"maximum": 9007199254740991
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
"required": [
|
|
281
|
+
"final"
|
|
282
|
+
],
|
|
283
|
+
"additionalProperties": false
|
|
284
|
+
}
|
|
285
|
+
]
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
package/src/dag.js
CHANGED
|
@@ -57,9 +57,9 @@ function compileEmbedded(compile, embedded, docPath) {
|
|
|
57
57
|
* value, `complete` records the run's result. Any member may return a
|
|
58
58
|
* promise; a throwing store fails the run (JF2009), never silently.
|
|
59
59
|
* @typedef {Object} DagCheckpointStore
|
|
60
|
-
* @property {(runId: string) => any} load
|
|
61
|
-
* @property {(runId: string, nodeId: string, value: any) => any} save
|
|
62
|
-
* @property {(runId: string, result: any) => any} complete
|
|
60
|
+
* @property {(runId: string, identity?: any) => any} load
|
|
61
|
+
* @property {(runId: string, nodeId: string, value: any, identity?: any) => any} save
|
|
62
|
+
* @property {(runId: string, result: any, identity?: any) => any} complete
|
|
63
63
|
*/
|
|
64
64
|
|
|
65
65
|
/**
|
|
@@ -71,7 +71,7 @@ function compileEmbedded(compile, embedded, docPath) {
|
|
|
71
71
|
* declared task identity this workflow depends on, keyed by node id and
|
|
72
72
|
* SORTED (§7.8); a nested workflow's map composes under its node's
|
|
73
73
|
* path. Empty when no node declares a version.
|
|
74
|
-
* @property {(input?: any, opts?: { signal?: AbortSignal, onNode?: (record: DagNodeRecord) => void, runId?: string }) => Promise<any>} run -
|
|
74
|
+
* @property {(input?: any, opts?: { signal?: AbortSignal, onNode?: (record: DagNodeRecord) => void, runId?: string, drainOnAbort?: boolean }) => Promise<any>} run -
|
|
75
75
|
* Execute the graph for one input (`undefined` reads as `null`).
|
|
76
76
|
*/
|
|
77
77
|
|
|
@@ -124,7 +124,7 @@ function normalizeTaskEntry(entry, name) {
|
|
|
124
124
|
* @param {{ tasks?: Record<string, ((props: { with: any, input: any }, signal: AbortSignal) => any)
|
|
125
125
|
* | { run: (props: { with: any, input: any }, signal: AbortSignal) => any, version?: string,
|
|
126
126
|
* taskVersions?: Record<string, string> }>,
|
|
127
|
-
* checkpoint?: DagCheckpointStore }} [options]
|
|
127
|
+
* checkpoint?: DagCheckpointStore, revision?: string }} [options]
|
|
128
128
|
* @returns {CompiledDag}
|
|
129
129
|
* @throws {FlowCompileError} when the document violates the format (JF0xxx)
|
|
130
130
|
* @throws {TypeError} when the options are malformed (a registry that is
|
|
@@ -137,6 +137,9 @@ export function compileDag(doc, options) {
|
|
|
137
137
|
throw new TypeError('compileDag: "tasks" must be an object of handler functions');
|
|
138
138
|
}
|
|
139
139
|
const checkpoint = options?.checkpoint;
|
|
140
|
+
const revision = options?.revision;
|
|
141
|
+
if (revision !== undefined && (typeof revision !== 'string' || !revision.trim() || !checkpoint))
|
|
142
|
+
throw new TypeError('compileDag: revision must be nonblank and requires a checkpoint store');
|
|
140
143
|
if (checkpoint !== undefined && (typeof checkpoint?.load !== 'function'
|
|
141
144
|
|| typeof checkpoint.save !== 'function'
|
|
142
145
|
|| typeof checkpoint.complete !== 'function')) {
|
|
@@ -262,6 +265,9 @@ export function compileDag(doc, options) {
|
|
|
262
265
|
`a dag declares exactly one output node (found ${outputs.length})`, '/nodes');
|
|
263
266
|
}
|
|
264
267
|
const outputId = outputs[0];
|
|
268
|
+
// Exact canonical provenance is opt-in for legacy stores. It is compared
|
|
269
|
+
// before any saved node value can enter the memo, not merely handed to a host.
|
|
270
|
+
const documentIdentity = revision === undefined ? null : canonicalizeJson(doc);
|
|
265
271
|
|
|
266
272
|
if (!Array.isArray(doc.edges)) {
|
|
267
273
|
throw new FlowCompileError('JF0012',
|
|
@@ -393,6 +399,8 @@ export function compileDag(doc, options) {
|
|
|
393
399
|
throw new TypeError('run: "signal" must be an AbortSignal');
|
|
394
400
|
}
|
|
395
401
|
const onNode = opts?.onNode;
|
|
402
|
+
if (opts?.drainOnAbort !== undefined && typeof opts.drainOnAbort !== 'boolean')
|
|
403
|
+
throw new TypeError('run: drainOnAbort must be boolean');
|
|
396
404
|
if (onNode !== undefined && typeof onNode !== 'function') {
|
|
397
405
|
throw new TypeError('run: "onNode" must be a function');
|
|
398
406
|
}
|
|
@@ -405,7 +413,7 @@ export function compileDag(doc, options) {
|
|
|
405
413
|
throw new TypeError(
|
|
406
414
|
'run: a checkpointed dag needs a non-empty string "runId" to persist under');
|
|
407
415
|
}
|
|
408
|
-
return execute(input === undefined ? null : input, signal, onNode, runId);
|
|
416
|
+
return execute(input === undefined ? null : input, signal, onNode, runId, opts?.drainOnAbort === true);
|
|
409
417
|
}
|
|
410
418
|
|
|
411
419
|
/**
|
|
@@ -413,11 +421,17 @@ export function compileDag(doc, options) {
|
|
|
413
421
|
* @param {AbortSignal|undefined} signal
|
|
414
422
|
* @param {((record: DagNodeRecord) => void)|undefined} onNode
|
|
415
423
|
* @param {string|undefined} runId
|
|
424
|
+
* @param {boolean} drainOnAbort
|
|
416
425
|
*/
|
|
417
|
-
async function execute(runInput, signal, onNode, runId) {
|
|
426
|
+
async function execute(runInput, signal, onNode, runId, drainOnAbort) {
|
|
418
427
|
const controller = new AbortController();
|
|
419
428
|
/** @type {FlowRuntimeError|null} */
|
|
420
429
|
let failure = null;
|
|
430
|
+
let rejectAborted;
|
|
431
|
+
const aborted = new Promise((_, reject) => { rejectAborted = reject; });
|
|
432
|
+
aborted.catch(() => {});
|
|
433
|
+
const identity = revision === undefined ? undefined
|
|
434
|
+
: Object.freeze({ revision, document: documentIdentity, input: canonicalizeJson(runInput), taskVersions });
|
|
421
435
|
|
|
422
436
|
/** @param {DagNodeRecord} rec */
|
|
423
437
|
const record = (rec) => {
|
|
@@ -437,6 +451,7 @@ export function compileDag(doc, options) {
|
|
|
437
451
|
if (failure === null) {
|
|
438
452
|
failure = err;
|
|
439
453
|
controller.abort();
|
|
454
|
+
rejectAborted(err);
|
|
440
455
|
}
|
|
441
456
|
};
|
|
442
457
|
|
|
@@ -463,11 +478,17 @@ export function compileDag(doc, options) {
|
|
|
463
478
|
if (checkpoint !== undefined && runId !== undefined) {
|
|
464
479
|
let loaded;
|
|
465
480
|
try {
|
|
466
|
-
loaded = await checkpoint.load(runId);
|
|
481
|
+
loaded = await Promise.race([checkpoint.load(runId, identity), aborted]);
|
|
482
|
+
if (identity !== undefined && loaded != null
|
|
483
|
+
&& canonicalizeJson(loaded.identity ?? null) !== canonicalizeJson(identity)) {
|
|
484
|
+
throw new FlowRuntimeError('JF2013', `checkpoint '${runId}' has different or missing provenance`);
|
|
485
|
+
}
|
|
467
486
|
}
|
|
468
487
|
catch (err) {
|
|
469
488
|
if (signal !== undefined && onAbort !== null)
|
|
470
489
|
signal.removeEventListener('abort', onAbort);
|
|
490
|
+
if (failure !== null) throw failure;
|
|
491
|
+
if (err instanceof FlowRuntimeError && err.code === 'JF2013') throw err;
|
|
471
492
|
const cause = asError(err);
|
|
472
493
|
throw new FlowRuntimeError('JF2009',
|
|
473
494
|
`the checkpoint store failed to load run '${runId}': ${cause.message}`,
|
|
@@ -550,6 +571,7 @@ export function compileDag(doc, options) {
|
|
|
550
571
|
}
|
|
551
572
|
default: value = null; break;
|
|
552
573
|
}
|
|
574
|
+
if (failure !== null) throw failure;
|
|
553
575
|
if (node.checkpoint && checkpoint !== undefined && runId !== undefined) {
|
|
554
576
|
// the explicit serialization contract (§7.6): the node
|
|
555
577
|
// DECLARED its output JSON; a value that is not fails the
|
|
@@ -564,7 +586,7 @@ export function compileDag(doc, options) {
|
|
|
564
586
|
+ `not JSON-serializable: ${cause.message}`, node.docPath, cause);
|
|
565
587
|
}
|
|
566
588
|
try {
|
|
567
|
-
await checkpoint.save(runId, node.id, value);
|
|
589
|
+
await Promise.race([checkpoint.save(runId, node.id, value, identity), aborted]);
|
|
568
590
|
}
|
|
569
591
|
catch (err) {
|
|
570
592
|
const cause = asError(err);
|
|
@@ -573,6 +595,7 @@ export function compileDag(doc, options) {
|
|
|
573
595
|
node.docPath, cause);
|
|
574
596
|
}
|
|
575
597
|
}
|
|
598
|
+
if (failure !== null) throw failure;
|
|
576
599
|
settle('ok');
|
|
577
600
|
return value;
|
|
578
601
|
}
|
|
@@ -603,9 +626,28 @@ export function compileDag(doc, options) {
|
|
|
603
626
|
});
|
|
604
627
|
|
|
605
628
|
try {
|
|
606
|
-
await Promise.all(all);
|
|
629
|
+
await Promise.race([Promise.all(all), aborted]);
|
|
630
|
+
if (failure !== null) throw failure;
|
|
631
|
+
const result = await promises.get(outputId);
|
|
632
|
+
if (checkpoint !== undefined && runId !== undefined) {
|
|
633
|
+
try {
|
|
634
|
+
await Promise.race([checkpoint.complete(runId, result, identity), aborted]);
|
|
635
|
+
}
|
|
636
|
+
catch (err) {
|
|
637
|
+
if (failure !== null) throw failure;
|
|
638
|
+
const cause = asError(err);
|
|
639
|
+
throw new FlowRuntimeError('JF2009',
|
|
640
|
+
`the checkpoint store failed to complete run '${runId}': ${cause.message}`,
|
|
641
|
+
'', cause);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
return result;
|
|
607
645
|
}
|
|
608
646
|
catch (err) {
|
|
647
|
+
// A worker's shutdown report accounts for actual task lifetimes. It
|
|
648
|
+
// owns a separate grace deadline, so it may ask us to retain the run
|
|
649
|
+
// until ignoring handlers settle instead of reporting early drainage.
|
|
650
|
+
if (drainOnAbort && failure?.code === 'JF2007') await Promise.allSettled(all);
|
|
609
651
|
throw failure ?? err;
|
|
610
652
|
}
|
|
611
653
|
finally {
|
|
@@ -613,20 +655,6 @@ export function compileDag(doc, options) {
|
|
|
613
655
|
signal.removeEventListener('abort', onAbort);
|
|
614
656
|
}
|
|
615
657
|
}
|
|
616
|
-
if (failure !== null) throw failure;
|
|
617
|
-
const result = await promises.get(outputId);
|
|
618
|
-
if (checkpoint !== undefined && runId !== undefined) {
|
|
619
|
-
try {
|
|
620
|
-
await checkpoint.complete(runId, result);
|
|
621
|
-
}
|
|
622
|
-
catch (err) {
|
|
623
|
-
const cause = asError(err);
|
|
624
|
-
throw new FlowRuntimeError('JF2009',
|
|
625
|
-
`the checkpoint store failed to complete run '${runId}': ${cause.message}`,
|
|
626
|
-
'', cause);
|
|
627
|
-
}
|
|
628
|
-
}
|
|
629
|
-
return result;
|
|
630
658
|
}
|
|
631
659
|
|
|
632
660
|
// The canonical version map: every declared task identity this
|
package/src/errors.js
CHANGED
|
@@ -37,6 +37,9 @@ export const FLOW_CODES = Object.freeze({
|
|
|
37
37
|
JF0017: 'the document does not declare exactly one output node',
|
|
38
38
|
JF0018: 'a task node names a handler the registry does not provide',
|
|
39
39
|
JF0019: 'a task node and its registered handler disagree about the handler version',
|
|
40
|
+
JF0020: 'a statechart declaration violates hierarchy or transition rules',
|
|
41
|
+
JF0021: 'a workflow declaration or reference is malformed',
|
|
42
|
+
JF0022: 'an automatic workflow cycle has no declared visit bound',
|
|
40
43
|
JF2001: 'a state id the machine does not declare',
|
|
41
44
|
JF2002: 'step was called with a non-string event',
|
|
42
45
|
JF2003: 'a guard threw while evaluating',
|
|
@@ -46,6 +49,13 @@ export const FLOW_CODES = Object.freeze({
|
|
|
46
49
|
JF2007: 'the caller signal aborted the run',
|
|
47
50
|
JF2008: 'a declared checkpoint value is not JSON-serializable',
|
|
48
51
|
JF2009: 'the checkpoint store failed',
|
|
52
|
+
JF2010: 'a statechart snapshot is malformed or has an illegal configuration',
|
|
53
|
+
JF2011: 'explicit time moved backwards or a timer exceeded its numeric range',
|
|
54
|
+
JF2012: 'a statechart exceeded its microstep limit',
|
|
55
|
+
JF2013: 'a checkpoint belongs to a different workflow, input or task implementation',
|
|
56
|
+
JF2014: 'a workflow run is busy or its snapshot generation is stale',
|
|
57
|
+
JF2015: 'a workflow state exceeded its declared visit bound',
|
|
58
|
+
JF2016: 'a workflow snapshot is malformed or execution could not progress',
|
|
49
59
|
});
|
|
50
60
|
|
|
51
61
|
/**
|
package/src/fsm.js
CHANGED
|
@@ -33,7 +33,7 @@ import { asError, FlowCompileError, FlowRuntimeError } from './errors.js';
|
|
|
33
33
|
* @param {string} docPath - JSON Pointer of the list member.
|
|
34
34
|
* @returns {CompiledEffect[]}
|
|
35
35
|
*/
|
|
36
|
-
function compileEffectList(list, docPath) {
|
|
36
|
+
export function compileEffectList(list, docPath) {
|
|
37
37
|
if (list === undefined) return [];
|
|
38
38
|
if (!Array.isArray(list)) {
|
|
39
39
|
throw new FlowCompileError('JF0008',
|
|
@@ -70,7 +70,7 @@ function compileEffectList(list, docPath) {
|
|
|
70
70
|
* @param {any[]} out - resolved descriptors, appended to
|
|
71
71
|
* @param {any[]} errors - step-result error records, appended to
|
|
72
72
|
*/
|
|
73
|
-
function resolveEffect(effect, scope, out, errors) {
|
|
73
|
+
export function resolveEffect(effect, scope, out, errors) {
|
|
74
74
|
if (effect.with === null) {
|
|
75
75
|
out.push({ run: effect.run });
|
|
76
76
|
return;
|
package/src/index.js
CHANGED
|
@@ -9,5 +9,16 @@
|
|
|
9
9
|
export { compileFsm, createFsmSession } from './fsm.js';
|
|
10
10
|
export { fsmToApp, fsmStateSchema } from './app.js';
|
|
11
11
|
export { compileDag } from './dag.js';
|
|
12
|
+
export { compileStatechart, createStatechartSession } from './statechart.js';
|
|
13
|
+
export { lowerWorkflow, compileWorkflow } from './workflow.js';
|
|
12
14
|
export { snapshotFsm, resumeFsmSession, createDurableFsmSession } from './persist.js';
|
|
13
15
|
export { FlowCompileError, FlowRuntimeError, FLOW_CODES } from './errors.js';
|
|
16
|
+
|
|
17
|
+
/** @typedef {import('./statechart.js').StatechartState} StatechartState */
|
|
18
|
+
/** @typedef {import('./statechart.js').StatechartResult} StatechartResult */
|
|
19
|
+
/** @typedef {import('./statechart.js').CompiledStatechart} CompiledStatechart */
|
|
20
|
+
/** @typedef {import('./workflow.js').LoweredWorkflow} LoweredWorkflow */
|
|
21
|
+
/** @typedef {import('./workflow.js').WorkflowSnapshot} WorkflowSnapshot */
|
|
22
|
+
/** @typedef {import('./workflow.js').WorkflowStore} WorkflowStore */
|
|
23
|
+
/** @typedef {import('./workflow.js').WorkflowResult} WorkflowResult */
|
|
24
|
+
/** @typedef {import('./workflow.js').CompiledWorkflow} CompiledWorkflow */
|