@sanity/workflow-engine 0.1.0 → 0.2.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/dist/define.cjs +271 -249
- package/dist/define.cjs.map +1 -1
- package/dist/define.d.cts +5035 -2647
- package/dist/define.d.ts +5035 -2647
- package/dist/define.js +255 -249
- package/dist/define.js.map +1 -1
- package/dist/index.cjs +626 -341
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5862 -3151
- package/dist/index.d.ts +5862 -3151
- package/dist/index.js +610 -341
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
import { parse, evaluate } from "groq-js";
|
|
2
|
-
import
|
|
2
|
+
import * as v from "valibot";
|
|
3
3
|
function validateDefinition(definition) {
|
|
4
|
-
const
|
|
4
|
+
const v2 = createDefinitionValidator();
|
|
5
5
|
for (const slot of definition.state ?? [])
|
|
6
|
-
|
|
6
|
+
v2.checkSlot(slot, `workflow.state "${slot.id}"`);
|
|
7
7
|
for (const p of definition.predicates ?? [])
|
|
8
|
-
p.groq &&
|
|
8
|
+
p.groq && v2.checkFilter(p.groq, `predicate "${p.id}"`);
|
|
9
9
|
for (const stage of definition.stages)
|
|
10
|
-
validateStage(
|
|
11
|
-
if (
|
|
10
|
+
validateStage(v2, stage);
|
|
11
|
+
if (v2.errors.length > 0)
|
|
12
12
|
throw new Error(
|
|
13
|
-
`defineWorkflow("${definition.workflowId}", v${definition.version}): ${
|
|
14
|
-
` +
|
|
13
|
+
`defineWorkflow("${definition.workflowId}", v${definition.version}): ${v2.errors.length} validation error${v2.errors.length === 1 ? "" : "s"}:
|
|
14
|
+
` + v2.errors.join(`
|
|
15
15
|
`)
|
|
16
16
|
);
|
|
17
17
|
}
|
|
@@ -30,31 +30,29 @@ function createDefinitionValidator() {
|
|
|
30
30
|
};
|
|
31
31
|
return { errors, tryParse, checkFilter: (groq, where) => {
|
|
32
32
|
tryParse(groq, where), rejectTypeScan(groq, where);
|
|
33
|
-
}, checkSlot: (slot,
|
|
34
|
-
slot.source.kind === "
|
|
35
|
-
` \xB7 ${scopeLabel} slot "${slot.id}": source.kind="computed" is reserved (not implemented in v0.1)`
|
|
36
|
-
), slot.source.kind === "query" && tryParse(slot.source.query, `${slotLabel}.query`);
|
|
33
|
+
}, checkSlot: (slot, slotLabel) => {
|
|
34
|
+
slot.source.kind === "query" && tryParse(slot.source.query, `${slotLabel}.query`);
|
|
37
35
|
} };
|
|
38
36
|
}
|
|
39
|
-
function validateStage(
|
|
37
|
+
function validateStage(v2, stage) {
|
|
40
38
|
for (const slot of stage.state ?? [])
|
|
41
|
-
|
|
42
|
-
typeof stage.completion == "string" &&
|
|
39
|
+
v2.checkSlot(slot, `stage "${stage.id}".state "${slot.id}"`);
|
|
40
|
+
typeof stage.completion == "string" && v2.tryParse(stage.completion, `stage "${stage.id}".completion`);
|
|
43
41
|
for (const t of stage.transitions ?? [])
|
|
44
|
-
typeof t.filter == "string" &&
|
|
42
|
+
typeof t.filter == "string" && v2.checkFilter(t.filter, `transition ${stage.id} \u2192 ${t.to} filter`);
|
|
45
43
|
for (const task of stage.tasks ?? [])
|
|
46
|
-
validateTask(
|
|
44
|
+
validateTask(v2, stage.id, task);
|
|
47
45
|
}
|
|
48
|
-
function validateTask(
|
|
46
|
+
function validateTask(v2, stageId, task) {
|
|
49
47
|
const where = `stage "${stageId}" task "${task.id}"`;
|
|
50
48
|
for (const slot of task.state ?? [])
|
|
51
|
-
|
|
52
|
-
typeof task.filter == "string" &&
|
|
49
|
+
v2.checkSlot(slot, `${where}.state "${slot.id}"`);
|
|
50
|
+
typeof task.filter == "string" && v2.checkFilter(task.filter, `${where}.filter`), typeof task.completeWhen == "string" && v2.checkFilter(task.completeWhen, `${where}.completeWhen`);
|
|
53
51
|
for (const a of task.actions ?? [])
|
|
54
|
-
typeof a.filter == "string" &&
|
|
55
|
-
task.spawns?.forEach?.groq &&
|
|
52
|
+
typeof a.filter == "string" && v2.checkFilter(a.filter, `task "${task.id}" action "${a.id}".filter`);
|
|
53
|
+
task.spawns?.forEach?.groq && v2.tryParse(task.spawns.forEach.groq, `task "${task.id}".spawns.forEach.groq`);
|
|
56
54
|
}
|
|
57
|
-
const KNOWN_SCHEMES = /* @__PURE__ */ new Set(["dataset", "canvas", "media-library", "dashboard"]);
|
|
55
|
+
const wallClock = () => (/* @__PURE__ */ new Date()).toISOString(), KNOWN_SCHEMES = /* @__PURE__ */ new Set(["dataset", "canvas", "media-library", "dashboard"]);
|
|
58
56
|
function parseGdr(uri) {
|
|
59
57
|
const colon = uri.indexOf(":");
|
|
60
58
|
if (colon < 0)
|
|
@@ -121,6 +119,9 @@ function gdrFromResource(res, documentId) {
|
|
|
121
119
|
}
|
|
122
120
|
return gdrUri({ scheme: res.type, resourceId: res.id, documentId });
|
|
123
121
|
}
|
|
122
|
+
function selfGdr(doc) {
|
|
123
|
+
return gdrFromResource(doc.workflowResource, doc._id);
|
|
124
|
+
}
|
|
124
125
|
function isGdrUri(value) {
|
|
125
126
|
if (typeof value != "string") return !1;
|
|
126
127
|
try {
|
|
@@ -135,15 +136,15 @@ function gdrRef(res, documentId, type) {
|
|
|
135
136
|
function isGdr(value) {
|
|
136
137
|
return typeof value == "object" && value !== null && typeof value.id == "string" && typeof value.type == "string";
|
|
137
138
|
}
|
|
138
|
-
function buildParams(instance, extra) {
|
|
139
|
+
function buildParams(instance, now, extra) {
|
|
139
140
|
return {
|
|
140
|
-
self:
|
|
141
|
+
self: selfGdr(instance),
|
|
141
142
|
state: stateMap(instance.state ?? []),
|
|
142
143
|
parent: instance.ancestors.at(-1)?.id ?? null,
|
|
143
144
|
ancestors: instance.ancestors.map((a) => a.id),
|
|
144
145
|
/** Current stage id — bind for filters that scope to the current stage. */
|
|
145
146
|
stage: instance.currentStageId,
|
|
146
|
-
now
|
|
147
|
+
now,
|
|
147
148
|
...extra
|
|
148
149
|
};
|
|
149
150
|
}
|
|
@@ -167,8 +168,8 @@ function stripStateForLake(value) {
|
|
|
167
168
|
if (Array.isArray(value)) return value.map(stripStateForLake);
|
|
168
169
|
if (typeof value == "object") {
|
|
169
170
|
const out = {};
|
|
170
|
-
for (const [k,
|
|
171
|
-
out[k] = stripStateForLake(
|
|
171
|
+
for (const [k, v2] of Object.entries(value))
|
|
172
|
+
out[k] = stripStateForLake(v2);
|
|
172
173
|
return out;
|
|
173
174
|
}
|
|
174
175
|
return value;
|
|
@@ -226,7 +227,7 @@ function matchesParamType(type, value) {
|
|
|
226
227
|
case "boolean":
|
|
227
228
|
return typeof value == "boolean";
|
|
228
229
|
case "string[]":
|
|
229
|
-
return Array.isArray(value) && value.every((
|
|
230
|
+
return Array.isArray(value) && value.every((v2) => typeof v2 == "string");
|
|
230
231
|
case "reference":
|
|
231
232
|
return isGdr(value);
|
|
232
233
|
}
|
|
@@ -247,11 +248,11 @@ function restampForSnapshot(doc, gdrUri2, resource) {
|
|
|
247
248
|
}
|
|
248
249
|
function rewriteRefsRecursive(value, resource) {
|
|
249
250
|
if (Array.isArray(value))
|
|
250
|
-
return value.map((
|
|
251
|
+
return value.map((v2) => rewriteRefsRecursive(v2, resource));
|
|
251
252
|
if (value === null || typeof value != "object") return value;
|
|
252
253
|
const obj = value, out = {};
|
|
253
|
-
for (const [k,
|
|
254
|
-
k === "_ref" && typeof
|
|
254
|
+
for (const [k, v2] of Object.entries(obj))
|
|
255
|
+
k === "_ref" && typeof v2 == "string" ? out[k] = v2.includes(":") ? v2 : gdrFromResource(resource, v2) : out[k] = rewriteRefsRecursive(v2, resource);
|
|
255
256
|
return out;
|
|
256
257
|
}
|
|
257
258
|
async function hydrateSnapshot(args) {
|
|
@@ -260,7 +261,7 @@ async function hydrateSnapshot(args) {
|
|
|
260
261
|
const fetched = await loadByGdr(client, clientForGdr, instance.workflowResource, uri);
|
|
261
262
|
fetched && (loaded.push(fetched), visited.add(uri));
|
|
262
263
|
};
|
|
263
|
-
loaded.push({ doc: instance, resource: instance.workflowResource }), visited.add(
|
|
264
|
+
loaded.push({ doc: instance, resource: instance.workflowResource }), visited.add(selfGdr(instance));
|
|
264
265
|
for (const ancestor of instance.ancestors)
|
|
265
266
|
await loadInto(ancestor.id);
|
|
266
267
|
for (const uri of collectSlotDocUris(instance.state))
|
|
@@ -295,12 +296,12 @@ function slotDocUris(slot) {
|
|
|
295
296
|
if (!slot || typeof slot != "object") return [];
|
|
296
297
|
const s = slot;
|
|
297
298
|
if (s._type === "workflow.state.doc.ref") {
|
|
298
|
-
const
|
|
299
|
+
const v2 = s.value, id = refId(v2);
|
|
299
300
|
return id !== void 0 ? [id] : [];
|
|
300
301
|
}
|
|
301
302
|
if (s._type === "workflow.state.doc.refs") {
|
|
302
|
-
const
|
|
303
|
-
return Array.isArray(
|
|
303
|
+
const v2 = s.value;
|
|
304
|
+
return Array.isArray(v2) ? v2.map(refId).filter((id) => id !== void 0) : [];
|
|
304
305
|
}
|
|
305
306
|
return [];
|
|
306
307
|
}
|
|
@@ -324,50 +325,61 @@ class SlotValueShapeError extends Error {
|
|
|
324
325
|
super(`Slot ${args.mode} shape invalid for "${args.slotId}" (${args.slotType}): ${issueText}`), this.name = "SlotValueShapeError", this.slotType = args.slotType, this.slotId = args.slotId, this.issues = args.issues;
|
|
325
326
|
}
|
|
326
327
|
}
|
|
327
|
-
const GdrShape =
|
|
328
|
-
id:
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
type:
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
328
|
+
const GdrShape = v.looseObject({
|
|
329
|
+
id: v.pipe(
|
|
330
|
+
v.string(),
|
|
331
|
+
v.check((s) => isGdrUri(s), "must be a GDR URI")
|
|
332
|
+
),
|
|
333
|
+
type: v.pipe(v.string(), v.minLength(1))
|
|
334
|
+
}), ReleaseRefShape = v.looseObject({
|
|
335
|
+
id: v.pipe(
|
|
336
|
+
v.string(),
|
|
337
|
+
v.check((s) => isGdrUri(s), "must be a GDR URI")
|
|
338
|
+
),
|
|
339
|
+
type: v.literal("system.release"),
|
|
340
|
+
releaseName: v.pipe(v.string(), v.minLength(1))
|
|
341
|
+
}), ActorShape = v.looseObject({
|
|
342
|
+
kind: v.picklist(["user", "ai", "system"]),
|
|
343
|
+
id: v.pipe(v.string(), v.minLength(1)),
|
|
344
|
+
roles: v.optional(v.array(v.string())),
|
|
345
|
+
onBehalfOf: v.optional(v.string())
|
|
346
|
+
}), AssigneeShape = v.union([
|
|
347
|
+
v.looseObject({ kind: v.literal("user"), id: v.pipe(v.string(), v.minLength(1)) }),
|
|
348
|
+
v.looseObject({ kind: v.literal("role"), role: v.pipe(v.string(), v.minLength(1)) })
|
|
349
|
+
]), ChecklistItemShape = v.looseObject({
|
|
350
|
+
label: v.pipe(v.string(), v.minLength(1)),
|
|
351
|
+
done: v.boolean(),
|
|
352
|
+
doneBy: v.optional(v.string()),
|
|
353
|
+
doneAt: v.optional(v.string()),
|
|
354
|
+
_key: v.optional(v.pipe(v.string(), v.minLength(1)))
|
|
355
|
+
}), NoteItemShape = v.pipe(
|
|
356
|
+
v.looseObject({
|
|
357
|
+
_key: v.optional(v.pipe(v.string(), v.minLength(1)))
|
|
358
|
+
}),
|
|
359
|
+
v.check(
|
|
360
|
+
(val) => typeof val == "object" && val !== null && !Array.isArray(val),
|
|
361
|
+
"must be an object"
|
|
362
|
+
)
|
|
363
|
+
), NullableString = v.union([v.null(), v.string()]), NullableNumber = v.union([v.null(), v.number()]), NullableBoolean = v.union([v.null(), v.boolean()]), NullableDateTime = v.union([
|
|
364
|
+
v.null(),
|
|
365
|
+
v.pipe(
|
|
366
|
+
v.string(),
|
|
367
|
+
v.check((s) => !Number.isNaN(Date.parse(s)), "must be an ISO-8601 datetime string")
|
|
368
|
+
)
|
|
357
369
|
]), NullableUrl = NullableString, valueSchemas = {
|
|
358
|
-
"workflow.state.doc.ref":
|
|
359
|
-
"workflow.state.doc.refs":
|
|
360
|
-
"workflow.state.release.ref":
|
|
361
|
-
"workflow.state.query":
|
|
370
|
+
"workflow.state.doc.ref": v.union([v.null(), GdrShape]),
|
|
371
|
+
"workflow.state.doc.refs": v.array(GdrShape),
|
|
372
|
+
"workflow.state.release.ref": v.union([v.null(), ReleaseRefShape]),
|
|
373
|
+
"workflow.state.query": v.any(),
|
|
362
374
|
"workflow.state.value.string": NullableString,
|
|
363
375
|
"workflow.state.value.url": NullableUrl,
|
|
364
376
|
"workflow.state.value.number": NullableNumber,
|
|
365
377
|
"workflow.state.value.boolean": NullableBoolean,
|
|
366
378
|
"workflow.state.value.dateTime": NullableDateTime,
|
|
367
|
-
"workflow.state.value.actor":
|
|
368
|
-
"workflow.state.checklist":
|
|
369
|
-
"workflow.state.notes":
|
|
370
|
-
"workflow.state.assignees":
|
|
379
|
+
"workflow.state.value.actor": v.union([v.null(), ActorShape]),
|
|
380
|
+
"workflow.state.checklist": v.array(ChecklistItemShape),
|
|
381
|
+
"workflow.state.notes": v.array(NoteItemShape),
|
|
382
|
+
"workflow.state.assignees": v.array(AssigneeShape)
|
|
371
383
|
}, itemSchemas = {
|
|
372
384
|
"workflow.state.doc.refs": GdrShape,
|
|
373
385
|
"workflow.state.checklist": ChecklistItemShape,
|
|
@@ -386,12 +398,12 @@ function validateSlotValue(args) {
|
|
|
386
398
|
issues: [`unknown slot type ${args.slotType}`],
|
|
387
399
|
mode: "value"
|
|
388
400
|
});
|
|
389
|
-
const result =
|
|
401
|
+
const result = v.safeParse(schema, args.value);
|
|
390
402
|
if (!result.success)
|
|
391
403
|
throw new SlotValueShapeError({
|
|
392
404
|
slotType: args.slotType,
|
|
393
405
|
slotId: args.slotId,
|
|
394
|
-
issues: formatIssues(result.
|
|
406
|
+
issues: formatIssues(result.issues),
|
|
395
407
|
mode: "value"
|
|
396
408
|
});
|
|
397
409
|
}
|
|
@@ -403,17 +415,20 @@ function validateSlotAppendItem(args) {
|
|
|
403
415
|
issues: [`slot type ${args.slotType} does not support append`],
|
|
404
416
|
mode: "item"
|
|
405
417
|
});
|
|
406
|
-
const
|
|
418
|
+
const schema = itemSchemas[args.slotType], result = v.safeParse(schema, args.item);
|
|
407
419
|
if (!result.success)
|
|
408
420
|
throw new SlotValueShapeError({
|
|
409
421
|
slotType: args.slotType,
|
|
410
422
|
slotId: args.slotId,
|
|
411
|
-
issues: formatIssues(result.
|
|
423
|
+
issues: formatIssues(result.issues),
|
|
412
424
|
mode: "item"
|
|
413
425
|
});
|
|
414
426
|
}
|
|
415
427
|
function formatIssues(issues) {
|
|
416
|
-
return issues.map((i) =>
|
|
428
|
+
return issues.map((i) => {
|
|
429
|
+
const keys = i.path?.map((p) => p.key) ?? [];
|
|
430
|
+
return `${keys.length > 0 ? `at ${keys.join(".")}: ` : ""}${i.message}`;
|
|
431
|
+
});
|
|
417
432
|
}
|
|
418
433
|
function derivePerspectiveFromState(slots) {
|
|
419
434
|
if (slots !== void 0) {
|
|
@@ -458,7 +473,7 @@ async function resolveQueryValue(slot, source, ctx, client, defaultValue) {
|
|
|
458
473
|
state: stateMapFromResolved(ctx.resolvedState ?? []),
|
|
459
474
|
stage: ctx.stageId ?? null,
|
|
460
475
|
task: ctx.taskId ?? null,
|
|
461
|
-
now:
|
|
476
|
+
now: ctx.now,
|
|
462
477
|
engineTags: ctx.tags ?? []
|
|
463
478
|
});
|
|
464
479
|
try {
|
|
@@ -475,7 +490,7 @@ async function resolveSlotValue(slot, initialState, ctx, defaultValue) {
|
|
|
475
490
|
const source = slot.source;
|
|
476
491
|
return source.kind === "init" ? resolveInitValue(slot, initialState, defaultValue) : source.kind === "literal" ? source.value ?? defaultValue : source.kind === "stateRead" ? resolveStateReadValue(source, ctx, defaultValue) : source.kind === "query" && ctx.client !== void 0 ? resolveQueryValue(slot, source, ctx, ctx.client, defaultValue) : defaultValue;
|
|
477
492
|
}
|
|
478
|
-
function buildResolvedSlot(slot, value, _key) {
|
|
493
|
+
function buildResolvedSlot(slot, value, _key, now) {
|
|
479
494
|
const titleProp = slot.title !== void 0 ? { title: slot.title } : {}, descriptionProp = slot.description !== void 0 ? { description: slot.description } : {};
|
|
480
495
|
return slot.type === "workflow.state.query" ? {
|
|
481
496
|
_key,
|
|
@@ -484,7 +499,7 @@ function buildResolvedSlot(slot, value, _key) {
|
|
|
484
499
|
...titleProp,
|
|
485
500
|
...descriptionProp,
|
|
486
501
|
value,
|
|
487
|
-
resolvedAt:
|
|
502
|
+
resolvedAt: now
|
|
488
503
|
} : {
|
|
489
504
|
_key,
|
|
490
505
|
_type: slot.type,
|
|
@@ -496,7 +511,7 @@ function buildResolvedSlot(slot, value, _key) {
|
|
|
496
511
|
}
|
|
497
512
|
async function resolveOneSlot(slot, initialState, ctx, randomKey2) {
|
|
498
513
|
const defaultValue = defaultSlotValue(slot.type), value = await resolveSlotValue(slot, initialState, ctx, defaultValue);
|
|
499
|
-
return validateSlotValue({ slotType: slot.type, slotId: slot.id, value }), buildResolvedSlot(slot, value, randomKey2());
|
|
514
|
+
return validateSlotValue({ slotType: slot.type, slotId: slot.id, value }), buildResolvedSlot(slot, value, randomKey2(), ctx.now);
|
|
500
515
|
}
|
|
501
516
|
function stateMapFromResolved(slots) {
|
|
502
517
|
const out = {};
|
|
@@ -510,10 +525,10 @@ function assertInitValueShape(slot, value) {
|
|
|
510
525
|
}
|
|
511
526
|
if (slot.type === "workflow.state.release.ref") {
|
|
512
527
|
assertGdrShape(value, `state slot "${slot.id}" (workflow.state.release.ref)`);
|
|
513
|
-
const
|
|
514
|
-
if (typeof
|
|
528
|
+
const v2 = value;
|
|
529
|
+
if (typeof v2.releaseName != "string" || v2.releaseName.length === 0)
|
|
515
530
|
throw new Error(
|
|
516
|
-
`Invalid init value for state slot "${slot.id}" (workflow.state.release.ref): \`releaseName\` must be a non-empty string. Got ${JSON.stringify(
|
|
531
|
+
`Invalid init value for state slot "${slot.id}" (workflow.state.release.ref): \`releaseName\` must be a non-empty string. Got ${JSON.stringify(v2.releaseName)}.`
|
|
517
532
|
);
|
|
518
533
|
return;
|
|
519
534
|
}
|
|
@@ -531,18 +546,18 @@ function assertGdrShape(value, context) {
|
|
|
531
546
|
throw new Error(
|
|
532
547
|
`Invalid GDR for ${context}: expected { id: "<scheme>:...", type: "<schema>" }, got ${typeof value}.`
|
|
533
548
|
);
|
|
534
|
-
const
|
|
535
|
-
if (typeof
|
|
549
|
+
const v2 = value;
|
|
550
|
+
if (typeof v2.id != "string" || !isGdrUri(v2.id))
|
|
536
551
|
throw new Error(
|
|
537
|
-
`Invalid GDR for ${context}: \`id\` must be a GDR URI ("<scheme>:<...id-parts>" with scheme dataset|canvas|media-library|dashboard). Got ${JSON.stringify(
|
|
552
|
+
`Invalid GDR for ${context}: \`id\` must be a GDR URI ("<scheme>:<...id-parts>" with scheme dataset|canvas|media-library|dashboard). Got ${JSON.stringify(v2.id)}. Construct via \`gdrFromResource\` / \`refDataset\` / \`refCanvas\` etc. \u2014 bare document ids are not accepted.`
|
|
538
553
|
);
|
|
539
|
-
if (typeof
|
|
554
|
+
if (typeof v2.type != "string" || v2.type.length === 0)
|
|
540
555
|
throw new Error(
|
|
541
|
-
`Invalid GDR for ${context}: \`type\` (schema name) must be a non-empty string. Got ${JSON.stringify(
|
|
556
|
+
`Invalid GDR for ${context}: \`type\` (schema name) must be a non-empty string. Got ${JSON.stringify(v2.type)}.`
|
|
542
557
|
);
|
|
543
558
|
}
|
|
544
559
|
function normalizeQueryResult(slotType, raw, workflowResource) {
|
|
545
|
-
return raw == null ? raw : slotType === "workflow.state.doc.ref" ? coerceToGdr(raw, workflowResource) : slotType === "workflow.state.doc.refs" ? Array.isArray(raw) ? raw.map((item) => coerceToGdr(item, workflowResource)).filter((
|
|
560
|
+
return raw == null ? raw : slotType === "workflow.state.doc.ref" ? coerceToGdr(raw, workflowResource) : slotType === "workflow.state.doc.refs" ? Array.isArray(raw) ? raw.map((item) => coerceToGdr(item, workflowResource)).filter((v2) => v2 !== null) : [] : raw;
|
|
546
561
|
}
|
|
547
562
|
function toGdrUri(docId, workflowResource) {
|
|
548
563
|
return isGdrUri(docId) ? docId : gdrFromResource(workflowResource, docId);
|
|
@@ -567,22 +582,24 @@ async function loadContext(client, instanceId, options) {
|
|
|
567
582
|
const instance = await client.getDocument(instanceId);
|
|
568
583
|
if (!instance)
|
|
569
584
|
throw new Error(`Workflow instance ${instanceId} not found`);
|
|
570
|
-
const definition = JSON.parse(instance.definitionSnapshot), clientForGdr = options?.clientForGdr ?? (() => client), snapshot = await hydrateSnapshot({ client, clientForGdr, instance, definition });
|
|
571
|
-
return { client, clientForGdr, instance, definition, snapshot };
|
|
585
|
+
const definition = JSON.parse(instance.definitionSnapshot), clientForGdr = options?.clientForGdr ?? (() => client), clock = options?.clock ?? wallClock, snapshot = await hydrateSnapshot({ client, clientForGdr, instance, definition });
|
|
586
|
+
return { client, clientForGdr, clock, now: clock(), instance, definition, snapshot };
|
|
572
587
|
}
|
|
573
588
|
function ctxEvaluateFilter(ctx, filter) {
|
|
574
589
|
return evaluateFilter({
|
|
575
590
|
filter,
|
|
576
591
|
definition: ctx.definition,
|
|
577
592
|
snapshot: ctx.snapshot,
|
|
578
|
-
params: buildParams(ctx.instance)
|
|
593
|
+
params: buildParams(ctx.instance, ctx.now)
|
|
579
594
|
});
|
|
580
595
|
}
|
|
581
596
|
async function buildEngineContext(args) {
|
|
582
|
-
const { client, clientForGdr, instance, definition } = args;
|
|
597
|
+
const { client, clientForGdr, instance, definition } = args, clock = args.clock ?? wallClock;
|
|
583
598
|
return {
|
|
584
599
|
client,
|
|
585
600
|
clientForGdr,
|
|
601
|
+
clock,
|
|
602
|
+
now: clock(),
|
|
586
603
|
instance,
|
|
587
604
|
definition,
|
|
588
605
|
snapshot: await hydrateSnapshot({ client, clientForGdr, instance, definition })
|
|
@@ -595,12 +612,13 @@ function findStage(definition, stageId) {
|
|
|
595
612
|
return stage;
|
|
596
613
|
}
|
|
597
614
|
async function resolveStageStateSlots(args) {
|
|
598
|
-
const { client, instance, stage, initialState } = args;
|
|
615
|
+
const { client, instance, stage, now, initialState } = args;
|
|
599
616
|
return resolveDeclaredState({
|
|
600
617
|
slotDefs: stage.state,
|
|
601
618
|
initialState: initialState ?? [],
|
|
602
619
|
ctx: {
|
|
603
620
|
client,
|
|
621
|
+
now,
|
|
604
622
|
selfId: instance._id,
|
|
605
623
|
tags: instance.tags ?? [],
|
|
606
624
|
stageId: stage.id,
|
|
@@ -615,12 +633,13 @@ async function resolveStageStateSlots(args) {
|
|
|
615
633
|
});
|
|
616
634
|
}
|
|
617
635
|
async function resolveTaskStateSlots(args) {
|
|
618
|
-
const { client, instance, stage, task } = args;
|
|
636
|
+
const { client, instance, stage, task, now } = args;
|
|
619
637
|
return resolveDeclaredState({
|
|
620
638
|
slotDefs: task.state,
|
|
621
639
|
initialState: [],
|
|
622
640
|
ctx: {
|
|
623
641
|
client,
|
|
642
|
+
now,
|
|
624
643
|
selfId: instance._id,
|
|
625
644
|
tags: instance.tags ?? [],
|
|
626
645
|
stageId: stage.id,
|
|
@@ -648,16 +667,24 @@ function slotValue$1(slot) {
|
|
|
648
667
|
if (slot != null && typeof slot == "object")
|
|
649
668
|
return slot.value;
|
|
650
669
|
}
|
|
651
|
-
function
|
|
670
|
+
function resolveStaticSource(src, ctx) {
|
|
652
671
|
switch (src.source) {
|
|
653
672
|
case "literal":
|
|
654
|
-
return src.value;
|
|
673
|
+
return { handled: !0, value: src.value };
|
|
655
674
|
case "param":
|
|
656
|
-
return ctx.params?.[src.paramId];
|
|
675
|
+
return { handled: !0, value: ctx.params?.[src.paramId] };
|
|
657
676
|
case "actor":
|
|
658
|
-
return ctx.actor;
|
|
677
|
+
return { handled: !0, value: ctx.actor };
|
|
659
678
|
case "now":
|
|
660
|
-
return
|
|
679
|
+
return { handled: !0, value: ctx.now };
|
|
680
|
+
default:
|
|
681
|
+
return { handled: !1 };
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
function resolveSource(src, ctx) {
|
|
685
|
+
const staticValue = resolveStaticSource(src, ctx);
|
|
686
|
+
if (staticValue.handled) return staticValue.value;
|
|
687
|
+
switch (src.source) {
|
|
661
688
|
case "self":
|
|
662
689
|
return ctx.instance._id;
|
|
663
690
|
case "stageId":
|
|
@@ -668,6 +695,11 @@ function resolveSource(src, ctx) {
|
|
|
668
695
|
return resolveEffectOutput(src, ctx);
|
|
669
696
|
case "object":
|
|
670
697
|
return resolveObject(src, ctx);
|
|
698
|
+
case "row":
|
|
699
|
+
case "parentState":
|
|
700
|
+
return;
|
|
701
|
+
default:
|
|
702
|
+
return;
|
|
671
703
|
}
|
|
672
704
|
}
|
|
673
705
|
function resolveStateRead(src, ctx) {
|
|
@@ -687,10 +719,11 @@ function resolveObject(src, ctx) {
|
|
|
687
719
|
return out;
|
|
688
720
|
}
|
|
689
721
|
async function resolveBindings(args) {
|
|
690
|
-
const { bindings, staticInput, instance, params, actor } = args, resolved = {};
|
|
722
|
+
const { bindings, staticInput, instance, now, params, actor } = args, resolved = {};
|
|
691
723
|
if (bindings) {
|
|
692
724
|
const ctx = {
|
|
693
725
|
instance,
|
|
726
|
+
now,
|
|
694
727
|
...params !== void 0 ? { params } : {},
|
|
695
728
|
...actor !== void 0 ? { actor } : {}
|
|
696
729
|
};
|
|
@@ -737,16 +770,29 @@ function buildInstanceBase(args) {
|
|
|
737
770
|
lastChangedAt: now
|
|
738
771
|
};
|
|
739
772
|
}
|
|
773
|
+
const GUARD_DOC_TYPE = "temp.system.guard";
|
|
774
|
+
class MutationGuardDeniedError extends Error {
|
|
775
|
+
denied;
|
|
776
|
+
documentId;
|
|
777
|
+
action;
|
|
778
|
+
constructor(args) {
|
|
779
|
+
const ids = args.denied.map((d) => d.guardId).join(", ");
|
|
780
|
+
super(`Mutation on "${args.documentId}" (${args.action}) denied by guard(s) [${ids}]`), this.name = "MutationGuardDeniedError", this.denied = args.denied, this.documentId = args.documentId, this.action = args.action;
|
|
781
|
+
}
|
|
782
|
+
}
|
|
740
783
|
function lakeGuardId(args) {
|
|
741
|
-
return
|
|
784
|
+
return `${GUARD_DOC_TYPE}.${args.instanceDocId}.${args.stageId}.${args.index}`;
|
|
742
785
|
}
|
|
743
786
|
function compileGuard(args) {
|
|
744
787
|
return {
|
|
745
788
|
_id: args.id,
|
|
746
|
-
_type:
|
|
789
|
+
_type: GUARD_DOC_TYPE,
|
|
747
790
|
resourceType: args.resourceType,
|
|
748
791
|
resourceId: args.resourceId,
|
|
749
792
|
owner: args.owner,
|
|
793
|
+
sourceInstanceId: args.sourceInstanceId,
|
|
794
|
+
sourceDefinitionId: args.sourceDefinitionId,
|
|
795
|
+
sourceStageId: args.sourceStageId,
|
|
750
796
|
...args.name !== void 0 ? { name: args.name } : {},
|
|
751
797
|
...args.description !== void 0 ? { description: args.description } : {},
|
|
752
798
|
match: args.match,
|
|
@@ -802,9 +848,9 @@ function resolveIdRefTarget(src, ctx) {
|
|
|
802
848
|
if (typeof value == "string" && isGdrUri(value))
|
|
803
849
|
return { parsed: parseGdr(value) };
|
|
804
850
|
if (value && typeof value == "object") {
|
|
805
|
-
const
|
|
806
|
-
if (typeof
|
|
807
|
-
return typeof
|
|
851
|
+
const v2 = value;
|
|
852
|
+
if (typeof v2.id == "string" && isGdrUri(v2.id))
|
|
853
|
+
return typeof v2.type == "string" ? { parsed: parseGdr(v2.id), type: v2.type } : { parsed: parseGdr(v2.id) };
|
|
808
854
|
}
|
|
809
855
|
return null;
|
|
810
856
|
}
|
|
@@ -843,8 +889,8 @@ function resolveMetadata(metadata, ctx) {
|
|
|
843
889
|
return out;
|
|
844
890
|
}
|
|
845
891
|
const GUARD_OWNER = "robot:workflow-engine", GUARD_LIFTED_PREDICATE = "true";
|
|
846
|
-
function resolveGuard(guard, index, instance, stageId) {
|
|
847
|
-
const ctx = { instance, stageId }, targets = resolveIdRefTargets(guard.match.idRefs, ctx);
|
|
892
|
+
function resolveGuard(guard, index, instance, stageId, now) {
|
|
893
|
+
const ctx = { instance, stageId, now }, targets = resolveIdRefTargets(guard.match.idRefs, ctx);
|
|
848
894
|
if (targets === null) return null;
|
|
849
895
|
const resource = assertSingleResource(targets), types = resolveMatchTypes(targets, guard.match.types);
|
|
850
896
|
return { doc: compileGuard({
|
|
@@ -852,6 +898,9 @@ function resolveGuard(guard, index, instance, stageId) {
|
|
|
852
898
|
resourceType: resource.type,
|
|
853
899
|
resourceId: resource.id,
|
|
854
900
|
owner: GUARD_OWNER,
|
|
901
|
+
sourceInstanceId: instance._id,
|
|
902
|
+
sourceDefinitionId: instance.workflowId,
|
|
903
|
+
sourceStageId: stageId,
|
|
855
904
|
...guard.name !== void 0 ? { name: guard.name } : {},
|
|
856
905
|
...guard.description !== void 0 ? { description: guard.description } : {},
|
|
857
906
|
match: {
|
|
@@ -865,47 +914,108 @@ function resolveGuard(guard, index, instance, stageId) {
|
|
|
865
914
|
}), routeGdr: targets[0].parsed };
|
|
866
915
|
}
|
|
867
916
|
async function upsertGuard(client, doc) {
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
metadata: doc.metadata
|
|
875
|
-
};
|
|
876
|
-
doc.name !== void 0 && (set.name = doc.name), doc.description !== void 0 && (set.description = doc.description), existing ? await client.patch(doc._id).set(set).commit() : await client.create(doc);
|
|
917
|
+
if (!await client.getDocument(doc._id)) {
|
|
918
|
+
await client.create(doc);
|
|
919
|
+
return;
|
|
920
|
+
}
|
|
921
|
+
const { _id, _type, _rev, _createdAt, _updatedAt, ...body } = doc;
|
|
922
|
+
await client.patch(doc._id).set(body).commit();
|
|
877
923
|
}
|
|
878
924
|
function resolvedStageGuards(args) {
|
|
879
925
|
const stage = args.definition.stages.find((s) => s.id === args.stageId), out = [];
|
|
880
926
|
for (const [index, guard] of (stage?.guards ?? []).entries()) {
|
|
881
|
-
const resolved = resolveGuard(guard, index, args.instance, args.stageId);
|
|
927
|
+
const resolved = resolveGuard(guard, index, args.instance, args.stageId, args.now);
|
|
882
928
|
resolved !== null && out.push({ client: args.clientForGdr(resolved.routeGdr), doc: resolved.doc });
|
|
883
929
|
}
|
|
884
930
|
return out;
|
|
885
931
|
}
|
|
932
|
+
async function committedStageId(args) {
|
|
933
|
+
return (await args.client.getDocument(args.instance._id))?.currentStageId;
|
|
934
|
+
}
|
|
886
935
|
async function deployStageGuards(args) {
|
|
887
|
-
|
|
888
|
-
|
|
936
|
+
if (await committedStageId(args) === args.stageId)
|
|
937
|
+
for (const { client, doc } of resolvedStageGuards(args))
|
|
938
|
+
await upsertGuard(client, doc);
|
|
889
939
|
}
|
|
890
940
|
async function retractStageGuards(args) {
|
|
891
|
-
|
|
892
|
-
|
|
941
|
+
const live = await committedStageId(args);
|
|
942
|
+
if (!(live === void 0 || live === args.stageId))
|
|
943
|
+
for (const { client, doc } of resolvedStageGuards(args))
|
|
944
|
+
await client.getDocument(doc._id) && await client.patch(doc._id).set({ predicate: GUARD_LIFTED_PREDICATE }).commit();
|
|
893
945
|
}
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
946
|
+
class ActionParamsInvalidError extends Error {
|
|
947
|
+
action;
|
|
948
|
+
taskId;
|
|
949
|
+
issues;
|
|
950
|
+
constructor(args) {
|
|
951
|
+
const lines = args.issues.map((i) => ` - ${i.paramId}: ${i.reason}`).join(`
|
|
952
|
+
`);
|
|
953
|
+
super(`Action "${args.action}" on task "${args.taskId}" rejected: invalid params
|
|
954
|
+
${lines}`), this.name = "ActionParamsInvalidError", this.action = args.action, this.taskId = args.taskId, this.issues = args.issues;
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
class WorkflowStateDivergedError extends Error {
|
|
958
|
+
instanceId;
|
|
959
|
+
/** The guard-deploy failure that triggered the (attempted) rollback. */
|
|
960
|
+
guardError;
|
|
961
|
+
/** The rollback failure, when rollback was attempted and itself failed. */
|
|
962
|
+
rollbackError;
|
|
963
|
+
constructor(args) {
|
|
964
|
+
super(
|
|
965
|
+
`Workflow "${args.instanceId}" state committed but its guards could not be reconciled: ${args.reason}.`,
|
|
966
|
+
{ cause: args.guardError }
|
|
967
|
+
), this.name = "WorkflowStateDivergedError", this.instanceId = args.instanceId, this.guardError = args.guardError, args.rollbackError !== void 0 && (this.rollbackError = args.rollbackError);
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
const CONCURRENT_FIRE_ACTION_MAX_ATTEMPTS = 3;
|
|
971
|
+
class ConcurrentFireActionError extends Error {
|
|
972
|
+
instanceId;
|
|
973
|
+
taskId;
|
|
974
|
+
action;
|
|
975
|
+
attempts;
|
|
976
|
+
constructor(args) {
|
|
977
|
+
super(
|
|
978
|
+
`Action "${args.action}" on task "${args.taskId}" (instance ${args.instanceId}) lost the optimistic-locking race ${args.attempts} attempts running \u2014 a concurrent writer kept committing first. Re-read and retry, or investigate a write storm on this instance.`
|
|
979
|
+
), this.name = "ConcurrentFireActionError", this.instanceId = args.instanceId, this.taskId = args.taskId, this.action = args.action, this.attempts = args.attempts;
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
function isRevisionConflict(error) {
|
|
983
|
+
if (typeof error != "object" || error === null) return !1;
|
|
984
|
+
const { statusCode, message } = error;
|
|
985
|
+
return statusCode === 409 ? !0 : typeof message == "string" && message.includes("ifRevisionId check failed");
|
|
986
|
+
}
|
|
987
|
+
class CascadeLimitError extends Error {
|
|
988
|
+
instanceId;
|
|
989
|
+
limit;
|
|
990
|
+
constructor(args) {
|
|
991
|
+
super(
|
|
992
|
+
`Cascade did not stabilise after ${args.limit} auto-transitions on ${args.instanceId} \u2014 likely a runaway loop (transition guards that stay mutually satisfied). Check that each transition's filter eventually goes false.`
|
|
993
|
+
), this.name = "CascadeLimitError", this.instanceId = args.instanceId, this.limit = args.limit;
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
async function deployOrRollback(args) {
|
|
997
|
+
try {
|
|
998
|
+
await args.deploy();
|
|
999
|
+
} catch (guardError) {
|
|
1000
|
+
if (!args.reversible)
|
|
1001
|
+
throw new WorkflowStateDivergedError({
|
|
1002
|
+
instanceId: args.instanceId,
|
|
1003
|
+
guardError,
|
|
1004
|
+
reason: "the move created child instances a parent-only rollback cannot undo"
|
|
1005
|
+
});
|
|
1006
|
+
try {
|
|
1007
|
+
let patch = args.client.patch(args.instanceId).set(args.restore);
|
|
1008
|
+
args.unset && args.unset.length > 0 && (patch = patch.unset(args.unset)), args.committedRev !== void 0 && (patch = patch.ifRevisionId(args.committedRev)), await patch.commit();
|
|
1009
|
+
} catch (rollbackError) {
|
|
1010
|
+
throw new WorkflowStateDivergedError({
|
|
1011
|
+
instanceId: args.instanceId,
|
|
1012
|
+
guardError,
|
|
1013
|
+
rollbackError,
|
|
1014
|
+
reason: "rollback of the committed move failed"
|
|
1015
|
+
});
|
|
1016
|
+
}
|
|
1017
|
+
throw guardError;
|
|
1018
|
+
}
|
|
909
1019
|
}
|
|
910
1020
|
async function discoverItems(args) {
|
|
911
1021
|
const { client, groq, params, perspective } = args, fetchOptions = perspective !== void 0 ? { perspective } : void 0;
|
|
@@ -922,31 +1032,29 @@ function clientForDiscovery(defaultClient, clientForGdr, subjectGdrUri) {
|
|
|
922
1032
|
const MAX_SPAWN_DEPTH = 6;
|
|
923
1033
|
async function spawnChildren(ctx, mutation, task, spawn, actor) {
|
|
924
1034
|
if (ctx.instance.ancestors.length + 1 > MAX_SPAWN_DEPTH) {
|
|
925
|
-
const chain = [
|
|
926
|
-
...ctx.instance.ancestors.map((a) => a.id),
|
|
927
|
-
gdrFromResource(ctx.instance.workflowResource, ctx.instance._id)
|
|
928
|
-
].join(" \u2192 ");
|
|
1035
|
+
const chain = [...ctx.instance.ancestors.map((a) => a.id), selfGdr(ctx.instance)].join(" \u2192 ");
|
|
929
1036
|
throw new Error(
|
|
930
1037
|
`Spawn depth limit (${MAX_SPAWN_DEPTH}) exceeded on task "${task.id}" of ${ctx.instance._id}. Ancestor chain: ${chain}`
|
|
931
1038
|
);
|
|
932
1039
|
}
|
|
933
|
-
const rows = await resolveSpawnRows(ctx, spawn), refs = [];
|
|
1040
|
+
const now = ctx.now, rows = await resolveSpawnRows(ctx, spawn), refs = [];
|
|
934
1041
|
for (const row of rows) {
|
|
935
|
-
const projected = projectSpawnRow(ctx.instance, spawn, row), { ref, body } = await prepareChildInstance({
|
|
1042
|
+
const projected = projectSpawnRow(ctx.instance, spawn, row, now), { ref, body } = await prepareChildInstance({
|
|
936
1043
|
client: ctx.client,
|
|
937
1044
|
parent: ctx.instance,
|
|
938
1045
|
task,
|
|
939
1046
|
spawn,
|
|
940
1047
|
initialState: projected.initialState,
|
|
941
1048
|
effectsContext: projected.effectsContext,
|
|
942
|
-
actor
|
|
1049
|
+
actor,
|
|
1050
|
+
now
|
|
943
1051
|
});
|
|
944
1052
|
refs.push(ref), mutation.pendingCreates.push(body);
|
|
945
1053
|
}
|
|
946
1054
|
return refs;
|
|
947
1055
|
}
|
|
948
1056
|
async function resolveSpawnRows(ctx, spawn) {
|
|
949
|
-
const params = buildParams(ctx.instance), groq = spawn.forEach.groq;
|
|
1057
|
+
const params = buildParams(ctx.instance, ctx.now), groq = spawn.forEach.groq;
|
|
950
1058
|
let value;
|
|
951
1059
|
if (groq.includes("*")) {
|
|
952
1060
|
const routingUri = firstDocRefGdrUri(ctx.instance.state), client = clientForDiscovery(ctx.client, ctx.clientForGdr, routingUri);
|
|
@@ -968,7 +1076,7 @@ function firstDocRefGdrUri(slots) {
|
|
|
968
1076
|
if (s._type === "workflow.state.doc.ref" && s.value !== null) return s.value.id;
|
|
969
1077
|
}
|
|
970
1078
|
}
|
|
971
|
-
function resolveSpawnSource(source, parent, row) {
|
|
1079
|
+
function resolveSpawnSource(source, parent, row, now) {
|
|
972
1080
|
switch (source.source) {
|
|
973
1081
|
case "literal":
|
|
974
1082
|
return source.value;
|
|
@@ -979,15 +1087,15 @@ function resolveSpawnSource(source, parent, row) {
|
|
|
979
1087
|
return slot === void 0 ? null : source.path !== void 0 ? getPath(slot, source.path) : slot;
|
|
980
1088
|
}
|
|
981
1089
|
case "self":
|
|
982
|
-
return
|
|
1090
|
+
return selfGdr(parent);
|
|
983
1091
|
case "stageId":
|
|
984
1092
|
return parent.currentStageId;
|
|
985
1093
|
case "now":
|
|
986
|
-
return
|
|
1094
|
+
return now;
|
|
987
1095
|
case "object": {
|
|
988
1096
|
const out = {};
|
|
989
|
-
for (const [k,
|
|
990
|
-
out[k] = resolveSpawnSource(
|
|
1097
|
+
for (const [k, v2] of Object.entries(source.fields))
|
|
1098
|
+
out[k] = resolveSpawnSource(v2, parent, row, now);
|
|
991
1099
|
return out;
|
|
992
1100
|
}
|
|
993
1101
|
case "actor":
|
|
@@ -997,10 +1105,10 @@ function resolveSpawnSource(source, parent, row) {
|
|
|
997
1105
|
return null;
|
|
998
1106
|
}
|
|
999
1107
|
}
|
|
1000
|
-
function projectSpawnRow(parent, spawn, row) {
|
|
1108
|
+
function projectSpawnRow(parent, spawn, row, now) {
|
|
1001
1109
|
const initialState = [];
|
|
1002
1110
|
for (const entry of spawn.forEach.as.initialState ?? []) {
|
|
1003
|
-
const raw = resolveSpawnSource(entry.value, parent, row), value = canonicaliseSpawnValue(entry.type, raw, parent.workflowResource);
|
|
1111
|
+
const raw = resolveSpawnSource(entry.value, parent, row, now), value = canonicaliseSpawnValue(entry.type, raw, parent.workflowResource);
|
|
1004
1112
|
initialState.push({
|
|
1005
1113
|
type: entry.type,
|
|
1006
1114
|
id: entry.id,
|
|
@@ -1009,13 +1117,13 @@ function projectSpawnRow(parent, spawn, row) {
|
|
|
1009
1117
|
}
|
|
1010
1118
|
const effectsContext = {};
|
|
1011
1119
|
for (const [k, src] of Object.entries(spawn.forEach.as.effectsContext ?? {})) {
|
|
1012
|
-
const
|
|
1013
|
-
|
|
1120
|
+
const v2 = resolveSpawnSource(src, parent, row, now);
|
|
1121
|
+
v2 != null && (typeof v2 == "string" || typeof v2 == "number" || typeof v2 == "boolean" || typeof v2 == "object" && "id" in v2 && "type" in v2) && (effectsContext[k] = v2);
|
|
1014
1122
|
}
|
|
1015
1123
|
return { initialState, effectsContext };
|
|
1016
1124
|
}
|
|
1017
1125
|
function canonicaliseSpawnValue(slotType, value, workflowResource) {
|
|
1018
|
-
return slotType === "workflow.state.doc.ref" ? coerceSpawnGdr(value, workflowResource) : slotType === "workflow.state.doc.refs" && Array.isArray(value) ? value.map((
|
|
1126
|
+
return slotType === "workflow.state.doc.ref" ? coerceSpawnGdr(value, workflowResource) : slotType === "workflow.state.doc.refs" && Array.isArray(value) ? value.map((v2) => coerceSpawnGdr(v2, workflowResource)).filter((v2) => v2 !== null) : value;
|
|
1019
1127
|
}
|
|
1020
1128
|
function coerceSpawnGdr(raw, workflowResource) {
|
|
1021
1129
|
const toUri = (id) => isGdrUri(id) ? id : gdrFromResource(workflowResource, id);
|
|
@@ -1038,7 +1146,7 @@ async function resolveLogicalDefinition(client, ref, tags) {
|
|
|
1038
1146
|
return await client.fetch(groq, params) ?? null;
|
|
1039
1147
|
}
|
|
1040
1148
|
async function prepareChildInstance(args) {
|
|
1041
|
-
const { client, parent, task, spawn, initialState, effectsContext, actor } = args, definition = await resolveLogicalDefinition(client, spawn.definitionRef, parent.tags ?? []);
|
|
1149
|
+
const { client, parent, task, spawn, initialState, effectsContext, actor, now } = args, definition = await resolveLogicalDefinition(client, spawn.definitionRef, parent.tags ?? []);
|
|
1042
1150
|
if (definition === null) {
|
|
1043
1151
|
const versionLabel = spawn.definitionRef.version === void 0 || spawn.definitionRef.version === "latest" ? "latest" : `v${spawn.definitionRef.version}`;
|
|
1044
1152
|
throw new Error(
|
|
@@ -1048,7 +1156,7 @@ async function prepareChildInstance(args) {
|
|
|
1048
1156
|
const childTags = parent.tags ?? [], childPrefix = childTags[0] ?? "wf", workflowResource = parent.workflowResource, childDocId = `${childPrefix}.wf-instance.${randomKey()}`, childRef = { id: gdrFromResource(workflowResource, childDocId), type: "workflow.instance" }, ancestors = [
|
|
1049
1157
|
...parent.ancestors,
|
|
1050
1158
|
{
|
|
1051
|
-
id:
|
|
1159
|
+
id: selfGdr(parent),
|
|
1052
1160
|
type: "workflow.instance"
|
|
1053
1161
|
}
|
|
1054
1162
|
], inheritedPerspective = parent.perspective, childState = await resolveDeclaredState({
|
|
@@ -1056,6 +1164,7 @@ async function prepareChildInstance(args) {
|
|
|
1056
1164
|
initialState,
|
|
1057
1165
|
ctx: {
|
|
1058
1166
|
client,
|
|
1167
|
+
now,
|
|
1059
1168
|
selfId: childDocId,
|
|
1060
1169
|
tags: childTags,
|
|
1061
1170
|
workflowResource,
|
|
@@ -1075,7 +1184,7 @@ async function prepareChildInstance(args) {
|
|
|
1075
1184
|
}
|
|
1076
1185
|
), base = buildInstanceBase({
|
|
1077
1186
|
id: childDocId,
|
|
1078
|
-
now
|
|
1187
|
+
now,
|
|
1079
1188
|
tags: childTags,
|
|
1080
1189
|
workflowResource,
|
|
1081
1190
|
workflowId: definition.workflowId,
|
|
@@ -1111,7 +1220,9 @@ async function checkSpawnCompletion(client, entry, spawn) {
|
|
|
1111
1220
|
}
|
|
1112
1221
|
}
|
|
1113
1222
|
async function invokeTask(args) {
|
|
1114
|
-
const { client, instanceId, taskId, options } = args, ctx = await loadContext(client, instanceId
|
|
1223
|
+
const { client, instanceId, taskId, options } = args, ctx = await loadContext(client, instanceId, {
|
|
1224
|
+
...options?.clock ? { clock: options.clock } : {}
|
|
1225
|
+
});
|
|
1115
1226
|
return commitInvoke(ctx, taskId, options?.actor);
|
|
1116
1227
|
}
|
|
1117
1228
|
async function commitInvoke(ctx, taskId, actor) {
|
|
@@ -1123,41 +1234,41 @@ async function commitInvoke(ctx, taskId, actor) {
|
|
|
1123
1234
|
const mutation = startMutation(ctx.instance), mutEntry = requireMutationTaskEntry(mutation, taskId);
|
|
1124
1235
|
return await activateTask(ctx, mutation, task, mutEntry, actor), await persist(ctx, mutation), { invoked: !0, taskId };
|
|
1125
1236
|
}
|
|
1126
|
-
async function autoResolveOnActivate(ctx, mutation, task, entry) {
|
|
1237
|
+
async function autoResolveOnActivate(ctx, mutation, task, entry, now) {
|
|
1127
1238
|
const checks = [
|
|
1128
1239
|
{ filter: task.failWhen, outcome: "failed", systemId: "engine.failWhen" },
|
|
1129
1240
|
{ filter: task.completeWhen, outcome: "done", systemId: "engine.completeWhen" }
|
|
1130
1241
|
];
|
|
1131
|
-
for (const { filter, outcome, systemId } of checks)
|
|
1132
|
-
if (filter
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
), !0;
|
|
1144
|
-
}
|
|
1242
|
+
for (const { filter, outcome, systemId } of checks)
|
|
1243
|
+
if (filter !== void 0 && await ctxEvaluateFilter(ctx, filter))
|
|
1244
|
+
return entry.status = outcome, entry.completedAt = now, entry.completedBy = systemId, mutation.history.push(
|
|
1245
|
+
taskStatusChangedEntry({
|
|
1246
|
+
stageId: mutation.currentStageId,
|
|
1247
|
+
taskId: task.id,
|
|
1248
|
+
from: "active",
|
|
1249
|
+
to: outcome,
|
|
1250
|
+
at: now,
|
|
1251
|
+
actor: { kind: "system", id: systemId }
|
|
1252
|
+
})
|
|
1253
|
+
), !0;
|
|
1145
1254
|
return !1;
|
|
1146
1255
|
}
|
|
1147
1256
|
async function activateTask(ctx, mutation, task, entry, actor) {
|
|
1148
|
-
|
|
1257
|
+
const now = ctx.now;
|
|
1258
|
+
if (entry.status = "active", entry.startedAt = now, task.state !== void 0 && task.state.length > 0) {
|
|
1149
1259
|
const stage = findStage(ctx.definition, mutation.currentStageId);
|
|
1150
1260
|
entry.state = await resolveTaskStateSlots({
|
|
1151
1261
|
client: ctx.client,
|
|
1152
1262
|
instance: ctx.instance,
|
|
1153
1263
|
stage,
|
|
1154
|
-
task
|
|
1264
|
+
task,
|
|
1265
|
+
now
|
|
1155
1266
|
});
|
|
1156
1267
|
}
|
|
1157
|
-
if (!await autoResolveOnActivate(ctx, mutation, task, entry) && (mutation.history.push({
|
|
1268
|
+
if (!await autoResolveOnActivate(ctx, mutation, task, entry, now) && (mutation.history.push({
|
|
1158
1269
|
_key: randomKey(),
|
|
1159
1270
|
_type: "workflow.history.taskActivated",
|
|
1160
|
-
at:
|
|
1271
|
+
at: now,
|
|
1161
1272
|
stageId: mutation.currentStageId,
|
|
1162
1273
|
taskId: task.id,
|
|
1163
1274
|
...actor !== void 0 ? { actor } : {}
|
|
@@ -1177,18 +1288,19 @@ async function activateTask(ctx, mutation, task, entry, actor) {
|
|
|
1177
1288
|
mutation.history.push({
|
|
1178
1289
|
_key: randomKey(),
|
|
1179
1290
|
_type: "workflow.history.spawned",
|
|
1180
|
-
at:
|
|
1291
|
+
at: now,
|
|
1181
1292
|
taskId: task.id,
|
|
1182
1293
|
instanceRef: ref
|
|
1183
1294
|
});
|
|
1184
1295
|
if (await checkSpawnCompletion(ctx.client, entry, task.spawns)) {
|
|
1185
1296
|
const previousStatus = entry.status;
|
|
1186
|
-
entry.status = "done", entry.completedAt =
|
|
1297
|
+
entry.status = "done", entry.completedAt = now, mutation.history.push(
|
|
1187
1298
|
taskStatusChangedEntry({
|
|
1188
1299
|
stageId: mutation.currentStageId,
|
|
1189
1300
|
taskId: task.id,
|
|
1190
1301
|
from: previousStatus,
|
|
1191
1302
|
to: "done",
|
|
1303
|
+
at: now,
|
|
1192
1304
|
...actor !== void 0 ? { actor } : {}
|
|
1193
1305
|
})
|
|
1194
1306
|
);
|
|
@@ -1205,7 +1317,7 @@ async function buildStageTasks(ctx, stage) {
|
|
|
1205
1317
|
status: inScope ? "pending" : "skipped",
|
|
1206
1318
|
...task.filter !== void 0 ? {
|
|
1207
1319
|
filterEvaluation: {
|
|
1208
|
-
at:
|
|
1320
|
+
at: ctx.now,
|
|
1209
1321
|
truthy: inScope
|
|
1210
1322
|
}
|
|
1211
1323
|
} : {}
|
|
@@ -1217,7 +1329,9 @@ function activatesOnStageEnter(task) {
|
|
|
1217
1329
|
return task.invokeOn === "stageEnter" || task.spawns !== void 0 || task.completeWhen !== void 0 || task.failWhen !== void 0;
|
|
1218
1330
|
}
|
|
1219
1331
|
async function setStage(args) {
|
|
1220
|
-
const { client, instanceId, targetStageId, reason, options } = args, ctx = await loadContext(client, instanceId
|
|
1332
|
+
const { client, instanceId, targetStageId, reason, options } = args, ctx = await loadContext(client, instanceId, {
|
|
1333
|
+
...options?.clock ? { clock: options.clock } : {}
|
|
1334
|
+
});
|
|
1221
1335
|
return commitSetStage(ctx, targetStageId, reason, options?.actor);
|
|
1222
1336
|
}
|
|
1223
1337
|
async function enterStage(ctx, mutation, nextStage, actor, at) {
|
|
@@ -1229,7 +1343,8 @@ async function enterStage(ctx, mutation, nextStage, actor, at) {
|
|
|
1229
1343
|
state: await resolveStageStateSlots({
|
|
1230
1344
|
client: ctx.client,
|
|
1231
1345
|
instance: ctx.instance,
|
|
1232
|
-
stage: nextStage
|
|
1346
|
+
stage: nextStage,
|
|
1347
|
+
now: at
|
|
1233
1348
|
}),
|
|
1234
1349
|
tasks: await buildStageTasks(ctx, nextStage)
|
|
1235
1350
|
};
|
|
@@ -1248,7 +1363,7 @@ async function enterStage(ctx, mutation, nextStage, actor, at) {
|
|
|
1248
1363
|
nextStage.kind === "terminal" && (mutation.completedAt = at);
|
|
1249
1364
|
}
|
|
1250
1365
|
async function beginStageMove(ctx, currentStage, actor) {
|
|
1251
|
-
const mutation = startMutation(ctx.instance), at =
|
|
1366
|
+
const mutation = startMutation(ctx.instance), at = ctx.now;
|
|
1252
1367
|
return await queueEffects(
|
|
1253
1368
|
ctx,
|
|
1254
1369
|
mutation,
|
|
@@ -1282,13 +1397,46 @@ async function commitSetStage(ctx, targetStageId, reason, actor) {
|
|
|
1282
1397
|
};
|
|
1283
1398
|
}
|
|
1284
1399
|
async function persistStageMove(ctx, mutation, exitedStageId, enteredStageId) {
|
|
1285
|
-
await
|
|
1400
|
+
await persistThenDeploy(
|
|
1401
|
+
ctx,
|
|
1402
|
+
mutation,
|
|
1403
|
+
() => deployStageGuards({
|
|
1404
|
+
client: ctx.client,
|
|
1405
|
+
clientForGdr: ctx.clientForGdr,
|
|
1406
|
+
instance: materializeInstance(ctx.instance, mutation),
|
|
1407
|
+
definition: ctx.definition,
|
|
1408
|
+
stageId: enteredStageId,
|
|
1409
|
+
now: ctx.now
|
|
1410
|
+
})
|
|
1411
|
+
), await retractStageGuards({
|
|
1286
1412
|
client: ctx.client,
|
|
1287
1413
|
clientForGdr: ctx.clientForGdr,
|
|
1288
1414
|
instance: ctx.instance,
|
|
1289
1415
|
definition: ctx.definition,
|
|
1290
|
-
exitedStageId,
|
|
1291
|
-
|
|
1416
|
+
stageId: exitedStageId,
|
|
1417
|
+
now: ctx.now
|
|
1418
|
+
});
|
|
1419
|
+
}
|
|
1420
|
+
async function persistThenDeploy(ctx, mutation, deploy) {
|
|
1421
|
+
const spawned = mutation.pendingCreates.length > 0, committed = await persist(ctx, mutation);
|
|
1422
|
+
await deployOrRollback({
|
|
1423
|
+
client: ctx.client,
|
|
1424
|
+
instanceId: ctx.instance._id,
|
|
1425
|
+
committedRev: committed._rev,
|
|
1426
|
+
restore: instanceStateFields(ctx.instance),
|
|
1427
|
+
unset: ctx.instance.completedAt === void 0 ? ["completedAt"] : [],
|
|
1428
|
+
reversible: !spawned,
|
|
1429
|
+
deploy
|
|
1430
|
+
});
|
|
1431
|
+
}
|
|
1432
|
+
async function refreshStageGuards(ctx, mutation, stageId) {
|
|
1433
|
+
await deployStageGuards({
|
|
1434
|
+
client: ctx.client,
|
|
1435
|
+
clientForGdr: ctx.clientForGdr,
|
|
1436
|
+
instance: materializeInstance(ctx.instance, mutation),
|
|
1437
|
+
definition: ctx.definition,
|
|
1438
|
+
stageId,
|
|
1439
|
+
now: ctx.now
|
|
1292
1440
|
});
|
|
1293
1441
|
}
|
|
1294
1442
|
async function commitTransition(ctx, action, actor) {
|
|
@@ -1330,15 +1478,18 @@ async function pickTransition(ctx, stage, action) {
|
|
|
1330
1478
|
if (await ctxEvaluateFilter(ctx, candidate.filter)) return candidate;
|
|
1331
1479
|
}
|
|
1332
1480
|
async function evaluateAutoTransitions(args) {
|
|
1333
|
-
const { client, instanceId, options, clientForGdr } = args, ctx = await loadContext(client, instanceId,
|
|
1481
|
+
const { client, instanceId, options, clientForGdr, clock } = args, ctx = await loadContext(client, instanceId, {
|
|
1482
|
+
...clientForGdr ? { clientForGdr } : {},
|
|
1483
|
+
...clock ? { clock } : {}
|
|
1484
|
+
});
|
|
1334
1485
|
return commitTransition(ctx, void 0, options?.actor);
|
|
1335
1486
|
}
|
|
1336
|
-
async function primeInitialStage(client, instanceId, actor, clientForGdr) {
|
|
1487
|
+
async function primeInitialStage(client, instanceId, actor, clientForGdr, clock) {
|
|
1337
1488
|
const instance = await client.getDocument(instanceId);
|
|
1338
1489
|
if (!instance || instance.stages.length > 0 || instance.pendingEffects.length > 0) return;
|
|
1339
1490
|
const definition = JSON.parse(instance.definitionSnapshot), stage = definition.stages.find((s) => s.id === instance.currentStageId);
|
|
1340
1491
|
if (stage === void 0) return;
|
|
1341
|
-
const snapshot = await hydrateSnapshot({
|
|
1492
|
+
const now = (clock ?? wallClock)(), snapshot = await hydrateSnapshot({
|
|
1342
1493
|
client,
|
|
1343
1494
|
clientForGdr: clientForGdr ?? (() => client),
|
|
1344
1495
|
instance,
|
|
@@ -1349,7 +1500,7 @@ async function primeInitialStage(client, instanceId, actor, clientForGdr) {
|
|
|
1349
1500
|
filter: task.filter,
|
|
1350
1501
|
definition,
|
|
1351
1502
|
snapshot,
|
|
1352
|
-
params: buildParams(instance)
|
|
1503
|
+
params: buildParams(instance, now)
|
|
1353
1504
|
});
|
|
1354
1505
|
tasks.push({
|
|
1355
1506
|
_key: randomKey(),
|
|
@@ -1360,8 +1511,8 @@ async function primeInitialStage(client, instanceId, actor, clientForGdr) {
|
|
|
1360
1511
|
const initialStageEntry = {
|
|
1361
1512
|
_key: randomKey(),
|
|
1362
1513
|
id: stage.id,
|
|
1363
|
-
enteredAt:
|
|
1364
|
-
state: await resolveStageStateSlots({ client, instance, stage }),
|
|
1514
|
+
enteredAt: now,
|
|
1515
|
+
state: await resolveStageStateSlots({ client, instance, stage, now }),
|
|
1365
1516
|
tasks
|
|
1366
1517
|
}, pendingEffects = [], newHistory = [...instance.history];
|
|
1367
1518
|
for (const effect of stage.onEnter ?? []) {
|
|
@@ -1369,29 +1520,44 @@ async function primeInitialStage(client, instanceId, actor, clientForGdr) {
|
|
|
1369
1520
|
bindings: effect.bindings,
|
|
1370
1521
|
staticInput: effect.input,
|
|
1371
1522
|
instance,
|
|
1523
|
+
now,
|
|
1372
1524
|
...actor !== void 0 ? { actor } : {}
|
|
1373
1525
|
}), { pending, history } = buildQueuedEffect(
|
|
1374
1526
|
effect,
|
|
1375
1527
|
{ kind: "stageEnter", id: stage.id },
|
|
1376
1528
|
params,
|
|
1377
|
-
actor
|
|
1529
|
+
actor,
|
|
1530
|
+
now
|
|
1378
1531
|
);
|
|
1379
1532
|
pendingEffects.push(pending), newHistory.push(history);
|
|
1380
1533
|
}
|
|
1381
|
-
await client.patch(instance._id).set({
|
|
1534
|
+
const committed = await client.patch(instance._id).set({
|
|
1382
1535
|
stages: [initialStageEntry],
|
|
1383
1536
|
pendingEffects,
|
|
1384
1537
|
history: newHistory,
|
|
1385
|
-
lastChangedAt:
|
|
1386
|
-
}).ifRevisionId(instance._rev).commit()
|
|
1538
|
+
lastChangedAt: now
|
|
1539
|
+
}).ifRevisionId(instance._rev).commit();
|
|
1540
|
+
await deployOrRollback({
|
|
1387
1541
|
client,
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1542
|
+
instanceId: instance._id,
|
|
1543
|
+
committedRev: committed._rev,
|
|
1544
|
+
restore: {
|
|
1545
|
+
stages: instance.stages,
|
|
1546
|
+
pendingEffects: instance.pendingEffects,
|
|
1547
|
+
history: instance.history
|
|
1548
|
+
},
|
|
1549
|
+
reversible: !0,
|
|
1550
|
+
deploy: () => deployStageGuards({
|
|
1551
|
+
client,
|
|
1552
|
+
clientForGdr: clientForGdr ?? (() => client),
|
|
1553
|
+
instance,
|
|
1554
|
+
definition,
|
|
1555
|
+
stageId: stage.id,
|
|
1556
|
+
now
|
|
1557
|
+
})
|
|
1558
|
+
}), await autoActivatePrimedTasks(client, instanceId, definition, stage, actor, clientForGdr, clock);
|
|
1393
1559
|
}
|
|
1394
|
-
async function autoActivatePrimedTasks(client, instanceId, definition, stage, actor, clientForGdr) {
|
|
1560
|
+
async function autoActivatePrimedTasks(client, instanceId, definition, stage, actor, clientForGdr, clock) {
|
|
1395
1561
|
const resolvedClientForGdr = clientForGdr ?? (() => client);
|
|
1396
1562
|
for (const task of stage.tasks ?? []) {
|
|
1397
1563
|
if (!activatesOnStageEnter(task)) continue;
|
|
@@ -1401,7 +1567,8 @@ async function autoActivatePrimedTasks(client, instanceId, definition, stage, ac
|
|
|
1401
1567
|
client,
|
|
1402
1568
|
clientForGdr: resolvedClientForGdr,
|
|
1403
1569
|
instance: updated,
|
|
1404
|
-
definition
|
|
1570
|
+
definition,
|
|
1571
|
+
...clock ? { clock } : {}
|
|
1405
1572
|
}), mutation = startMutation(updated), mutEntry = currentTasks(mutation).find((t) => t.id === task.id);
|
|
1406
1573
|
mutEntry && mutEntry.status === "pending" && (await activateTask(updatedCtx, mutation, task, mutEntry, actor), await persist(updatedCtx, mutation));
|
|
1407
1574
|
}
|
|
@@ -1419,8 +1586,11 @@ async function gatherAutoResolveCandidates(ctx, tasks) {
|
|
|
1419
1586
|
}
|
|
1420
1587
|
return candidates;
|
|
1421
1588
|
}
|
|
1422
|
-
async function resolveCompleteWhen(client, instanceId, clientForGdr) {
|
|
1423
|
-
const ctx = await loadContext(client, instanceId,
|
|
1589
|
+
async function resolveCompleteWhen(client, instanceId, clientForGdr, clock) {
|
|
1590
|
+
const ctx = await loadContext(client, instanceId, {
|
|
1591
|
+
...clientForGdr ? { clientForGdr } : {},
|
|
1592
|
+
...clock ? { clock } : {}
|
|
1593
|
+
}), stage = findStage(ctx.definition, ctx.instance.currentStageId), tasksWithAutoPredicate = (stage.tasks ?? []).filter(
|
|
1424
1594
|
(t) => t.completeWhen !== void 0 || t.failWhen !== void 0
|
|
1425
1595
|
);
|
|
1426
1596
|
if (tasksWithAutoPredicate.length === 0) return 0;
|
|
@@ -1434,65 +1604,71 @@ async function resolveCompleteWhen(client, instanceId, clientForGdr) {
|
|
|
1434
1604
|
const previousStatus = mutEntry.status, actor = {
|
|
1435
1605
|
kind: "system",
|
|
1436
1606
|
id: outcome === "failed" ? "engine.failWhen" : "engine.completeWhen"
|
|
1437
|
-
};
|
|
1438
|
-
mutEntry.status = outcome, mutEntry.completedAt =
|
|
1607
|
+
}, now = ctx.now;
|
|
1608
|
+
mutEntry.status = outcome, mutEntry.completedAt = now, mutEntry.completedBy = actor.id, mutation.history.push(
|
|
1439
1609
|
taskStatusChangedEntry({
|
|
1440
1610
|
stageId: stage.id,
|
|
1441
1611
|
taskId: task.id,
|
|
1442
1612
|
from: previousStatus,
|
|
1443
1613
|
to: outcome,
|
|
1614
|
+
at: now,
|
|
1444
1615
|
actor
|
|
1445
1616
|
})
|
|
1446
1617
|
), count++;
|
|
1447
1618
|
}
|
|
1448
1619
|
return count > 0 && await persist(ctx, mutation), count;
|
|
1449
1620
|
}
|
|
1450
|
-
async function cascadeAutoTransitions(client, instanceId, actor, clientForGdr) {
|
|
1621
|
+
async function cascadeAutoTransitions(client, instanceId, actor, clientForGdr, clock) {
|
|
1451
1622
|
let count = 0;
|
|
1452
1623
|
for (; ; ) {
|
|
1453
|
-
if (await resolveCompleteWhen(client, instanceId, clientForGdr), !(await evaluateAutoTransitions({
|
|
1624
|
+
if (await resolveCompleteWhen(client, instanceId, clientForGdr, clock), !(await evaluateAutoTransitions({
|
|
1454
1625
|
client,
|
|
1455
1626
|
instanceId,
|
|
1456
1627
|
...actor !== void 0 ? { options: { actor } } : {},
|
|
1457
|
-
...clientForGdr ? { clientForGdr } : {}
|
|
1628
|
+
...clientForGdr ? { clientForGdr } : {},
|
|
1629
|
+
...clock ? { clock } : {}
|
|
1458
1630
|
})).fired) return count;
|
|
1459
|
-
if (count++, count
|
|
1460
|
-
throw new
|
|
1461
|
-
`Cascade did not stabilise after ${CASCADE_LIMIT} auto-transitions on ${instanceId} \u2014 possible infinite loop`
|
|
1462
|
-
);
|
|
1631
|
+
if (count++, count >= CASCADE_LIMIT)
|
|
1632
|
+
throw new CascadeLimitError({ instanceId, limit: CASCADE_LIMIT });
|
|
1463
1633
|
}
|
|
1464
1634
|
}
|
|
1465
|
-
async function
|
|
1466
|
-
const
|
|
1467
|
-
if (!instance || instance.ancestors.length === 0) return;
|
|
1468
|
-
const parentRef = instance.ancestors[instance.ancestors.length - 1];
|
|
1635
|
+
async function findSatisfiedParentSpawn(client, child) {
|
|
1636
|
+
const parentRef = child.ancestors.at(-1);
|
|
1469
1637
|
if (parentRef === void 0) return;
|
|
1470
|
-
const
|
|
1471
|
-
if (!parent || parent._type !== "workflow.instance" || typeof parent.definitionSnapshot != "string")
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1638
|
+
const parent = await client.getDocument(gdrToBareId(parentRef.id));
|
|
1639
|
+
if (!parent || parent._type !== "workflow.instance" || typeof parent.definitionSnapshot != "string") return;
|
|
1640
|
+
const definition = JSON.parse(parent.definitionSnapshot), stage = definition.stages.find((s) => s.id === parent.currentStageId);
|
|
1641
|
+
if (stage === void 0) return;
|
|
1642
|
+
const parentCurrentTasks = findCurrentTasks(parent), task = (stage.tasks ?? []).find((t) => t.spawns === void 0 ? !1 : parentCurrentTasks.find((e) => e.id === t.id)?.spawnedInstances?.some((r) => gdrToBareId(r.id) === child._id) ?? !1);
|
|
1643
|
+
if (task?.spawns === void 0) return;
|
|
1644
|
+
const entry = parentCurrentTasks.find((e) => e.id === task.id);
|
|
1645
|
+
if (!(entry === void 0 || entry.status === "done") && await checkSpawnCompletion(client, entry, task.spawns))
|
|
1646
|
+
return { parent, definition, stage, task };
|
|
1647
|
+
}
|
|
1648
|
+
async function propagateToAncestors(client, instanceId, actor, clientForGdr, clock) {
|
|
1649
|
+
const instance = await client.getDocument(instanceId);
|
|
1650
|
+
if (!instance) return;
|
|
1651
|
+
const found = await findSatisfiedParentSpawn(client, instance);
|
|
1652
|
+
if (found === void 0) return;
|
|
1653
|
+
const { parent, definition, stage, task } = found, ctx = await buildEngineContext({
|
|
1480
1654
|
client,
|
|
1481
1655
|
clientForGdr: clientForGdr ?? (() => client),
|
|
1482
1656
|
instance: parent,
|
|
1483
|
-
definition
|
|
1484
|
-
|
|
1657
|
+
definition,
|
|
1658
|
+
...clock ? { clock } : {}
|
|
1659
|
+
}), mutation = startMutation(parent), mutEntry = currentTasks(mutation).find((e) => e.id === task.id);
|
|
1485
1660
|
if (mutEntry === void 0) return;
|
|
1486
|
-
const previousStatus = mutEntry.status;
|
|
1487
|
-
mutEntry.status = "done", mutEntry.completedAt =
|
|
1661
|
+
const previousStatus = mutEntry.status, now = ctx.now;
|
|
1662
|
+
mutEntry.status = "done", mutEntry.completedAt = now, mutation.history.push(
|
|
1488
1663
|
taskStatusChangedEntry({
|
|
1489
|
-
stageId:
|
|
1490
|
-
taskId:
|
|
1664
|
+
stageId: stage.id,
|
|
1665
|
+
taskId: task.id,
|
|
1491
1666
|
from: previousStatus,
|
|
1492
1667
|
to: "done",
|
|
1668
|
+
at: now,
|
|
1493
1669
|
...actor !== void 0 ? { actor } : {}
|
|
1494
1670
|
})
|
|
1495
|
-
), await persist(ctx, mutation), await cascadeAutoTransitions(client,
|
|
1671
|
+
), await persist(ctx, mutation), await cascadeAutoTransitions(client, parent._id, actor, clientForGdr, clock), await propagateToAncestors(client, parent._id, actor, clientForGdr, clock);
|
|
1496
1672
|
}
|
|
1497
1673
|
function startMutation(instance) {
|
|
1498
1674
|
return {
|
|
@@ -1521,7 +1697,7 @@ function taskStatusChangedEntry(args) {
|
|
|
1521
1697
|
return {
|
|
1522
1698
|
_key: randomKey(),
|
|
1523
1699
|
_type: "workflow.history.taskStatusChanged",
|
|
1524
|
-
at: args.at
|
|
1700
|
+
at: args.at,
|
|
1525
1701
|
stageId: args.stageId,
|
|
1526
1702
|
taskId: args.taskId,
|
|
1527
1703
|
from: args.from,
|
|
@@ -1561,19 +1737,31 @@ function stageTransitionHistory(args) {
|
|
|
1561
1737
|
}
|
|
1562
1738
|
];
|
|
1563
1739
|
}
|
|
1564
|
-
|
|
1565
|
-
const
|
|
1740
|
+
function instanceStateFields(src) {
|
|
1741
|
+
const fields = {
|
|
1742
|
+
currentStageId: src.currentStageId,
|
|
1743
|
+
state: src.state,
|
|
1744
|
+
stages: src.stages,
|
|
1745
|
+
pendingEffects: src.pendingEffects,
|
|
1746
|
+
effectHistory: src.effectHistory,
|
|
1747
|
+
effectsContext: src.effectsContext,
|
|
1748
|
+
history: src.history
|
|
1749
|
+
};
|
|
1750
|
+
return src.completedAt !== void 0 && (fields.completedAt = src.completedAt), fields;
|
|
1751
|
+
}
|
|
1752
|
+
function materializeInstance(base, mutation) {
|
|
1753
|
+
return {
|
|
1754
|
+
...base,
|
|
1566
1755
|
currentStageId: mutation.currentStageId,
|
|
1567
1756
|
state: mutation.state,
|
|
1568
|
-
stages: mutation.stages
|
|
1569
|
-
pendingEffects: mutation.pendingEffects,
|
|
1570
|
-
effectHistory: mutation.effectHistory,
|
|
1571
|
-
effectsContext: mutation.effectsContext,
|
|
1572
|
-
history: mutation.history,
|
|
1573
|
-
lastChangedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1757
|
+
stages: mutation.stages
|
|
1574
1758
|
};
|
|
1575
|
-
|
|
1576
|
-
|
|
1759
|
+
}
|
|
1760
|
+
async function persist(ctx, mutation) {
|
|
1761
|
+
const set = {
|
|
1762
|
+
...instanceStateFields(mutation),
|
|
1763
|
+
lastChangedAt: ctx.now
|
|
1764
|
+
}, pendingCreates = mutation.pendingCreates;
|
|
1577
1765
|
if (pendingCreates.length === 0)
|
|
1578
1766
|
return ctx.client.patch(ctx.instance._id).set(set).ifRevisionId(ctx.instance._rev).commit();
|
|
1579
1767
|
const tx = ctx.client.transaction();
|
|
@@ -1581,7 +1769,7 @@ async function persist(ctx, mutation) {
|
|
|
1581
1769
|
tx.patch(ctx.client.patch(ctx.instance._id).set(set).ifRevisionId(ctx.instance._rev)), await tx.commit(), mutation.pendingCreates = [];
|
|
1582
1770
|
const actorForPriming = void 0;
|
|
1583
1771
|
for (const body of pendingCreates)
|
|
1584
|
-
await primeInitialStage(ctx.client, body._id, actorForPriming, ctx.clientForGdr), await cascadeAutoTransitions(ctx.client, body._id, actorForPriming, ctx.clientForGdr), await propagateToAncestors(ctx.client, body._id, actorForPriming, ctx.clientForGdr);
|
|
1772
|
+
await primeInitialStage(ctx.client, body._id, actorForPriming, ctx.clientForGdr, ctx.clock), await cascadeAutoTransitions(ctx.client, body._id, actorForPriming, ctx.clientForGdr, ctx.clock), await propagateToAncestors(ctx.client, body._id, actorForPriming, ctx.clientForGdr, ctx.clock);
|
|
1585
1773
|
const reloaded = await ctx.client.getDocument(ctx.instance._id);
|
|
1586
1774
|
if (!reloaded)
|
|
1587
1775
|
throw new Error(`Instance ${ctx.instance._id} disappeared after transaction commit`);
|
|
@@ -1622,7 +1810,9 @@ function findCurrentTasks(instance) {
|
|
|
1622
1810
|
return findCurrentStageEntry(instance)?.tasks ?? [];
|
|
1623
1811
|
}
|
|
1624
1812
|
async function completeEffect(args) {
|
|
1625
|
-
const { client, instanceId, effectKey, status, outputs, detail, error, durationMs, options } = args, ctx = await loadContext(client, instanceId
|
|
1813
|
+
const { client, instanceId, effectKey, status, outputs, detail, error, durationMs, options } = args, ctx = await loadContext(client, instanceId, {
|
|
1814
|
+
...options?.clock ? { clock: options.clock } : {}
|
|
1815
|
+
});
|
|
1626
1816
|
return commitCompleteEffect(
|
|
1627
1817
|
ctx,
|
|
1628
1818
|
effectKey,
|
|
@@ -1664,7 +1854,7 @@ async function commitCompleteEffect(ctx, effectKey, status, outputs, detail, err
|
|
|
1664
1854
|
throw new Error(`Pending effect "${effectKey}" not found on instance ${ctx.instance._id}`);
|
|
1665
1855
|
const mutation = startMutation(ctx.instance);
|
|
1666
1856
|
mutation.pendingEffects = mutation.pendingEffects.filter((e) => e._key !== effectKey);
|
|
1667
|
-
const ranAt =
|
|
1857
|
+
const ranAt = ctx.now;
|
|
1668
1858
|
return mutation.effectHistory.push(
|
|
1669
1859
|
buildEffectHistoryEntry(pending, { status, ranAt, actor, detail, error, durationMs, outputs })
|
|
1670
1860
|
), status === "done" && outputs !== void 0 && upsertEffectsContext(mutation, outputs), mutation.history.push({
|
|
@@ -1679,7 +1869,7 @@ async function commitCompleteEffect(ctx, effectKey, status, outputs, detail, err
|
|
|
1679
1869
|
...actor !== void 0 ? { actor } : {}
|
|
1680
1870
|
}), await persist(ctx, mutation), { effectKey, status };
|
|
1681
1871
|
}
|
|
1682
|
-
function buildQueuedEffect(effect, origin, params, actor) {
|
|
1872
|
+
function buildQueuedEffect(effect, origin, params, actor, now) {
|
|
1683
1873
|
const key = randomKey(), pending = {
|
|
1684
1874
|
_key: key,
|
|
1685
1875
|
_type: "workflow.pendingEffect",
|
|
@@ -1690,11 +1880,11 @@ function buildQueuedEffect(effect, origin, params, actor) {
|
|
|
1690
1880
|
params,
|
|
1691
1881
|
origin,
|
|
1692
1882
|
...actor !== void 0 ? { actor } : {},
|
|
1693
|
-
queuedAt:
|
|
1883
|
+
queuedAt: now
|
|
1694
1884
|
}, history = {
|
|
1695
1885
|
_key: randomKey(),
|
|
1696
1886
|
_type: "workflow.history.effectQueued",
|
|
1697
|
-
at:
|
|
1887
|
+
at: now,
|
|
1698
1888
|
effectKey: key,
|
|
1699
1889
|
effectId: effect.id,
|
|
1700
1890
|
origin
|
|
@@ -1703,7 +1893,7 @@ function buildQueuedEffect(effect, origin, params, actor) {
|
|
|
1703
1893
|
}
|
|
1704
1894
|
async function queueEffects(ctx, mutation, effects, origin, actor, callerParams) {
|
|
1705
1895
|
if (!effects) return;
|
|
1706
|
-
const liveInstance = {
|
|
1896
|
+
const now = ctx.now, liveInstance = {
|
|
1707
1897
|
...ctx.instance,
|
|
1708
1898
|
state: mutation.state,
|
|
1709
1899
|
stages: mutation.stages,
|
|
@@ -1714,22 +1904,21 @@ async function queueEffects(ctx, mutation, effects, origin, actor, callerParams)
|
|
|
1714
1904
|
bindings: effect.bindings,
|
|
1715
1905
|
staticInput: effect.input,
|
|
1716
1906
|
instance: liveInstance,
|
|
1907
|
+
now,
|
|
1717
1908
|
...callerParams !== void 0 ? { params: callerParams } : {},
|
|
1718
1909
|
...actor !== void 0 ? { actor } : {}
|
|
1719
|
-
}), { pending, history } = buildQueuedEffect(effect, origin, params, actor);
|
|
1910
|
+
}), { pending, history } = buildQueuedEffect(effect, origin, params, actor, now);
|
|
1720
1911
|
mutation.pendingEffects.push(pending), mutation.history.push(history);
|
|
1721
1912
|
}
|
|
1722
1913
|
}
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
${lines}`), this.name = "ActionParamsInvalidError", this.action = args.action, this.taskId = args.taskId, this.issues = args.issues;
|
|
1732
|
-
}
|
|
1914
|
+
const STATE_OP_TYPES = [
|
|
1915
|
+
"workflow.op.state.set",
|
|
1916
|
+
"workflow.op.state.append",
|
|
1917
|
+
"workflow.op.state.updateWhere",
|
|
1918
|
+
"workflow.op.state.removeWhere"
|
|
1919
|
+
];
|
|
1920
|
+
function isStateOp(summary) {
|
|
1921
|
+
return STATE_OP_TYPES.includes(summary.opType);
|
|
1733
1922
|
}
|
|
1734
1923
|
function validateActionParams(action, taskId, callerParams) {
|
|
1735
1924
|
const declared = action.params ?? [], params = callerParams ?? {}, issues = [];
|
|
@@ -1766,7 +1955,7 @@ function checkParamType(value, decl) {
|
|
|
1766
1955
|
case "doc.ref":
|
|
1767
1956
|
return hasStringField(value, "id");
|
|
1768
1957
|
case "doc.refs":
|
|
1769
|
-
return Array.isArray(value) && value.every((
|
|
1958
|
+
return Array.isArray(value) && value.every((v2) => checkParamType(v2, { paramType: "doc.ref" }));
|
|
1770
1959
|
case "json":
|
|
1771
1960
|
return !0;
|
|
1772
1961
|
default:
|
|
@@ -1774,15 +1963,15 @@ function checkParamType(value, decl) {
|
|
|
1774
1963
|
}
|
|
1775
1964
|
}
|
|
1776
1965
|
async function runActionOps(args) {
|
|
1777
|
-
const { ops, mutation, stageId, taskId, actionName, params, actor } = args;
|
|
1966
|
+
const { ops, mutation, stageId, taskId, actionName, params, actor, self, now } = args;
|
|
1778
1967
|
if (ops === void 0 || ops.length === 0) return [];
|
|
1779
1968
|
const summaries = [];
|
|
1780
1969
|
for (const op of ops) {
|
|
1781
|
-
const summary = applyOp(op, { mutation, stageId, taskId, params, actor });
|
|
1970
|
+
const summary = applyOp(op, { mutation, stageId, taskId, params, actor, self, now });
|
|
1782
1971
|
summaries.push(summary), mutation.history.push({
|
|
1783
1972
|
_key: randomKey(),
|
|
1784
1973
|
_type: "workflow.history.opApplied",
|
|
1785
|
-
at:
|
|
1974
|
+
at: now,
|
|
1786
1975
|
stageId,
|
|
1787
1976
|
taskId,
|
|
1788
1977
|
actionId: actionName,
|
|
@@ -1841,8 +2030,8 @@ function applyStateUpdateWhere(op, ctx) {
|
|
|
1841
2030
|
/*createIfMissing*/
|
|
1842
2031
|
!1
|
|
1843
2032
|
), resolvedMerge = {};
|
|
1844
|
-
for (const [k,
|
|
1845
|
-
resolvedMerge[k] = resolveOpSource(
|
|
2033
|
+
for (const [k, v2] of Object.entries(op.merge))
|
|
2034
|
+
resolvedMerge[k] = resolveOpSource(v2, ctx);
|
|
1846
2035
|
return slot !== void 0 && Array.isArray(slot.value) && (slot.value = slot.value.map(
|
|
1847
2036
|
(row) => evalOpPredicate(op.where, row, ctx) ? { ...row, ...resolvedMerge } : row
|
|
1848
2037
|
)), { opType: op.type, target: op.target, resolved: { merge: resolvedMerge } };
|
|
@@ -1885,18 +2074,11 @@ function locateSlot(ctx, target, createIfMissing) {
|
|
|
1885
2074
|
}, host.push(slot)), slot;
|
|
1886
2075
|
}
|
|
1887
2076
|
function resolveOpSource(src, ctx) {
|
|
2077
|
+
const staticValue = resolveStaticSource(src, ctx);
|
|
2078
|
+
if (staticValue.handled) return staticValue.value;
|
|
1888
2079
|
switch (src.source) {
|
|
1889
|
-
case "literal":
|
|
1890
|
-
return src.value;
|
|
1891
|
-
case "param":
|
|
1892
|
-
return ctx.params[src.paramId];
|
|
1893
|
-
case "actor":
|
|
1894
|
-
return ctx.actor;
|
|
1895
|
-
case "now":
|
|
1896
|
-
return (/* @__PURE__ */ new Date()).toISOString();
|
|
1897
2080
|
case "self":
|
|
1898
|
-
return;
|
|
1899
|
-
// Not applicable inside ops; reserved for higher-level binding.
|
|
2081
|
+
return ctx.self;
|
|
1900
2082
|
case "stageId":
|
|
1901
2083
|
return ctx.stageId;
|
|
1902
2084
|
case "stateRead": {
|
|
@@ -1906,8 +2088,8 @@ function resolveOpSource(src, ctx) {
|
|
|
1906
2088
|
case "effectOutput": {
|
|
1907
2089
|
const entry = ctx.mutation.effectsContext.find((e) => e.id === src.contextId);
|
|
1908
2090
|
if (entry === void 0) return null;
|
|
1909
|
-
const
|
|
1910
|
-
return
|
|
2091
|
+
const v2 = src.path !== void 0 ? getPath(entry.value, src.path) : entry.value;
|
|
2092
|
+
return v2 === void 0 ? null : v2;
|
|
1911
2093
|
}
|
|
1912
2094
|
case "object": {
|
|
1913
2095
|
const out = {};
|
|
@@ -1915,6 +2097,11 @@ function resolveOpSource(src, ctx) {
|
|
|
1915
2097
|
out[field] = resolveOpSource(fieldSrc, ctx);
|
|
1916
2098
|
return out;
|
|
1917
2099
|
}
|
|
2100
|
+
case "row":
|
|
2101
|
+
case "parentState":
|
|
2102
|
+
return;
|
|
2103
|
+
default:
|
|
2104
|
+
return;
|
|
1918
2105
|
}
|
|
1919
2106
|
}
|
|
1920
2107
|
function slotValue(slots, slotId) {
|
|
@@ -1934,8 +2121,23 @@ function evalOpPredicate(p, row, ctx) {
|
|
|
1934
2121
|
return row[p.field] === target;
|
|
1935
2122
|
}
|
|
1936
2123
|
async function fireAction(args) {
|
|
1937
|
-
const { client, instanceId, taskId, action, params, options } = args
|
|
1938
|
-
|
|
2124
|
+
const { client, instanceId, taskId, action, params, options } = args;
|
|
2125
|
+
for (let attempt = 1; attempt <= CONCURRENT_FIRE_ACTION_MAX_ATTEMPTS; attempt++) {
|
|
2126
|
+
const ctx = await loadContext(client, instanceId, {
|
|
2127
|
+
...options?.clock ? { clock: options.clock } : {}
|
|
2128
|
+
});
|
|
2129
|
+
try {
|
|
2130
|
+
return await commitAction(ctx, taskId, action, params, options?.actor);
|
|
2131
|
+
} catch (error) {
|
|
2132
|
+
if (!isRevisionConflict(error)) throw error;
|
|
2133
|
+
}
|
|
2134
|
+
}
|
|
2135
|
+
throw new ConcurrentFireActionError({
|
|
2136
|
+
instanceId,
|
|
2137
|
+
taskId,
|
|
2138
|
+
action,
|
|
2139
|
+
attempts: CONCURRENT_FIRE_ACTION_MAX_ATTEMPTS
|
|
2140
|
+
});
|
|
1939
2141
|
}
|
|
1940
2142
|
async function resolveActionCommit(ctx, taskId, actionName, callerParams) {
|
|
1941
2143
|
const { stage, task } = findTaskInCurrentStage(ctx, taskId), action = (task.actions ?? []).find((a) => a.id === actionName);
|
|
@@ -1951,15 +2153,16 @@ async function resolveActionCommit(ctx, taskId, actionName, callerParams) {
|
|
|
1951
2153
|
const params = validateActionParams(action, taskId, callerParams);
|
|
1952
2154
|
return { stage, task, action, params };
|
|
1953
2155
|
}
|
|
1954
|
-
function applyActionSetStatus(action, mutation, mutEntry, stageId, taskId, actor) {
|
|
2156
|
+
function applyActionSetStatus(action, mutation, mutEntry, stageId, taskId, actor, now) {
|
|
1955
2157
|
if (action.setStatus === void 0) return;
|
|
1956
2158
|
const initialStatus = mutEntry.status, newStatus = action.setStatus;
|
|
1957
|
-
return mutEntry.status = newStatus, mutEntry.completedAt =
|
|
2159
|
+
return mutEntry.status = newStatus, mutEntry.completedAt = now, actor && (mutEntry.completedBy = actor.id), mutation.history.push(
|
|
1958
2160
|
taskStatusChangedEntry({
|
|
1959
2161
|
stageId,
|
|
1960
2162
|
taskId,
|
|
1961
2163
|
from: initialStatus,
|
|
1962
2164
|
to: newStatus,
|
|
2165
|
+
at: now,
|
|
1963
2166
|
...actor !== void 0 ? { actor } : {}
|
|
1964
2167
|
})
|
|
1965
2168
|
), newStatus;
|
|
@@ -1970,11 +2173,11 @@ async function commitAction(ctx, taskId, actionName, callerParams, actor) {
|
|
|
1970
2173
|
taskId,
|
|
1971
2174
|
actionName,
|
|
1972
2175
|
callerParams
|
|
1973
|
-
), mutation = startMutation(ctx.instance), mutEntry = requireMutationTaskEntry(mutation, taskId);
|
|
2176
|
+
), mutation = startMutation(ctx.instance), mutEntry = requireMutationTaskEntry(mutation, taskId), now = ctx.now;
|
|
1974
2177
|
mutation.history.push({
|
|
1975
2178
|
_key: randomKey(),
|
|
1976
2179
|
_type: "workflow.history.actionFired",
|
|
1977
|
-
at:
|
|
2180
|
+
at: now,
|
|
1978
2181
|
stageId: stage.id,
|
|
1979
2182
|
taskId,
|
|
1980
2183
|
actionId: actionName,
|
|
@@ -1987,8 +2190,10 @@ async function commitAction(ctx, taskId, actionName, callerParams, actor) {
|
|
|
1987
2190
|
taskId,
|
|
1988
2191
|
actionName,
|
|
1989
2192
|
params,
|
|
1990
|
-
actor
|
|
1991
|
-
|
|
2193
|
+
actor,
|
|
2194
|
+
self: selfGdr(ctx.instance),
|
|
2195
|
+
now
|
|
2196
|
+
}), newStatus = applyActionSetStatus(action, mutation, mutEntry, stage.id, taskId, actor, now);
|
|
1992
2197
|
return await queueEffects(
|
|
1993
2198
|
ctx,
|
|
1994
2199
|
mutation,
|
|
@@ -1999,7 +2204,11 @@ async function commitAction(ctx, taskId, actionName, callerParams, actor) {
|
|
|
1999
2204
|
},
|
|
2000
2205
|
actor,
|
|
2001
2206
|
params
|
|
2002
|
-
), await
|
|
2207
|
+
), await persistThenDeploy(
|
|
2208
|
+
ctx,
|
|
2209
|
+
mutation,
|
|
2210
|
+
() => ranOps.some(isStateOp) ? refreshStageGuards(ctx, mutation, stage.id) : Promise.resolve()
|
|
2211
|
+
), {
|
|
2003
2212
|
fired: !0,
|
|
2004
2213
|
taskId,
|
|
2005
2214
|
action: actionName,
|
|
@@ -2038,7 +2247,7 @@ async function resolveAccess(client, args = {}) {
|
|
|
2038
2247
|
const overrideActor = args.override?.actor, overrideGrants = args.override?.grants;
|
|
2039
2248
|
if (overrideActor !== void 0 && overrideGrants !== void 0)
|
|
2040
2249
|
return { actor: overrideActor, grants: overrideGrants };
|
|
2041
|
-
const requestFn = client.request;
|
|
2250
|
+
const requestFn = client.request === void 0 ? void 0 : (opts) => client.request(opts);
|
|
2042
2251
|
if (requestFn === void 0) {
|
|
2043
2252
|
if (overrideActor === void 0)
|
|
2044
2253
|
throw new Error(
|
|
@@ -2113,7 +2322,7 @@ function canonicalTag(tags) {
|
|
|
2113
2322
|
}
|
|
2114
2323
|
const WILDCARD_ROLE = "*";
|
|
2115
2324
|
async function evaluateInstance(args) {
|
|
2116
|
-
const { client, tags, instanceId, resourceClients } = args;
|
|
2325
|
+
const { client, tags, instanceId, resourceClients } = args, now = (args.clock ?? wallClock)();
|
|
2117
2326
|
validateTags(tags);
|
|
2118
2327
|
const { actor, grants } = await resolveAccess(client, {
|
|
2119
2328
|
...args.access !== void 0 ? { override: args.access } : {},
|
|
@@ -2145,7 +2354,8 @@ async function evaluateInstance(args) {
|
|
|
2145
2354
|
definition,
|
|
2146
2355
|
actor,
|
|
2147
2356
|
grants,
|
|
2148
|
-
snapshot
|
|
2357
|
+
snapshot,
|
|
2358
|
+
now
|
|
2149
2359
|
})
|
|
2150
2360
|
);
|
|
2151
2361
|
const transitionEvaluations = [];
|
|
@@ -2156,7 +2366,7 @@ async function evaluateInstance(args) {
|
|
|
2156
2366
|
filter: transition.filter,
|
|
2157
2367
|
definition,
|
|
2158
2368
|
snapshot,
|
|
2159
|
-
params: buildParams(instance)
|
|
2369
|
+
params: buildParams(instance, now)
|
|
2160
2370
|
})
|
|
2161
2371
|
});
|
|
2162
2372
|
const currentStage = {
|
|
@@ -2174,7 +2384,7 @@ async function evaluateInstance(args) {
|
|
|
2174
2384
|
};
|
|
2175
2385
|
}
|
|
2176
2386
|
async function evaluateTask(args) {
|
|
2177
|
-
const { task, statusEntry, instance, definition, actor, grants, snapshot } = args, status = statusEntry?.status ?? "pending", actions = [];
|
|
2387
|
+
const { task, statusEntry, instance, definition, actor, grants, snapshot, now } = args, status = statusEntry?.status ?? "pending", actions = [];
|
|
2178
2388
|
for (const action of task.actions ?? [])
|
|
2179
2389
|
actions.push(
|
|
2180
2390
|
await evaluateAction({
|
|
@@ -2186,7 +2396,8 @@ async function evaluateTask(args) {
|
|
|
2186
2396
|
definition,
|
|
2187
2397
|
actor,
|
|
2188
2398
|
...grants !== void 0 ? { grants } : {},
|
|
2189
|
-
snapshot
|
|
2399
|
+
snapshot,
|
|
2400
|
+
now
|
|
2190
2401
|
})
|
|
2191
2402
|
);
|
|
2192
2403
|
return {
|
|
@@ -2200,9 +2411,9 @@ async function evaluateTask(args) {
|
|
|
2200
2411
|
};
|
|
2201
2412
|
}
|
|
2202
2413
|
async function evaluateAction(args) {
|
|
2203
|
-
const { action, task, status, instance, definition, actor, grants, snapshot } = args, syncReason = lifecycleReason(instance, definition, status) ?? actorReason(action, task, actor);
|
|
2414
|
+
const { action, task, status, instance, definition, actor, grants, snapshot, now } = args, syncReason = lifecycleReason(instance, definition, status) ?? actorReason(action, task, actor);
|
|
2204
2415
|
if (syncReason !== void 0) return disabled(action, syncReason);
|
|
2205
|
-
const asyncReason = await permissionReason(action, instance, actor, grants) ?? await filterReason(action, instance, definition, snapshot);
|
|
2416
|
+
const asyncReason = await permissionReason(action, instance, actor, grants) ?? await filterReason(action, instance, definition, snapshot, now);
|
|
2206
2417
|
return asyncReason !== void 0 ? disabled(action, asyncReason) : { action, allowed: !0 };
|
|
2207
2418
|
}
|
|
2208
2419
|
function lifecycleReason(instance, definition, status) {
|
|
@@ -2231,12 +2442,12 @@ async function permissionReason(action, instance, actor, grants) {
|
|
|
2231
2442
|
}))
|
|
2232
2443
|
return { kind: "permission-denied", permission, matchingGrants: grants.length };
|
|
2233
2444
|
}
|
|
2234
|
-
async function filterReason(action, instance, definition, snapshot) {
|
|
2445
|
+
async function filterReason(action, instance, definition, snapshot, now) {
|
|
2235
2446
|
if (!(action.filter === void 0 || await evaluateFilter({
|
|
2236
2447
|
filter: action.filter,
|
|
2237
2448
|
definition,
|
|
2238
2449
|
snapshot,
|
|
2239
|
-
params: buildParams(instance)
|
|
2450
|
+
params: buildParams(instance, now)
|
|
2240
2451
|
})))
|
|
2241
2452
|
return { kind: "filter-failed", filter: action.filter };
|
|
2242
2453
|
}
|
|
@@ -2261,6 +2472,43 @@ function parseDefinitionSnapshot(instance) {
|
|
|
2261
2472
|
);
|
|
2262
2473
|
}
|
|
2263
2474
|
}
|
|
2475
|
+
const resourceKey = (r) => `${r.type}.${r.id}`;
|
|
2476
|
+
function guardsForResource(client) {
|
|
2477
|
+
return client.fetch("*[_type == $t] | order(_id asc)", { t: GUARD_DOC_TYPE });
|
|
2478
|
+
}
|
|
2479
|
+
async function guardsForInstance(args) {
|
|
2480
|
+
const { client, clientForGdr, instance } = args, clientsByResource = /* @__PURE__ */ new Map([
|
|
2481
|
+
[resourceKey(instance.workflowResource), client]
|
|
2482
|
+
]), stateUris = [
|
|
2483
|
+
...collectSlotDocUris(instance.state),
|
|
2484
|
+
...instance.stages.flatMap((stage) => collectSlotDocUris(stage.state))
|
|
2485
|
+
];
|
|
2486
|
+
for (const uri of stateUris) {
|
|
2487
|
+
const parsed = tryParseGdr(uri);
|
|
2488
|
+
if (parsed === void 0) continue;
|
|
2489
|
+
const key = resourceKey(resourceFromParsed(parsed));
|
|
2490
|
+
clientsByResource.has(key) || clientsByResource.set(key, clientForGdr(parsed));
|
|
2491
|
+
}
|
|
2492
|
+
const perResource = await Promise.all(
|
|
2493
|
+
[...clientsByResource.values()].map(
|
|
2494
|
+
(c) => c.fetch("*[_type == $t && sourceInstanceId == $id]", {
|
|
2495
|
+
t: GUARD_DOC_TYPE,
|
|
2496
|
+
id: instance._id
|
|
2497
|
+
})
|
|
2498
|
+
)
|
|
2499
|
+
);
|
|
2500
|
+
return dedupById(perResource.flat());
|
|
2501
|
+
}
|
|
2502
|
+
function tryParseGdr(uri) {
|
|
2503
|
+
try {
|
|
2504
|
+
return parseGdr(uri);
|
|
2505
|
+
} catch {
|
|
2506
|
+
return;
|
|
2507
|
+
}
|
|
2508
|
+
}
|
|
2509
|
+
function dedupById(guards) {
|
|
2510
|
+
return [...new Map(guards.map((g) => [g._id, g])).values()].sort((a, b) => a._id.localeCompare(b._id));
|
|
2511
|
+
}
|
|
2264
2512
|
class ActionDisabledError extends Error {
|
|
2265
2513
|
reason;
|
|
2266
2514
|
taskId;
|
|
@@ -2371,14 +2619,14 @@ function isDefinitionUnchanged(existing, expected) {
|
|
|
2371
2619
|
}
|
|
2372
2620
|
function stripSystemFields(doc) {
|
|
2373
2621
|
const out = {};
|
|
2374
|
-
for (const [k,
|
|
2375
|
-
k === "_rev" || k === "_createdAt" || k === "_updatedAt" || (out[k] =
|
|
2622
|
+
for (const [k, v2] of Object.entries(doc))
|
|
2623
|
+
k === "_rev" || k === "_createdAt" || k === "_updatedAt" || (out[k] = v2);
|
|
2376
2624
|
return out;
|
|
2377
2625
|
}
|
|
2378
2626
|
function stableStringify(value) {
|
|
2379
2627
|
return value === null || typeof value != "object" ? JSON.stringify(value) : Array.isArray(value) ? `[${value.map(stableStringify).join(",")}]` : `{${Object.entries(value).toSorted(
|
|
2380
2628
|
([a], [b]) => a.localeCompare(b)
|
|
2381
|
-
).map(([k,
|
|
2629
|
+
).map(([k, v2]) => `${JSON.stringify(k)}:${stableStringify(v2)}`).join(",")}}`;
|
|
2382
2630
|
}
|
|
2383
2631
|
async function loadDefinition(client, workflowId, version, tags) {
|
|
2384
2632
|
if (version !== void 0) {
|
|
@@ -2418,9 +2666,9 @@ async function resolveOperationContext(args) {
|
|
|
2418
2666
|
clientForGdr: buildClientForGdr(args.client, args.resourceClients)
|
|
2419
2667
|
};
|
|
2420
2668
|
}
|
|
2421
|
-
async function cascade(client, instanceId, actor, clientForGdr) {
|
|
2422
|
-
const count = await cascadeAutoTransitions(client, instanceId, actor, clientForGdr);
|
|
2423
|
-
return await propagateToAncestors(client, instanceId, actor, clientForGdr), count;
|
|
2669
|
+
async function cascade(client, instanceId, actor, clientForGdr, clock) {
|
|
2670
|
+
const count = await cascadeAutoTransitions(client, instanceId, actor, clientForGdr, clock);
|
|
2671
|
+
return await propagateToAncestors(client, instanceId, actor, clientForGdr, clock), count;
|
|
2424
2672
|
}
|
|
2425
2673
|
function buildClientForGdr(defaultClient, resolver) {
|
|
2426
2674
|
return resolver === void 0 ? () => defaultClient : (parsed) => resolver(parsed) ?? defaultClient;
|
|
@@ -2512,16 +2760,17 @@ const workflow = {
|
|
|
2512
2760
|
effectsContext,
|
|
2513
2761
|
instanceId,
|
|
2514
2762
|
perspective
|
|
2515
|
-
} = args, { actor, clientForGdr } = await resolveOperationContext(args), definition = await loadDefinition(client, workflowId, version, tags), id = instanceId ?? `${canonicalTag(tags)}.wf-instance.${randomKey()}`;
|
|
2763
|
+
} = args, { actor, clientForGdr } = await resolveOperationContext(args), clock = args.clock ?? wallClock, definition = await loadDefinition(client, workflowId, version, tags), id = instanceId ?? `${canonicalTag(tags)}.wf-instance.${randomKey()}`;
|
|
2516
2764
|
if (definition.stages.find((s) => s.id === definition.initialStageId) === void 0)
|
|
2517
2765
|
throw new Error(
|
|
2518
2766
|
`Initial stage "${definition.initialStageId}" missing in ${workflowId} v${definition.version}`
|
|
2519
2767
|
);
|
|
2520
|
-
const now = (
|
|
2768
|
+
const now = clock(), effectsContextEntries = effectsContext ? toEffectsContextEntries(effectsContext) : [], resolvedState = await resolveDeclaredState({
|
|
2521
2769
|
slotDefs: definition.state ?? [],
|
|
2522
2770
|
initialState: initialState ?? [],
|
|
2523
2771
|
ctx: {
|
|
2524
2772
|
client,
|
|
2773
|
+
now,
|
|
2525
2774
|
selfId: id,
|
|
2526
2775
|
tags,
|
|
2527
2776
|
workflowResource,
|
|
@@ -2543,7 +2792,7 @@ const workflow = {
|
|
|
2543
2792
|
initialStageId: definition.initialStageId,
|
|
2544
2793
|
actor
|
|
2545
2794
|
});
|
|
2546
|
-
return await client.create(base), await primeInitialStage(client, id, actor, clientForGdr), await cascade(client, id, actor, clientForGdr), reload(client, id, tags);
|
|
2795
|
+
return await client.create(base), await primeInitialStage(client, id, actor, clientForGdr, clock), await cascade(client, id, actor, clientForGdr, clock), reload(client, id, tags);
|
|
2547
2796
|
},
|
|
2548
2797
|
/**
|
|
2549
2798
|
* Fire an action against a task. If the task is pending, it is
|
|
@@ -2565,7 +2814,7 @@ const workflow = {
|
|
|
2565
2814
|
params,
|
|
2566
2815
|
idempotent,
|
|
2567
2816
|
resourceClients
|
|
2568
|
-
} = args, { access, actor, clientForGdr } = await resolveOperationContext(args), before = await reload(client, instanceId, tags), taskEntry = before.stages.find(
|
|
2817
|
+
} = args, { access, actor, clientForGdr } = await resolveOperationContext(args), clock = args.clock ?? wallClock, before = await reload(client, instanceId, tags), taskEntry = before.stages.find(
|
|
2569
2818
|
(s) => s.id === before.currentStageId && s.exitedAt === void 0
|
|
2570
2819
|
)?.tasks.find((t) => t.id === taskId);
|
|
2571
2820
|
if (taskEntry === void 0 && idempotent === !0)
|
|
@@ -2575,6 +2824,7 @@ const workflow = {
|
|
|
2575
2824
|
tags,
|
|
2576
2825
|
instanceId,
|
|
2577
2826
|
access,
|
|
2827
|
+
clock,
|
|
2578
2828
|
...resourceClients !== void 0 ? { resourceClients } : {}
|
|
2579
2829
|
})).currentStage.tasks.find((t) => t.task.id === taskId)?.actions.find((a) => a.action.id === action);
|
|
2580
2830
|
if (actionEval !== void 0 && !actionEval.allowed && actionEval.disabledReason)
|
|
@@ -2587,7 +2837,7 @@ const workflow = {
|
|
|
2587
2837
|
client,
|
|
2588
2838
|
instanceId,
|
|
2589
2839
|
taskId,
|
|
2590
|
-
options: { actor }
|
|
2840
|
+
options: { actor, clock }
|
|
2591
2841
|
});
|
|
2592
2842
|
const actionResult = await fireAction({
|
|
2593
2843
|
client,
|
|
@@ -2595,8 +2845,8 @@ const workflow = {
|
|
|
2595
2845
|
taskId,
|
|
2596
2846
|
action,
|
|
2597
2847
|
...params !== void 0 ? { params } : {},
|
|
2598
|
-
options: { actor }
|
|
2599
|
-
}), cascaded = await cascade(client, instanceId, actor, clientForGdr);
|
|
2848
|
+
options: { actor, clock }
|
|
2849
|
+
}), cascaded = await cascade(client, instanceId, actor, clientForGdr, clock);
|
|
2600
2850
|
return {
|
|
2601
2851
|
instance: await reload(client, instanceId, tags),
|
|
2602
2852
|
cascaded,
|
|
@@ -2612,7 +2862,7 @@ const workflow = {
|
|
|
2612
2862
|
* reference them. Cascades after.
|
|
2613
2863
|
*/
|
|
2614
2864
|
completeEffect: async (args) => {
|
|
2615
|
-
const { client, tags, instanceId, effectKey, status, outputs, detail, error, durationMs } = args, { actor, clientForGdr } = await resolveOperationContext(args);
|
|
2865
|
+
const { client, tags, instanceId, effectKey, status, outputs, detail, error, durationMs } = args, { actor, clientForGdr } = await resolveOperationContext(args), clock = args.clock ?? wallClock;
|
|
2616
2866
|
await completeEffect({
|
|
2617
2867
|
client,
|
|
2618
2868
|
instanceId,
|
|
@@ -2622,9 +2872,9 @@ const workflow = {
|
|
|
2622
2872
|
...detail !== void 0 ? { detail } : {},
|
|
2623
2873
|
...error !== void 0 ? { error } : {},
|
|
2624
2874
|
...durationMs !== void 0 ? { durationMs } : {},
|
|
2625
|
-
...actor !== void 0 ? {
|
|
2875
|
+
options: { ...actor !== void 0 ? { actor } : {}, clock }
|
|
2626
2876
|
});
|
|
2627
|
-
const cascaded = await cascade(client, instanceId, actor, clientForGdr);
|
|
2877
|
+
const cascaded = await cascade(client, instanceId, actor, clientForGdr, clock);
|
|
2628
2878
|
return {
|
|
2629
2879
|
instance: await reload(client, instanceId, tags),
|
|
2630
2880
|
cascaded,
|
|
@@ -2641,9 +2891,9 @@ const workflow = {
|
|
|
2641
2891
|
* affected instances and the engine re-evaluates.
|
|
2642
2892
|
*/
|
|
2643
2893
|
tick: async (args) => {
|
|
2644
|
-
const { client, tags, instanceId } = args, { actor, clientForGdr } = await resolveOperationContext(args);
|
|
2894
|
+
const { client, tags, instanceId } = args, { actor, clientForGdr } = await resolveOperationContext(args), clock = args.clock ?? wallClock;
|
|
2645
2895
|
await reload(client, instanceId, tags);
|
|
2646
|
-
const cascaded = await cascade(client, instanceId, actor, clientForGdr);
|
|
2896
|
+
const cascaded = await cascade(client, instanceId, actor, clientForGdr, clock);
|
|
2647
2897
|
return {
|
|
2648
2898
|
instance: await reload(client, instanceId, tags),
|
|
2649
2899
|
cascaded,
|
|
@@ -2656,15 +2906,15 @@ const workflow = {
|
|
|
2656
2906
|
* upstream; this verb performs the mechanical move.
|
|
2657
2907
|
*/
|
|
2658
2908
|
setStage: async (args) => {
|
|
2659
|
-
const { client, tags, instanceId, targetStageId, reason } = args, { actor, clientForGdr } = await resolveOperationContext(args);
|
|
2909
|
+
const { client, tags, instanceId, targetStageId, reason } = args, { actor, clientForGdr } = await resolveOperationContext(args), clock = args.clock ?? wallClock;
|
|
2660
2910
|
await reload(client, instanceId, tags);
|
|
2661
2911
|
const result = await setStage({
|
|
2662
2912
|
client,
|
|
2663
2913
|
instanceId,
|
|
2664
2914
|
targetStageId,
|
|
2665
2915
|
...reason !== void 0 ? { reason } : {},
|
|
2666
|
-
...actor !== void 0 ? {
|
|
2667
|
-
}), cascaded = result.fired ? await cascade(client, instanceId, actor, clientForGdr) : 0;
|
|
2916
|
+
options: { ...actor !== void 0 ? { actor } : {}, clock }
|
|
2917
|
+
}), cascaded = result.fired ? await cascade(client, instanceId, actor, clientForGdr, clock) : 0;
|
|
2668
2918
|
return {
|
|
2669
2919
|
instance: await reload(client, instanceId, tags),
|
|
2670
2920
|
cascaded,
|
|
@@ -2680,6 +2930,16 @@ const workflow = {
|
|
|
2680
2930
|
const { client, tags, instanceId } = args;
|
|
2681
2931
|
return validateTags(tags), reload(client, instanceId, tags);
|
|
2682
2932
|
},
|
|
2933
|
+
guardsForInstance: async (args) => {
|
|
2934
|
+
const { client, tags, instanceId, resourceClients } = args;
|
|
2935
|
+
validateTags(tags);
|
|
2936
|
+
const instance = await reload(client, instanceId, tags);
|
|
2937
|
+
return guardsForInstance({
|
|
2938
|
+
client,
|
|
2939
|
+
clientForGdr: buildClientForGdr(client, resourceClients),
|
|
2940
|
+
instance
|
|
2941
|
+
});
|
|
2942
|
+
},
|
|
2683
2943
|
/**
|
|
2684
2944
|
* Run a caller-supplied GROQ query with the engine's tags bound as
|
|
2685
2945
|
* `$engineTags`. This does NOT rewrite the query — arbitrary GROQ
|
|
@@ -2714,9 +2974,9 @@ const workflow = {
|
|
|
2714
2974
|
* without re-implementing hydration. Pure read — never writes.
|
|
2715
2975
|
*/
|
|
2716
2976
|
queryInScope: async (args) => {
|
|
2717
|
-
const { client, tags, instanceId, groq, params, resourceClients } = args;
|
|
2977
|
+
const { client, tags, instanceId, groq, params, resourceClients } = args, clock = args.clock ?? wallClock;
|
|
2718
2978
|
validateTags(tags);
|
|
2719
|
-
const instance = await reload(client, instanceId, tags), definition = JSON.parse(instance.definitionSnapshot), clientForGdr = buildClientForGdr(client, resourceClients), snapshot = await hydrateSnapshot({ client, clientForGdr, instance, definition }), reserved = buildParams(instance), tree = parse(groq, { params: { ...reserved, ...params } });
|
|
2979
|
+
const instance = await reload(client, instanceId, tags), definition = JSON.parse(instance.definitionSnapshot), clientForGdr = buildClientForGdr(client, resourceClients), snapshot = await hydrateSnapshot({ client, clientForGdr, instance, definition }), reserved = buildParams(instance, clock()), tree = parse(groq, { params: { ...reserved, ...params } });
|
|
2720
2980
|
return await (await evaluate(tree, {
|
|
2721
2981
|
dataset: snapshot.docs,
|
|
2722
2982
|
params: { ...reserved, ...params }
|
|
@@ -2900,7 +3160,8 @@ async function claimPendingEffect(client, instance, effectKey, drainerActor) {
|
|
|
2900
3160
|
};
|
|
2901
3161
|
try {
|
|
2902
3162
|
return await client.patch(instance._id).ifRevisionId(instance._rev).set({ [`pendingEffects[_key=="${effectKey}"].claim`]: claim }).commit(), !0;
|
|
2903
|
-
} catch {
|
|
3163
|
+
} catch (error) {
|
|
3164
|
+
if (!isRevisionConflict(error)) throw error;
|
|
2904
3165
|
return !1;
|
|
2905
3166
|
}
|
|
2906
3167
|
}
|
|
@@ -2945,13 +3206,14 @@ const defaultLoggerFactory = (name) => ({
|
|
|
2945
3206
|
}
|
|
2946
3207
|
};
|
|
2947
3208
|
function createEngine(args) {
|
|
2948
|
-
const { client, workflowResource, tags, resourceClients } = args;
|
|
3209
|
+
const { client, workflowResource, tags, resourceClients, clock } = args;
|
|
2949
3210
|
validateTags(tags);
|
|
2950
3211
|
const effectHandlers = args.effectHandlers ?? {}, missingHandler = args.missingHandler ?? "fail", logger = args.loggerFactory ?? defaultLoggerFactory, bind = (rest) => ({
|
|
2951
3212
|
client,
|
|
2952
3213
|
tags,
|
|
2953
3214
|
workflowResource,
|
|
2954
3215
|
...resourceClients !== void 0 ? { resourceClients } : {},
|
|
3216
|
+
...clock !== void 0 ? { clock } : {},
|
|
2955
3217
|
...rest
|
|
2956
3218
|
});
|
|
2957
3219
|
return {
|
|
@@ -2969,6 +3231,13 @@ function createEngine(args) {
|
|
|
2969
3231
|
evaluateInstance: (rest) => evaluateInstance(bind(rest)),
|
|
2970
3232
|
setStage: (rest) => workflow.setStage(bind(rest)),
|
|
2971
3233
|
getInstance: ({ instanceId }) => workflow.getInstance({ client, tags, workflowResource, instanceId }),
|
|
3234
|
+
guardsForInstance: ({ instanceId }) => workflow.guardsForInstance({
|
|
3235
|
+
client,
|
|
3236
|
+
tags,
|
|
3237
|
+
workflowResource,
|
|
3238
|
+
instanceId,
|
|
3239
|
+
...resourceClients !== void 0 ? { resourceClients } : {}
|
|
3240
|
+
}),
|
|
2972
3241
|
children: ({ instanceId, taskId }) => workflow.children({
|
|
2973
3242
|
client,
|
|
2974
3243
|
tags,
|
|
@@ -2989,7 +3258,8 @@ function createEngine(args) {
|
|
|
2989
3258
|
workflowResource,
|
|
2990
3259
|
instanceId,
|
|
2991
3260
|
groq,
|
|
2992
|
-
...params !== void 0 ? { params } : {}
|
|
3261
|
+
...params !== void 0 ? { params } : {},
|
|
3262
|
+
...clock !== void 0 ? { clock } : {}
|
|
2993
3263
|
}),
|
|
2994
3264
|
listPendingEffects: ({ instanceId }) => workflow.listPendingEffects({ client, tags, workflowResource, instanceId }),
|
|
2995
3265
|
findPendingEffects: ({ instanceId, claimed, names }) => workflow.findPendingEffects({
|
|
@@ -3020,15 +3290,6 @@ function createEngine(args) {
|
|
|
3020
3290
|
})
|
|
3021
3291
|
};
|
|
3022
3292
|
}
|
|
3023
|
-
class MutationGuardDeniedError extends Error {
|
|
3024
|
-
denied;
|
|
3025
|
-
documentId;
|
|
3026
|
-
action;
|
|
3027
|
-
constructor(args) {
|
|
3028
|
-
const ids = args.denied.map((d) => d.guardId).join(", ");
|
|
3029
|
-
super(`Mutation on "${args.documentId}" (${args.action}) denied by guard(s) [${ids}]`), this.name = "MutationGuardDeniedError", this.denied = args.denied, this.documentId = args.documentId, this.action = args.action;
|
|
3030
|
-
}
|
|
3031
|
-
}
|
|
3032
3293
|
const HISTORY_DISPLAY = {
|
|
3033
3294
|
"workflow.history.stageEntered": {
|
|
3034
3295
|
title: "Stage entered",
|
|
@@ -3224,14 +3485,18 @@ export {
|
|
|
3224
3485
|
AUTHORING_DISPLAY,
|
|
3225
3486
|
ActionDisabledError,
|
|
3226
3487
|
ActionParamsInvalidError,
|
|
3488
|
+
CascadeLimitError,
|
|
3489
|
+
ConcurrentFireActionError,
|
|
3227
3490
|
DISPLAY,
|
|
3228
3491
|
EFFECTS_CONTEXT_DISPLAY,
|
|
3492
|
+
GUARD_DOC_TYPE,
|
|
3229
3493
|
GUARD_LIFTED_PREDICATE,
|
|
3230
3494
|
GUARD_OWNER,
|
|
3231
3495
|
HISTORY_DISPLAY,
|
|
3232
3496
|
MutationGuardDeniedError,
|
|
3233
3497
|
OP_DISPLAY,
|
|
3234
3498
|
STATE_SLOT_DISPLAY,
|
|
3499
|
+
WorkflowStateDivergedError,
|
|
3235
3500
|
canonicalTag,
|
|
3236
3501
|
compileGuard,
|
|
3237
3502
|
createEngine,
|
|
@@ -3246,10 +3511,12 @@ export {
|
|
|
3246
3511
|
gdrRef,
|
|
3247
3512
|
gdrUri,
|
|
3248
3513
|
guardMatches,
|
|
3514
|
+
guardsForInstance,
|
|
3515
|
+
guardsForResource,
|
|
3516
|
+
isDefinitionUnchanged,
|
|
3249
3517
|
isGdr,
|
|
3250
3518
|
lakeGuardId,
|
|
3251
3519
|
parseGdr,
|
|
3252
|
-
reconcileStageGuards,
|
|
3253
3520
|
refCanvas,
|
|
3254
3521
|
refDashboard,
|
|
3255
3522
|
refDataset,
|
|
@@ -3257,8 +3524,10 @@ export {
|
|
|
3257
3524
|
resolveAccess,
|
|
3258
3525
|
retractStageGuards,
|
|
3259
3526
|
silentLogger,
|
|
3527
|
+
stripSystemFields,
|
|
3260
3528
|
validateDefinition,
|
|
3261
3529
|
validateTags,
|
|
3530
|
+
wallClock,
|
|
3262
3531
|
workflow
|
|
3263
3532
|
};
|
|
3264
3533
|
//# sourceMappingURL=index.js.map
|