@sanity/workflow-engine 0.12.0 → 0.13.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/_chunks-cjs/schema.cjs +97 -55
- package/dist/_chunks-cjs/schema.cjs.map +1 -1
- package/dist/_chunks-es/schema.js +97 -55
- package/dist/_chunks-es/schema.js.map +1 -1
- package/dist/define.cjs +321 -118
- package/dist/define.cjs.map +1 -1
- package/dist/define.d.cts +1305 -12045
- package/dist/define.d.ts +1305 -12045
- package/dist/define.js +321 -118
- package/dist/define.js.map +1 -1
- package/dist/index.cjs +1345 -642
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1166 -15587
- package/dist/index.d.ts +1166 -15587
- package/dist/index.js +1346 -643
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,10 +1,32 @@
|
|
|
1
1
|
import { parse, evaluate } from "groq-js";
|
|
2
|
-
import { actorFulfillsRole, isTerminalActivityStatus, WORKFLOW_DEFINITION_TYPE, andConditions, DOCUMENT_VALUE_PERMISSIONS } from "./_chunks-es/schema.js";
|
|
2
|
+
import { actorFulfillsRole, isTerminalActivityStatus, StoredFieldOpSchema, WORKFLOW_DEFINITION_TYPE, andConditions, DOCUMENT_VALUE_PERMISSIONS } from "./_chunks-es/schema.js";
|
|
3
3
|
import { ACTIVITY_KINDS, DRIVER_KINDS, isStartableDefinition } from "./_chunks-es/schema.js";
|
|
4
4
|
import * as v from "valibot";
|
|
5
5
|
function findOpenStageEntry(host) {
|
|
6
6
|
return host.stages.find((s) => s.name === host.currentStage && s.exitedAt === void 0);
|
|
7
7
|
}
|
|
8
|
+
function findCurrentActivityEntry(host, activityName) {
|
|
9
|
+
return findOpenStageEntry(host)?.activities.find((a) => a.name === activityName);
|
|
10
|
+
}
|
|
11
|
+
function errorMessage(err) {
|
|
12
|
+
return err instanceof Error ? err.message : String(err);
|
|
13
|
+
}
|
|
14
|
+
function rethrowWithContext(err, context) {
|
|
15
|
+
throw new Error(`${context}: ${errorMessage(err)}`, { cause: err });
|
|
16
|
+
}
|
|
17
|
+
function mapJsonStrings(value, transform) {
|
|
18
|
+
const walk = (val, key) => {
|
|
19
|
+
if (typeof val == "string") return transform(val, key);
|
|
20
|
+
if (Array.isArray(val)) return val.map((item) => walk(item, key));
|
|
21
|
+
if (val !== null && typeof val == "object") {
|
|
22
|
+
const out = {};
|
|
23
|
+
for (const [k, v2] of Object.entries(val)) out[k] = walk(v2, k);
|
|
24
|
+
return out;
|
|
25
|
+
}
|
|
26
|
+
return val;
|
|
27
|
+
};
|
|
28
|
+
return walk(value, void 0);
|
|
29
|
+
}
|
|
8
30
|
const KNOWN_SCHEMES = /* @__PURE__ */ new Set(["dataset", "canvas", "media-library", "dashboard"]);
|
|
9
31
|
function parseGdr(uri) {
|
|
10
32
|
const colon = uri.indexOf(":");
|
|
@@ -44,22 +66,56 @@ function parseGdr(uri) {
|
|
|
44
66
|
documentId: parts[1]
|
|
45
67
|
};
|
|
46
68
|
}
|
|
69
|
+
function tryParseGdr(uri) {
|
|
70
|
+
try {
|
|
71
|
+
return parseGdr(uri);
|
|
72
|
+
} catch {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
47
76
|
function gdrUri(parts) {
|
|
48
77
|
return parts.scheme === "dataset" ? `dataset:${parts.projectId}:${parts.dataset}:${parts.documentId}` : `${parts.scheme}:${parts.resourceId}:${parts.documentId}`;
|
|
49
78
|
}
|
|
50
79
|
function extractDocumentId(gdrUriString) {
|
|
51
80
|
return parseGdr(gdrUriString).documentId;
|
|
52
81
|
}
|
|
53
|
-
|
|
82
|
+
const RESOURCE_ALIAS_NAME_SOURCE = "[a-z0-9][a-z0-9-]*", RESOURCE_ALIAS_NAME_RE = new RegExp(`^${RESOURCE_ALIAS_NAME_SOURCE}$`);
|
|
83
|
+
function validateResourceAliasName(name) {
|
|
84
|
+
if (!RESOURCE_ALIAS_NAME_RE.test(name))
|
|
85
|
+
throw new Error(
|
|
86
|
+
`Invalid resource alias name "${name}": expected lowercase letters, digits, and dashes (no leading dash).`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
function toPhysicalGdr(id, home) {
|
|
90
|
+
return isGdrUri(id) ? id : gdrFromResource(home, id);
|
|
91
|
+
}
|
|
92
|
+
function refDataset({
|
|
93
|
+
projectId,
|
|
94
|
+
dataset,
|
|
95
|
+
documentId,
|
|
96
|
+
type
|
|
97
|
+
}) {
|
|
54
98
|
return { id: gdrUri({ scheme: "dataset", projectId, dataset, documentId }), type };
|
|
55
99
|
}
|
|
56
|
-
function refCanvas(
|
|
100
|
+
function refCanvas({
|
|
101
|
+
resourceId,
|
|
102
|
+
documentId,
|
|
103
|
+
type
|
|
104
|
+
}) {
|
|
57
105
|
return { id: gdrUri({ scheme: "canvas", resourceId, documentId }), type };
|
|
58
106
|
}
|
|
59
|
-
function refMediaLibrary(
|
|
107
|
+
function refMediaLibrary({
|
|
108
|
+
resourceId,
|
|
109
|
+
documentId,
|
|
110
|
+
type
|
|
111
|
+
}) {
|
|
60
112
|
return { id: gdrUri({ scheme: "media-library", resourceId, documentId }), type };
|
|
61
113
|
}
|
|
62
|
-
function refDashboard(
|
|
114
|
+
function refDashboard({
|
|
115
|
+
resourceId,
|
|
116
|
+
documentId,
|
|
117
|
+
type
|
|
118
|
+
}) {
|
|
63
119
|
return { id: gdrUri({ scheme: "dashboard", resourceId, documentId }), type };
|
|
64
120
|
}
|
|
65
121
|
function datasetResourceParts(id) {
|
|
@@ -75,17 +131,16 @@ function gdrFromResource(res, documentId) {
|
|
|
75
131
|
}
|
|
76
132
|
return gdrUri({ scheme: res.type, resourceId: res.id, documentId });
|
|
77
133
|
}
|
|
134
|
+
function gdrResourcePrefix(res) {
|
|
135
|
+
const full = gdrFromResource(res, "x");
|
|
136
|
+
return full.slice(0, full.length - 1);
|
|
137
|
+
}
|
|
138
|
+
function resourceFromParsed(parsed) {
|
|
139
|
+
return parsed.scheme === "dataset" ? { type: "dataset", id: `${parsed.projectId}.${parsed.dataset}` } : { type: parsed.scheme, id: parsed.resourceId ?? "" };
|
|
140
|
+
}
|
|
78
141
|
function resourceFromGdrUri(uri) {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
parsed = parseGdr(uri);
|
|
82
|
-
} catch {
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
if (parsed.scheme === "dataset")
|
|
86
|
-
return parsed.projectId === void 0 || parsed.dataset === void 0 ? void 0 : { type: "dataset", id: `${parsed.projectId}.${parsed.dataset}` };
|
|
87
|
-
if (parsed.resourceId !== void 0)
|
|
88
|
-
return { type: parsed.scheme, id: parsed.resourceId };
|
|
142
|
+
const parsed = tryParseGdr(uri);
|
|
143
|
+
return parsed === void 0 ? void 0 : resourceFromParsed(parsed);
|
|
89
144
|
}
|
|
90
145
|
function sameResource(a, b) {
|
|
91
146
|
return a.type === b.type && a.id === b.id;
|
|
@@ -93,15 +148,24 @@ function sameResource(a, b) {
|
|
|
93
148
|
function selfGdr(doc) {
|
|
94
149
|
return gdrFromResource(doc.workflowResource, doc._id);
|
|
95
150
|
}
|
|
151
|
+
function definitionDocId({
|
|
152
|
+
tag,
|
|
153
|
+
definition,
|
|
154
|
+
version
|
|
155
|
+
}) {
|
|
156
|
+
return `${tag}.${definition}.v${version}`;
|
|
157
|
+
}
|
|
96
158
|
function isGdrUri(value) {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
return !1;
|
|
102
|
-
}
|
|
159
|
+
return typeof value == "string" && tryParseGdr(value) !== void 0;
|
|
160
|
+
}
|
|
161
|
+
function toBareId(id) {
|
|
162
|
+
return isGdrUri(id) ? extractDocumentId(id) : id;
|
|
103
163
|
}
|
|
104
|
-
function gdrRef(
|
|
164
|
+
function gdrRef({
|
|
165
|
+
res,
|
|
166
|
+
documentId,
|
|
167
|
+
type
|
|
168
|
+
}) {
|
|
105
169
|
return { id: gdrFromResource(res, documentId), type };
|
|
106
170
|
}
|
|
107
171
|
function isGdr(value) {
|
|
@@ -136,17 +200,26 @@ function renderedFields(entries, snapshot) {
|
|
|
136
200
|
function renderedValue(entry, snapshot) {
|
|
137
201
|
return entry._type !== "doc.ref" || entry.value === null || snapshot === void 0 ? entry.value : snapshot.docs.find((d) => d._id === entry.value.id) ?? entry.value;
|
|
138
202
|
}
|
|
139
|
-
function scopedFieldOverlay(
|
|
203
|
+
function scopedFieldOverlay({
|
|
204
|
+
instance,
|
|
205
|
+
snapshot,
|
|
206
|
+
activityName
|
|
207
|
+
}) {
|
|
140
208
|
const stageEntry = findOpenStageEntry(instance);
|
|
141
209
|
if (stageEntry === void 0) return {};
|
|
142
210
|
const stageFields = renderedFields(stageEntry.fields ?? [], snapshot), activity = activityName ? stageEntry.activities.find((t) => t.name === activityName) : void 0;
|
|
143
211
|
return { ...stageFields, ...renderedFields(activity?.fields ?? [], snapshot) };
|
|
144
212
|
}
|
|
145
|
-
function assignedFor(
|
|
213
|
+
function assignedFor({
|
|
214
|
+
instance,
|
|
215
|
+
activityName,
|
|
216
|
+
actor,
|
|
217
|
+
roleAliases
|
|
218
|
+
}) {
|
|
146
219
|
if (actor === void 0) return !1;
|
|
147
|
-
const entry =
|
|
220
|
+
const entry = findCurrentActivityEntry(instance, activityName)?.fields?.find((s) => s._type === "assignees");
|
|
148
221
|
return entry === void 0 ? !1 : entry.value.some(
|
|
149
|
-
(a) => a.type === "user" ? a.id === actor.id : actorFulfillsRole(actor.roles, a.role, roleAliases)
|
|
222
|
+
(a) => a.type === "user" ? a.id === actor.id : actorFulfillsRole({ actorRoles: actor.roles, required: a.role, aliases: roleAliases })
|
|
150
223
|
);
|
|
151
224
|
}
|
|
152
225
|
function effectsContextMap(instance) {
|
|
@@ -159,35 +232,20 @@ function parseJsonContextEntry(entry) {
|
|
|
159
232
|
try {
|
|
160
233
|
return JSON.parse(entry.value);
|
|
161
234
|
} catch (err) {
|
|
162
|
-
|
|
163
|
-
`effectsContext entry "${entry.name}" holds unparseable JSON: ${err instanceof Error ? err.message : String(err)}`,
|
|
164
|
-
{ cause: err }
|
|
165
|
-
);
|
|
235
|
+
rethrowWithContext(err, `effectsContext entry "${entry.name}" holds unparseable JSON`);
|
|
166
236
|
}
|
|
167
237
|
}
|
|
168
238
|
function paramsForLake(params) {
|
|
169
239
|
return {
|
|
170
240
|
...params,
|
|
171
|
-
self: typeof params.self == "string" ?
|
|
172
|
-
parent: typeof params.parent == "string" ?
|
|
173
|
-
ancestors: Array.isArray(params.ancestors) ? params.ancestors.map((a) => typeof a == "string" ?
|
|
241
|
+
self: typeof params.self == "string" ? toBareId(params.self) : params.self,
|
|
242
|
+
parent: typeof params.parent == "string" ? toBareId(params.parent) : params.parent,
|
|
243
|
+
ancestors: Array.isArray(params.ancestors) ? params.ancestors.map((a) => typeof a == "string" ? toBareId(a) : a) : params.ancestors,
|
|
174
244
|
fields: stripFieldsForLake(params.fields)
|
|
175
245
|
};
|
|
176
246
|
}
|
|
177
247
|
function stripFieldsForLake(value) {
|
|
178
|
-
|
|
179
|
-
if (typeof value == "string") return bareId(value);
|
|
180
|
-
if (Array.isArray(value)) return value.map(stripFieldsForLake);
|
|
181
|
-
if (typeof value == "object") {
|
|
182
|
-
const out = {};
|
|
183
|
-
for (const [k, v2] of Object.entries(value))
|
|
184
|
-
out[k] = stripFieldsForLake(v2);
|
|
185
|
-
return out;
|
|
186
|
-
}
|
|
187
|
-
return value;
|
|
188
|
-
}
|
|
189
|
-
function bareId(id) {
|
|
190
|
-
return isGdrUri(id) ? extractDocumentId(id) : id;
|
|
248
|
+
return mapJsonStrings(value, (s) => toBareId(s));
|
|
191
249
|
}
|
|
192
250
|
function getPath(value, path) {
|
|
193
251
|
let current = value;
|
|
@@ -197,9 +255,6 @@ function getPath(value, path) {
|
|
|
197
255
|
}
|
|
198
256
|
return current;
|
|
199
257
|
}
|
|
200
|
-
function resourceOf(p) {
|
|
201
|
-
return p.scheme === "dataset" ? { type: "dataset", id: `${p.projectId}.${p.dataset}` } : { type: p.scheme, id: p.resourceId ?? "" };
|
|
202
|
-
}
|
|
203
258
|
const FIELD_READ = /^\$fields\.(\w+)(?:\.(.+))?$/, EFFECTS_READ = /^\$effects\['([^']+)'\](?:\.(.+))?$/;
|
|
204
259
|
function isGuardReadExpr(expr) {
|
|
205
260
|
return expr === "$self" || expr === "$now" || FIELD_READ.test(expr) || EFFECTS_READ.test(expr);
|
|
@@ -242,9 +297,9 @@ function resolveIdRefTargets(idRefs, ctx) {
|
|
|
242
297
|
return targets.length === 0 ? null : targets;
|
|
243
298
|
}
|
|
244
299
|
function assertSingleResource(targets) {
|
|
245
|
-
const resource =
|
|
300
|
+
const resource = resourceFromParsed(targets[0].parsed);
|
|
246
301
|
for (const g of targets) {
|
|
247
|
-
const r =
|
|
302
|
+
const r = resourceFromParsed(g.parsed);
|
|
248
303
|
if (r.type !== resource.type || r.id !== resource.id)
|
|
249
304
|
throw new Error(
|
|
250
305
|
`Guard targets span multiple resources (${resource.type}:${resource.id} vs ${r.type}:${r.id}); a guard is single-resource.`
|
|
@@ -308,23 +363,31 @@ function validateStage(v2, stage) {
|
|
|
308
363
|
for (const entry of stage.fields ?? [])
|
|
309
364
|
v2.checkEntry(entry, `stage "${stage.name}".fields "${entry.name}"`);
|
|
310
365
|
for (const guard of stage.guards ?? [])
|
|
311
|
-
validateGuardReads(v2, stage.name, guard);
|
|
366
|
+
validateGuardReads({ v: v2, stageName: stage.name, guard });
|
|
312
367
|
for (const t of stage.transitions ?? []) {
|
|
313
368
|
v2.checkCondition(t.filter, `transition "${t.name}" (${stage.name} \u2192 ${t.to}) filter`);
|
|
314
369
|
for (const [key, groq] of effectBindings(t.effects))
|
|
315
370
|
v2.tryParse(groq, `transition "${t.name}" effect binding "${key}"`);
|
|
316
371
|
}
|
|
317
372
|
for (const activity of stage.activities ?? [])
|
|
318
|
-
validateActivity(v2, stage.name, activity);
|
|
373
|
+
validateActivity({ v: v2, stageName: stage.name, activity });
|
|
319
374
|
}
|
|
320
|
-
function validateGuardReads(
|
|
375
|
+
function validateGuardReads({
|
|
376
|
+
v: v2,
|
|
377
|
+
stageName,
|
|
378
|
+
guard
|
|
379
|
+
}) {
|
|
321
380
|
const where = `stage "${stageName}" guard "${guard.name}"`;
|
|
322
381
|
for (const expr of guard.match.idRefs ?? [])
|
|
323
382
|
v2.checkGuardRead(expr, `${where} match.idRefs`);
|
|
324
383
|
for (const [key, expr] of Object.entries(guard.metadata ?? {}))
|
|
325
384
|
v2.checkGuardRead(expr, `${where} metadata "${key}"`);
|
|
326
385
|
}
|
|
327
|
-
function validateActivity(
|
|
386
|
+
function validateActivity({
|
|
387
|
+
v: v2,
|
|
388
|
+
stageName,
|
|
389
|
+
activity
|
|
390
|
+
}) {
|
|
328
391
|
const where = `stage "${stageName}" activity "${activity.name}"`;
|
|
329
392
|
for (const entry of activity.fields ?? [])
|
|
330
393
|
v2.checkEntry(entry, `${where}.fields "${entry.name}"`);
|
|
@@ -333,7 +396,7 @@ function validateActivity(v2, stageName, activity) {
|
|
|
333
396
|
v2.checkCondition(groq, `${where}.requirements "${name}"`);
|
|
334
397
|
for (const [key, groq] of effectBindings(activity.effects))
|
|
335
398
|
v2.tryParse(groq, `${where} effect binding "${key}"`);
|
|
336
|
-
validateActivityActions(v2, activity), activity.subworkflows !== void 0 && validateSubworkflows(v2, where, activity.subworkflows);
|
|
399
|
+
validateActivityActions(v2, activity), activity.subworkflows !== void 0 && validateSubworkflows({ v: v2, where, sub: activity.subworkflows });
|
|
337
400
|
}
|
|
338
401
|
function validateActivityActions(v2, activity) {
|
|
339
402
|
for (const a of activity.actions ?? []) {
|
|
@@ -342,7 +405,11 @@ function validateActivityActions(v2, activity) {
|
|
|
342
405
|
v2.tryParse(groq, `activity "${activity.name}" action "${a.name}" effect binding "${key}"`);
|
|
343
406
|
}
|
|
344
407
|
}
|
|
345
|
-
function validateSubworkflows(
|
|
408
|
+
function validateSubworkflows({
|
|
409
|
+
v: v2,
|
|
410
|
+
where,
|
|
411
|
+
sub
|
|
412
|
+
}) {
|
|
346
413
|
v2.tryParse(sub.forEach, `${where}.subworkflows.forEach`);
|
|
347
414
|
for (const [key, groq] of Object.entries(sub.with ?? {}))
|
|
348
415
|
v2.tryParse(groq, `${where}.subworkflows.with "${key}"`);
|
|
@@ -421,7 +488,11 @@ function globMatch(pattern, value) {
|
|
|
421
488
|
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
422
489
|
return new RegExp(`^${escaped}$`).test(value);
|
|
423
490
|
}
|
|
424
|
-
function guardMatches(
|
|
491
|
+
function guardMatches({
|
|
492
|
+
guard,
|
|
493
|
+
doc,
|
|
494
|
+
action
|
|
495
|
+
}) {
|
|
425
496
|
const m = guard.match;
|
|
426
497
|
if (!m.actions.includes(action) || m.types && m.types.length > 0 && !(doc.type !== void 0 && m.types.includes(doc.type)))
|
|
427
498
|
return !1;
|
|
@@ -450,7 +521,7 @@ async function evaluateMutationGuard(args) {
|
|
|
450
521
|
async function denyingGuards(args) {
|
|
451
522
|
const { guards, doc, context } = args, denied = [];
|
|
452
523
|
for (const guard of guards)
|
|
453
|
-
guardMatches(guard, doc, context.action) && (await evaluateMutationGuard({ guard, context }) || denied.push(guard));
|
|
524
|
+
guardMatches({ guard, doc, action: context.action }) && (await evaluateMutationGuard({ guard, context }) || denied.push(guard));
|
|
454
525
|
return denied;
|
|
455
526
|
}
|
|
456
527
|
async function instanceWriteDenials(args) {
|
|
@@ -621,6 +692,16 @@ ${lines}`
|
|
|
621
692
|
), this.name = "ActionParamsInvalidError", this.action = args.action, this.activity = args.activity, this.issues = args.issues;
|
|
622
693
|
}
|
|
623
694
|
}
|
|
695
|
+
class EffectOpsInvalidError extends Error {
|
|
696
|
+
effect;
|
|
697
|
+
issues;
|
|
698
|
+
constructor(args) {
|
|
699
|
+
const lines = args.issues.map((i) => ` - ${i}`).join(`
|
|
700
|
+
`);
|
|
701
|
+
super(`Effect "${args.effect}" returned invalid completion ops:
|
|
702
|
+
${lines}`), this.name = "EffectOpsInvalidError", this.effect = args.effect, this.issues = args.issues;
|
|
703
|
+
}
|
|
704
|
+
}
|
|
624
705
|
class RequiredFieldNotProvidedError extends Error {
|
|
625
706
|
definition;
|
|
626
707
|
missing;
|
|
@@ -697,38 +778,39 @@ class CascadeLimitError extends Error {
|
|
|
697
778
|
function buildSnapshot(args) {
|
|
698
779
|
const docs = [], knownIds = /* @__PURE__ */ new Set();
|
|
699
780
|
for (const { doc, resource } of args.docs) {
|
|
700
|
-
const uri = gdrFromResource(resource, doc._id), restamped = restampForSnapshot(doc, uri, resource);
|
|
781
|
+
const uri = gdrFromResource(resource, doc._id), restamped = restampForSnapshot({ doc, gdrUri: uri, resource });
|
|
701
782
|
docs.push(restamped), knownIds.add(uri);
|
|
702
783
|
}
|
|
703
784
|
return { docs, knownIds };
|
|
704
785
|
}
|
|
705
|
-
function restampForSnapshot(
|
|
786
|
+
function restampForSnapshot({
|
|
787
|
+
doc,
|
|
788
|
+
gdrUri: gdrUri2,
|
|
789
|
+
resource
|
|
790
|
+
}) {
|
|
706
791
|
return { ...rewriteRefsRecursive(doc, resource), _id: gdrUri2 };
|
|
707
792
|
}
|
|
708
793
|
function rewriteRefsRecursive(value, resource) {
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
794
|
+
return mapJsonStrings(
|
|
795
|
+
value,
|
|
796
|
+
(str, key) => (
|
|
797
|
+
// Only `_ref` string values become URIs; already-qualified ones pass through.
|
|
798
|
+
key === "_ref" && !str.includes(":") ? gdrFromResource(resource, str) : str
|
|
799
|
+
)
|
|
800
|
+
);
|
|
716
801
|
}
|
|
717
802
|
const WORKFLOW_INSTANCE_TYPE = "sanity.workflow.instance";
|
|
718
803
|
function parseDefinitionSnapshot(instance) {
|
|
719
804
|
try {
|
|
720
805
|
return JSON.parse(instance.definitionSnapshot);
|
|
721
806
|
} catch (err) {
|
|
722
|
-
|
|
723
|
-
`Failed to parse definitionSnapshot on instance "${instance._id}": ${err instanceof Error ? err.message : String(err)}`,
|
|
724
|
-
{ cause: err }
|
|
725
|
-
);
|
|
807
|
+
rethrowWithContext(err, `Failed to parse definitionSnapshot on instance "${instance._id}"`);
|
|
726
808
|
}
|
|
727
809
|
}
|
|
728
810
|
function collectWatchRefs(instance) {
|
|
729
811
|
const stage = findOpenStageEntry(instance);
|
|
730
812
|
return [
|
|
731
|
-
gdrRef(instance.workflowResource, instance._id, instance._type),
|
|
813
|
+
gdrRef({ res: instance.workflowResource, documentId: instance._id, type: instance._type }),
|
|
732
814
|
...instance.ancestors,
|
|
733
815
|
...entryDocRefs(instance.fields),
|
|
734
816
|
...entryDocRefs(stage?.fields),
|
|
@@ -786,13 +868,13 @@ async function hydrateSnapshot(args) {
|
|
|
786
868
|
loaded.push(held), visited.add(uri);
|
|
787
869
|
return;
|
|
788
870
|
}
|
|
789
|
-
const fetched = await loadByGdr(
|
|
790
|
-
client,
|
|
871
|
+
const fetched = await loadByGdr({
|
|
872
|
+
defaultClient: client,
|
|
791
873
|
clientForGdr,
|
|
792
|
-
instance.workflowResource,
|
|
874
|
+
defaultResource: instance.workflowResource,
|
|
793
875
|
uri,
|
|
794
876
|
perspective
|
|
795
|
-
);
|
|
877
|
+
});
|
|
796
878
|
fetched && (loaded.push(fetched), visited.add(uri));
|
|
797
879
|
};
|
|
798
880
|
loaded.push({ doc: instance, resource: instance.workflowResource }), visited.add(selfGdr(instance));
|
|
@@ -803,23 +885,28 @@ async function hydrateSnapshot(args) {
|
|
|
803
885
|
);
|
|
804
886
|
return buildSnapshot({ docs: loaded });
|
|
805
887
|
}
|
|
806
|
-
async function loadByGdr(
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
888
|
+
async function loadByGdr({
|
|
889
|
+
defaultClient,
|
|
890
|
+
clientForGdr,
|
|
891
|
+
defaultResource,
|
|
892
|
+
uri,
|
|
893
|
+
perspective
|
|
894
|
+
}) {
|
|
895
|
+
const parsed = tryParseGdr(uri);
|
|
896
|
+
if (parsed === void 0) {
|
|
897
|
+
const doc2 = await readDoc({ client: defaultClient, id: uri, perspective });
|
|
812
898
|
return doc2 ? { doc: doc2, resource: defaultResource } : null;
|
|
813
899
|
}
|
|
814
|
-
const routed = clientForGdr(parsed), doc = await readDoc(routed, parsed.documentId, perspective);
|
|
900
|
+
const routed = clientForGdr(parsed), doc = await readDoc({ client: routed, id: parsed.documentId, perspective });
|
|
815
901
|
return doc ? { doc, resource: resourceFromParsed(parsed) } : null;
|
|
816
902
|
}
|
|
817
|
-
async function readDoc(
|
|
903
|
+
async function readDoc({
|
|
904
|
+
client,
|
|
905
|
+
id,
|
|
906
|
+
perspective
|
|
907
|
+
}) {
|
|
818
908
|
return perspective === "raw" ? await client.getDocument(id) ?? null : await client.fetch("*[_id == $id][0]", { id }, { perspective }) ?? null;
|
|
819
909
|
}
|
|
820
|
-
function resourceFromParsed(parsed) {
|
|
821
|
-
return parsed.scheme === "dataset" ? { type: "dataset", id: `${parsed.projectId}.${parsed.dataset}` } : { type: parsed.scheme, id: parsed.resourceId ?? "" };
|
|
822
|
-
}
|
|
823
910
|
function collectEntryDocUris(resolvedFieldEntries) {
|
|
824
911
|
return entryDocRefs(resolvedFieldEntries).map((ref) => ref.id);
|
|
825
912
|
}
|
|
@@ -841,13 +928,13 @@ async function guardsForInstance(args) {
|
|
|
841
928
|
const { client, clientForGdr, instance } = args, stateUris = [
|
|
842
929
|
...collectEntryDocUris(instance.fields),
|
|
843
930
|
...instance.stages.flatMap((stage) => collectEntryDocUris(stage.fields))
|
|
844
|
-
], clients = resourceClientMap(
|
|
845
|
-
instance.workflowResource,
|
|
846
|
-
client,
|
|
931
|
+
], clients = resourceClientMap({
|
|
932
|
+
base: instance.workflowResource,
|
|
933
|
+
baseClient: client,
|
|
847
934
|
clientForGdr,
|
|
848
|
-
stateUris.map((uri) => tryParseGdr(uri))
|
|
849
|
-
), { query, params } = instanceGuardQuery(instance._id);
|
|
850
|
-
return queryGuardsAcross(clients.values(), query, params);
|
|
935
|
+
gdrs: stateUris.map((uri) => tryParseGdr(uri))
|
|
936
|
+
}), { query, params } = instanceGuardQuery(instance._id);
|
|
937
|
+
return queryGuardsAcross({ clients: clients.values(), query, params });
|
|
851
938
|
}
|
|
852
939
|
async function guardsForDefinition(args) {
|
|
853
940
|
const perClient = await guardsForDefinitionByClient(args);
|
|
@@ -855,8 +942,13 @@ async function guardsForDefinition(args) {
|
|
|
855
942
|
}
|
|
856
943
|
async function guardsForDefinitionByClient(args) {
|
|
857
944
|
const { client, clientForGdr, workflowResource, definition, definitions } = args, gdrs = definitions.flatMap(
|
|
858
|
-
(def) => guardIdRefs(def).map(({ idRef, stage }) => staticIdRefGdr(idRef, stage, def))
|
|
859
|
-
), clients = resourceClientMap(
|
|
945
|
+
(def) => guardIdRefs(def).map(({ idRef, stage }) => staticIdRefGdr({ idRef, stage, definition: def }))
|
|
946
|
+
), clients = resourceClientMap({
|
|
947
|
+
base: workflowResource,
|
|
948
|
+
baseClient: client,
|
|
949
|
+
clientForGdr,
|
|
950
|
+
gdrs
|
|
951
|
+
}), query = "*[_type == $t && sourceDefinition == $definition]", params = { t: GUARD_DOC_TYPE, definition };
|
|
860
952
|
return Promise.all(
|
|
861
953
|
[...clients.values()].map(async (resourceClient) => ({
|
|
862
954
|
client: resourceClient,
|
|
@@ -871,7 +963,11 @@ function guardIdRefs(definition) {
|
|
|
871
963
|
)
|
|
872
964
|
);
|
|
873
965
|
}
|
|
874
|
-
function staticIdRefGdr(
|
|
966
|
+
function staticIdRefGdr({
|
|
967
|
+
idRef,
|
|
968
|
+
stage,
|
|
969
|
+
definition
|
|
970
|
+
}) {
|
|
875
971
|
const read = /^\$fields\.([\w-]+)/.exec(idRef);
|
|
876
972
|
if (read === null) return;
|
|
877
973
|
const seed = entriesInScope(stage, definition).find((e) => e.name === read[1])?.initialValue;
|
|
@@ -891,14 +987,12 @@ function gdrFromValue(value) {
|
|
|
891
987
|
return typeof id == "string" ? tryParseGdr(id) : void 0;
|
|
892
988
|
}
|
|
893
989
|
}
|
|
894
|
-
function
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
}
|
|
901
|
-
function resourceClientMap(base, baseClient, clientForGdr, gdrs) {
|
|
990
|
+
function resourceClientMap({
|
|
991
|
+
base,
|
|
992
|
+
baseClient,
|
|
993
|
+
clientForGdr,
|
|
994
|
+
gdrs
|
|
995
|
+
}) {
|
|
902
996
|
const map = /* @__PURE__ */ new Map([[resourceKey(base), baseClient]]);
|
|
903
997
|
for (const parsed of gdrs) {
|
|
904
998
|
if (parsed === void 0) continue;
|
|
@@ -907,7 +1001,11 @@ function resourceClientMap(base, baseClient, clientForGdr, gdrs) {
|
|
|
907
1001
|
}
|
|
908
1002
|
return map;
|
|
909
1003
|
}
|
|
910
|
-
async function queryGuardsAcross(
|
|
1004
|
+
async function queryGuardsAcross({
|
|
1005
|
+
clients,
|
|
1006
|
+
query,
|
|
1007
|
+
params
|
|
1008
|
+
}) {
|
|
911
1009
|
const perResource = await Promise.all(
|
|
912
1010
|
[...clients].map((c) => c.fetch(query, params))
|
|
913
1011
|
);
|
|
@@ -917,7 +1015,12 @@ function dedupById(guards) {
|
|
|
917
1015
|
return [...new Map(guards.map((g) => [g._id, g])).values()].sort((a, b) => a._id.localeCompare(b._id));
|
|
918
1016
|
}
|
|
919
1017
|
const SYNC_COMMIT = { visibility: "sync" }, GUARD_OWNER = "robot:workflow-engine", GUARD_LIFTED_PREDICATE = "true";
|
|
920
|
-
function resolveGuard(
|
|
1018
|
+
function resolveGuard({
|
|
1019
|
+
guard,
|
|
1020
|
+
instance,
|
|
1021
|
+
stageName,
|
|
1022
|
+
now
|
|
1023
|
+
}) {
|
|
921
1024
|
const ctx = { instance, now }, targets = resolveIdRefTargets(guard.match.idRefs, ctx);
|
|
922
1025
|
if (targets === null) return null;
|
|
923
1026
|
const resource = assertSingleResource(targets), types = resolveMatchTypes(targets, guard.match.types);
|
|
@@ -952,7 +1055,12 @@ async function upsertGuard(client, doc) {
|
|
|
952
1055
|
function resolvedStageGuards(args) {
|
|
953
1056
|
const stage = args.definition.stages.find((s) => s.name === args.stageName), out = [];
|
|
954
1057
|
for (const guard of stage?.guards ?? []) {
|
|
955
|
-
const resolved = resolveGuard(
|
|
1058
|
+
const resolved = resolveGuard({
|
|
1059
|
+
guard,
|
|
1060
|
+
instance: args.instance,
|
|
1061
|
+
stageName: args.stageName,
|
|
1062
|
+
now: args.now
|
|
1063
|
+
});
|
|
956
1064
|
resolved !== null && out.push({ client: args.clientForGdr(resolved.routeGdr), doc: resolved.doc });
|
|
957
1065
|
}
|
|
958
1066
|
return out;
|
|
@@ -1001,13 +1109,16 @@ function randomKey(length = 12) {
|
|
|
1001
1109
|
const bytes = new Uint8Array(length);
|
|
1002
1110
|
return globalThis.crypto.getRandomValues(bytes), [...bytes].map((b) => b.toString(16).padStart(2, "0")).join("").slice(0, length);
|
|
1003
1111
|
}
|
|
1112
|
+
function instanceDocId(tag) {
|
|
1113
|
+
return `${tag}.wf-instance.${randomKey()}`;
|
|
1114
|
+
}
|
|
1004
1115
|
function isUnevaluable(result) {
|
|
1005
1116
|
return result == null;
|
|
1006
1117
|
}
|
|
1007
1118
|
async function evaluateConditionOutcome(args) {
|
|
1008
1119
|
const { condition, snapshot, params } = args;
|
|
1009
1120
|
if (condition === void 0) return "satisfied";
|
|
1010
|
-
const result = await runGroq(condition, params, snapshot);
|
|
1121
|
+
const result = await runGroq({ groq: condition, params, snapshot });
|
|
1011
1122
|
return isUnevaluable(result) ? "unevaluable" : result ? "satisfied" : "unsatisfied";
|
|
1012
1123
|
}
|
|
1013
1124
|
async function evaluateCondition(args) {
|
|
@@ -1016,7 +1127,7 @@ async function evaluateCondition(args) {
|
|
|
1016
1127
|
async function evaluatePredicates(args) {
|
|
1017
1128
|
const out = {};
|
|
1018
1129
|
for (const [name, groq] of Object.entries(args.predicates ?? {})) {
|
|
1019
|
-
const result = await runGroq(groq, args.params, args.snapshot);
|
|
1130
|
+
const result = await runGroq({ groq, params: args.params, snapshot: args.snapshot });
|
|
1020
1131
|
out[name] = isUnevaluable(result) ? null : !!result;
|
|
1021
1132
|
}
|
|
1022
1133
|
return out;
|
|
@@ -1027,7 +1138,11 @@ async function evaluateRequirements(args) {
|
|
|
1027
1138
|
await evaluateCondition({ condition, snapshot: args.snapshot, params: args.params }) || unmet.push(name);
|
|
1028
1139
|
return unmet;
|
|
1029
1140
|
}
|
|
1030
|
-
async function runGroq(
|
|
1141
|
+
async function runGroq({
|
|
1142
|
+
groq,
|
|
1143
|
+
params,
|
|
1144
|
+
snapshot
|
|
1145
|
+
}) {
|
|
1031
1146
|
const tree = parse(groq, { params });
|
|
1032
1147
|
return (await evaluate(tree, { dataset: snapshot.docs, params })).get();
|
|
1033
1148
|
}
|
|
@@ -1157,15 +1272,19 @@ function derivePerspectiveFromFields(entries) {
|
|
|
1157
1272
|
async function resolveDeclaredFields(args) {
|
|
1158
1273
|
const { entryDefs, initialFields, ctx, randomKey: randomKey2 } = args;
|
|
1159
1274
|
if (entryDefs === void 0 || entryDefs.length === 0) return [];
|
|
1160
|
-
assertRequiredInputProvided(entryDefs, initialFields, ctx.definitionName);
|
|
1275
|
+
assertRequiredInputProvided({ entryDefs, initialFields, definitionName: ctx.definitionName });
|
|
1161
1276
|
const out = [];
|
|
1162
1277
|
for (const entry of entryDefs) {
|
|
1163
1278
|
const scopedCtx = { ...ctx, resolvedFields: out };
|
|
1164
|
-
out.push(await resolveOneEntry(entry, initialFields, scopedCtx, randomKey2));
|
|
1279
|
+
out.push(await resolveOneEntry({ entry, initialFields, ctx: scopedCtx, randomKey: randomKey2 }));
|
|
1165
1280
|
}
|
|
1166
1281
|
return out;
|
|
1167
1282
|
}
|
|
1168
|
-
function assertRequiredInputProvided(
|
|
1283
|
+
function assertRequiredInputProvided({
|
|
1284
|
+
entryDefs,
|
|
1285
|
+
initialFields,
|
|
1286
|
+
definitionName
|
|
1287
|
+
}) {
|
|
1169
1288
|
const missing = entryDefs.filter((entry) => entry.required === !0 && entry.initialValue?.type === "input").filter((entry) => {
|
|
1170
1289
|
const match = initialFields.find((s) => s.name === entry.name && s.type === entry.type);
|
|
1171
1290
|
return match === void 0 || match.value === void 0 || match.value === null;
|
|
@@ -1180,15 +1299,29 @@ const ARRAY_SLOT_TYPES = /* @__PURE__ */ new Set(["doc.refs", "array", "assignee
|
|
|
1180
1299
|
function defaultEntryValue(entryType) {
|
|
1181
1300
|
return ARRAY_SLOT_TYPES.has(entryType) ? [] : null;
|
|
1182
1301
|
}
|
|
1183
|
-
function resolveInputValue(
|
|
1302
|
+
function resolveInputValue({
|
|
1303
|
+
entry,
|
|
1304
|
+
initialFields,
|
|
1305
|
+
defaultValue
|
|
1306
|
+
}) {
|
|
1184
1307
|
const initMatch = initialFields.find((s) => s.name === entry.name && s.type === entry.type);
|
|
1185
1308
|
return initMatch === void 0 ? defaultValue : (assertInputValueShape(entry, initMatch.value), initMatch.value);
|
|
1186
1309
|
}
|
|
1187
|
-
function resolveFieldReadValue(
|
|
1310
|
+
function resolveFieldReadValue({
|
|
1311
|
+
source,
|
|
1312
|
+
ctx,
|
|
1313
|
+
defaultValue
|
|
1314
|
+
}) {
|
|
1188
1315
|
const target = (source.scope === "workflow" ? ctx.workflowFields ?? [] : ctx.resolvedFields ?? []).find((s) => s.name === source.field), targetValue = target === void 0 ? void 0 : target.value;
|
|
1189
1316
|
return (source.path !== void 0 ? getPath(targetValue, source.path) : targetValue) ?? defaultValue;
|
|
1190
1317
|
}
|
|
1191
|
-
async function resolveQueryValue(
|
|
1318
|
+
async function resolveQueryValue({
|
|
1319
|
+
entry,
|
|
1320
|
+
source,
|
|
1321
|
+
ctx,
|
|
1322
|
+
client,
|
|
1323
|
+
defaultValue
|
|
1324
|
+
}) {
|
|
1192
1325
|
const params = paramsForLake({
|
|
1193
1326
|
self: ctx.selfId ?? null,
|
|
1194
1327
|
fields: fieldMapFromResolved(ctx.resolvedFields ?? []),
|
|
@@ -1199,19 +1332,30 @@ async function resolveQueryValue(entry, source, ctx, client, defaultValue) {
|
|
|
1199
1332
|
});
|
|
1200
1333
|
try {
|
|
1201
1334
|
const fetchOptions = { perspective: ctx.perspective ?? DEFAULT_CONTENT_PERSPECTIVE }, result = await client.fetch(source.query, params, fetchOptions);
|
|
1202
|
-
return normalizeQueryResult(
|
|
1335
|
+
return normalizeQueryResult({
|
|
1336
|
+
entryType: entry.type,
|
|
1337
|
+
raw: result,
|
|
1338
|
+
workflowResource: ctx.workflowResource
|
|
1339
|
+
}) ?? defaultValue;
|
|
1203
1340
|
} catch (err) {
|
|
1204
|
-
|
|
1205
|
-
`Failed to resolve query entry "${entry.name}" (${entry.type}): ${err instanceof Error ? err.message : String(err)}`,
|
|
1206
|
-
{ cause: err }
|
|
1207
|
-
);
|
|
1341
|
+
rethrowWithContext(err, `Failed to resolve query entry "${entry.name}" (${entry.type})`);
|
|
1208
1342
|
}
|
|
1209
1343
|
}
|
|
1210
|
-
async function resolveEntryValue(
|
|
1344
|
+
async function resolveEntryValue({
|
|
1345
|
+
entry,
|
|
1346
|
+
initialFields,
|
|
1347
|
+
ctx,
|
|
1348
|
+
defaultValue
|
|
1349
|
+
}) {
|
|
1211
1350
|
const source = entry.initialValue;
|
|
1212
|
-
return source === void 0 ? defaultValue : source.type === "input" ? resolveInputValue(entry, initialFields, defaultValue) : source.type === "literal" ? source.value ?? defaultValue : source.type === "fieldRead" ? resolveFieldReadValue(source, ctx, defaultValue) : source.type === "query" && ctx.client !== void 0 ? resolveQueryValue(entry, source, ctx, ctx.client, defaultValue) : defaultValue;
|
|
1213
|
-
}
|
|
1214
|
-
function buildResolvedEntry(
|
|
1351
|
+
return source === void 0 ? defaultValue : source.type === "input" ? resolveInputValue({ entry, initialFields, defaultValue }) : source.type === "literal" ? source.value ?? defaultValue : source.type === "fieldRead" ? resolveFieldReadValue({ source, ctx, defaultValue }) : source.type === "query" && ctx.client !== void 0 ? resolveQueryValue({ entry, source, ctx, client: ctx.client, defaultValue }) : defaultValue;
|
|
1352
|
+
}
|
|
1353
|
+
function buildResolvedEntry({
|
|
1354
|
+
entry,
|
|
1355
|
+
value,
|
|
1356
|
+
_key,
|
|
1357
|
+
now
|
|
1358
|
+
}) {
|
|
1215
1359
|
return {
|
|
1216
1360
|
_key,
|
|
1217
1361
|
_type: entry.type,
|
|
@@ -1224,8 +1368,13 @@ function buildResolvedEntry(entry, value, _key, now) {
|
|
|
1224
1368
|
...entry.type === "array" ? { of: entry.of ?? [] } : {}
|
|
1225
1369
|
};
|
|
1226
1370
|
}
|
|
1227
|
-
async function resolveOneEntry(
|
|
1228
|
-
|
|
1371
|
+
async function resolveOneEntry({
|
|
1372
|
+
entry,
|
|
1373
|
+
initialFields,
|
|
1374
|
+
ctx,
|
|
1375
|
+
randomKey: randomKey2
|
|
1376
|
+
}) {
|
|
1377
|
+
const defaultValue = defaultEntryValue(entry.type), value = await resolveEntryValue({ entry, initialFields, ctx, defaultValue });
|
|
1229
1378
|
if (entry.initialValue?.type === "query") {
|
|
1230
1379
|
const issues = checkFieldValue({
|
|
1231
1380
|
entryType: entry.type,
|
|
@@ -1233,7 +1382,7 @@ async function resolveOneEntry(entry, initialFields, ctx, randomKey2) {
|
|
|
1233
1382
|
fields: entry.fields,
|
|
1234
1383
|
of: entry.of
|
|
1235
1384
|
});
|
|
1236
|
-
return issues !== void 0 ? (ctx.recordDiscard?.({ field: entry.name, detail: issues.join("; ") }), buildResolvedEntry(entry, defaultValue, randomKey2(), ctx.now)) : buildResolvedEntry(entry, value, randomKey2(), ctx.now);
|
|
1385
|
+
return issues !== void 0 ? (ctx.recordDiscard?.({ field: entry.name, detail: issues.join("; ") }), buildResolvedEntry({ entry, value: defaultValue, _key: randomKey2(), now: ctx.now })) : buildResolvedEntry({ entry, value, _key: randomKey2(), now: ctx.now });
|
|
1237
1386
|
}
|
|
1238
1387
|
return validateFieldValue({
|
|
1239
1388
|
entryType: entry.type,
|
|
@@ -1241,7 +1390,7 @@ async function resolveOneEntry(entry, initialFields, ctx, randomKey2) {
|
|
|
1241
1390
|
value,
|
|
1242
1391
|
fields: entry.fields,
|
|
1243
1392
|
of: entry.of
|
|
1244
|
-
}), buildResolvedEntry(entry, value, randomKey2(), ctx.now);
|
|
1393
|
+
}), buildResolvedEntry({ entry, value, _key: randomKey2(), now: ctx.now });
|
|
1245
1394
|
}
|
|
1246
1395
|
function fieldMapFromResolved(entries) {
|
|
1247
1396
|
const out = {};
|
|
@@ -1286,35 +1435,57 @@ function assertGdrShape(value, context) {
|
|
|
1286
1435
|
`Invalid GDR for ${context}: \`type\` (schema name) must be a non-empty string. Got ${JSON.stringify(v2.type)}.`
|
|
1287
1436
|
);
|
|
1288
1437
|
}
|
|
1289
|
-
function normalizeQueryResult(
|
|
1438
|
+
function normalizeQueryResult({
|
|
1439
|
+
entryType,
|
|
1440
|
+
raw,
|
|
1441
|
+
workflowResource
|
|
1442
|
+
}) {
|
|
1290
1443
|
return raw == null ? raw : entryType === "doc.ref" ? coerceToGdr(raw, workflowResource) : entryType === "doc.refs" ? Array.isArray(raw) ? raw.map((item) => coerceToGdr(item, workflowResource)).filter((v2) => v2 !== null) : [] : raw;
|
|
1291
1444
|
}
|
|
1292
|
-
function toGdrUri(docId, workflowResource) {
|
|
1293
|
-
return isGdrUri(docId) ? docId : gdrFromResource(workflowResource, docId);
|
|
1294
|
-
}
|
|
1295
1445
|
function coerceGdrShape(raw, workflowResource) {
|
|
1296
1446
|
if (!("id" in raw) || !("type" in raw)) return null;
|
|
1297
1447
|
const r = raw;
|
|
1298
|
-
return typeof r.id != "string" || typeof r.type != "string" ? null : isGdrUri(r.id) ? { id: r.id, type: r.type } : workflowResource ? { id:
|
|
1448
|
+
return typeof r.id != "string" || typeof r.type != "string" ? null : isGdrUri(r.id) ? { id: r.id, type: r.type } : workflowResource ? { id: toPhysicalGdr(r.id, workflowResource), type: r.type } : null;
|
|
1299
1449
|
}
|
|
1300
1450
|
function coerceRefEnvelope(raw, workflowResource) {
|
|
1301
1451
|
if (!("_ref" in raw)) return null;
|
|
1302
1452
|
const r = raw;
|
|
1303
|
-
return typeof r._ref != "string" || !workflowResource ? null : { id:
|
|
1453
|
+
return typeof r._ref != "string" || !workflowResource ? null : { id: toPhysicalGdr(r._ref, workflowResource), type: "document" };
|
|
1304
1454
|
}
|
|
1305
1455
|
function coerceToGdr(raw, workflowResource) {
|
|
1306
|
-
return raw == null ? null : typeof raw == "object" ? coerceGdrShape(raw, workflowResource) ?? coerceRefEnvelope(raw, workflowResource) : typeof raw == "string" && workflowResource ? { id:
|
|
1307
|
-
}
|
|
1308
|
-
function
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1456
|
+
return raw == null ? null : typeof raw == "object" ? coerceGdrShape(raw, workflowResource) ?? coerceRefEnvelope(raw, workflowResource) : typeof raw == "string" && workflowResource ? { id: toPhysicalGdr(raw, workflowResource), type: "document" } : null;
|
|
1457
|
+
}
|
|
1458
|
+
function loadCallContext({
|
|
1459
|
+
client,
|
|
1460
|
+
instanceId,
|
|
1461
|
+
options
|
|
1462
|
+
}) {
|
|
1463
|
+
return loadContext({
|
|
1464
|
+
client,
|
|
1465
|
+
instanceId,
|
|
1466
|
+
options: {
|
|
1467
|
+
...options?.clock ? { clock: options.clock } : {},
|
|
1468
|
+
...options?.clientForGdr ? { clientForGdr: options.clientForGdr } : {}
|
|
1469
|
+
}
|
|
1315
1470
|
});
|
|
1316
1471
|
}
|
|
1317
|
-
async function
|
|
1472
|
+
async function retryOnRevisionConflict(args) {
|
|
1473
|
+
const { client, instanceId, options, commit, onExhausted } = args;
|
|
1474
|
+
for (let attempt = 1; attempt <= CONCURRENT_FIRE_ACTION_MAX_ATTEMPTS; attempt++) {
|
|
1475
|
+
const ctx = await loadCallContext({ client, instanceId, options });
|
|
1476
|
+
try {
|
|
1477
|
+
return await commit(ctx);
|
|
1478
|
+
} catch (error) {
|
|
1479
|
+
if (!isRevisionConflict(error)) throw error;
|
|
1480
|
+
}
|
|
1481
|
+
}
|
|
1482
|
+
throw onExhausted();
|
|
1483
|
+
}
|
|
1484
|
+
async function loadContext({
|
|
1485
|
+
client,
|
|
1486
|
+
instanceId,
|
|
1487
|
+
options
|
|
1488
|
+
}) {
|
|
1318
1489
|
const instance = await client.getDocument(instanceId);
|
|
1319
1490
|
if (!instance)
|
|
1320
1491
|
throw new Error(`Workflow instance ${instanceId} not found`);
|
|
@@ -1329,12 +1500,21 @@ async function loadContext(client, instanceId, options) {
|
|
|
1329
1500
|
async function ctxConditionParams(ctx, opts) {
|
|
1330
1501
|
const base = buildParams({ instance: ctx.instance, now: ctx.now, snapshot: ctx.snapshot }), fields = {
|
|
1331
1502
|
...base.fields,
|
|
1332
|
-
...scopedFieldOverlay(
|
|
1503
|
+
...scopedFieldOverlay({
|
|
1504
|
+
instance: ctx.instance,
|
|
1505
|
+
snapshot: ctx.snapshot,
|
|
1506
|
+
activityName: opts?.activityName
|
|
1507
|
+
})
|
|
1333
1508
|
}, params = {
|
|
1334
1509
|
...base,
|
|
1335
1510
|
fields,
|
|
1336
1511
|
actor: opts?.actor,
|
|
1337
|
-
assigned: opts?.activityName !== void 0 ? assignedFor(
|
|
1512
|
+
assigned: opts?.activityName !== void 0 ? assignedFor({
|
|
1513
|
+
instance: ctx.instance,
|
|
1514
|
+
activityName: opts.activityName,
|
|
1515
|
+
actor: opts?.actor,
|
|
1516
|
+
roleAliases: ctx.definition.roleAliases
|
|
1517
|
+
}) : !1,
|
|
1338
1518
|
...opts?.vars
|
|
1339
1519
|
};
|
|
1340
1520
|
return { ...await evaluatePredicates({
|
|
@@ -1343,15 +1523,23 @@ async function ctxConditionParams(ctx, opts) {
|
|
|
1343
1523
|
params
|
|
1344
1524
|
}), ...params };
|
|
1345
1525
|
}
|
|
1346
|
-
async function ctxEvaluateConditionOutcome(
|
|
1526
|
+
async function ctxEvaluateConditionOutcome({
|
|
1527
|
+
ctx,
|
|
1528
|
+
condition,
|
|
1529
|
+
opts
|
|
1530
|
+
}) {
|
|
1347
1531
|
return condition === void 0 ? "satisfied" : evaluateConditionOutcome({
|
|
1348
1532
|
condition,
|
|
1349
1533
|
snapshot: ctx.snapshot,
|
|
1350
1534
|
params: await ctxConditionParams(ctx, opts)
|
|
1351
1535
|
});
|
|
1352
1536
|
}
|
|
1353
|
-
async function ctxEvaluateCondition(
|
|
1354
|
-
|
|
1537
|
+
async function ctxEvaluateCondition({
|
|
1538
|
+
ctx,
|
|
1539
|
+
condition,
|
|
1540
|
+
opts
|
|
1541
|
+
}) {
|
|
1542
|
+
return await ctxEvaluateConditionOutcome({ ctx, condition, opts }) === "satisfied";
|
|
1355
1543
|
}
|
|
1356
1544
|
async function buildEngineContext(args) {
|
|
1357
1545
|
const { client, clientForGdr, instance, definition } = args, clock = args.clock ?? wallClock;
|
|
@@ -1415,10 +1603,14 @@ async function resolveActivityFieldEntries(args) {
|
|
|
1415
1603
|
async function resolveBindings(args) {
|
|
1416
1604
|
const resolved = {};
|
|
1417
1605
|
for (const [key, groq] of Object.entries(args.bindings ?? {}))
|
|
1418
|
-
resolved[key] = await runGroq(groq, args.params, args.snapshot);
|
|
1606
|
+
resolved[key] = await runGroq({ groq, params: args.params, snapshot: args.snapshot });
|
|
1419
1607
|
return { ...resolved, ...args.staticInput };
|
|
1420
1608
|
}
|
|
1421
|
-
async function reload(
|
|
1609
|
+
async function reload({
|
|
1610
|
+
client,
|
|
1611
|
+
instanceId,
|
|
1612
|
+
tag
|
|
1613
|
+
}) {
|
|
1422
1614
|
const doc = await client.getDocument(instanceId);
|
|
1423
1615
|
if (!doc)
|
|
1424
1616
|
throw new Error(`Workflow instance ${instanceId} not found`);
|
|
@@ -1527,13 +1719,25 @@ async function persist(ctx, mutation) {
|
|
|
1527
1719
|
const actorForPriming = void 0;
|
|
1528
1720
|
for (const body of pendingCreates)
|
|
1529
1721
|
try {
|
|
1530
|
-
await primeInitialStage(
|
|
1531
|
-
ctx.client,
|
|
1532
|
-
body._id,
|
|
1533
|
-
actorForPriming,
|
|
1534
|
-
ctx.clientForGdr,
|
|
1535
|
-
ctx.clock
|
|
1536
|
-
), await
|
|
1722
|
+
await primeInitialStage({
|
|
1723
|
+
client: ctx.client,
|
|
1724
|
+
instanceId: body._id,
|
|
1725
|
+
actor: actorForPriming,
|
|
1726
|
+
clientForGdr: ctx.clientForGdr,
|
|
1727
|
+
clock: ctx.clock
|
|
1728
|
+
}), await cascadeAutoTransitions({
|
|
1729
|
+
client: ctx.client,
|
|
1730
|
+
instanceId: body._id,
|
|
1731
|
+
actor: actorForPriming,
|
|
1732
|
+
clientForGdr: ctx.clientForGdr,
|
|
1733
|
+
clock: ctx.clock
|
|
1734
|
+
}), await propagateToAncestors({
|
|
1735
|
+
client: ctx.client,
|
|
1736
|
+
instanceId: body._id,
|
|
1737
|
+
actor: actorForPriming,
|
|
1738
|
+
clientForGdr: ctx.clientForGdr,
|
|
1739
|
+
clock: ctx.clock
|
|
1740
|
+
});
|
|
1537
1741
|
} catch (cause) {
|
|
1538
1742
|
throw cause instanceof WorkflowStateDivergedError ? cause : new WorkflowStateDivergedError({
|
|
1539
1743
|
instanceId: ctx.instance._id,
|
|
@@ -1577,159 +1781,16 @@ function findCurrentStageEntry(instance) {
|
|
|
1577
1781
|
function findCurrentActivities(instance) {
|
|
1578
1782
|
return findCurrentStageEntry(instance)?.activities ?? [];
|
|
1579
1783
|
}
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
try {
|
|
1591
|
-
let patch = args.client.patch(args.instanceId).set(args.restore);
|
|
1592
|
-
args.unset && args.unset.length > 0 && (patch = patch.unset(args.unset)), args.committedRev !== void 0 && (patch = patch.ifRevisionId(args.committedRev)), await patch.commit(SYNC_COMMIT);
|
|
1593
|
-
} catch (rollbackError) {
|
|
1594
|
-
throw new WorkflowStateDivergedError({
|
|
1595
|
-
instanceId: args.instanceId,
|
|
1596
|
-
guardError,
|
|
1597
|
-
rollbackError,
|
|
1598
|
-
reason: "rollback of the committed move failed"
|
|
1599
|
-
});
|
|
1600
|
-
}
|
|
1601
|
-
throw guardError instanceof PartialGuardDeployError ? new WorkflowStateDivergedError({
|
|
1602
|
-
instanceId: args.instanceId,
|
|
1603
|
-
guardError,
|
|
1604
|
-
reason: "a multi-guard deploy partially applied; the rolled-back move left orphaned guard locks"
|
|
1605
|
-
}) : guardError;
|
|
1606
|
-
}
|
|
1607
|
-
}
|
|
1608
|
-
async function persistThenDeploy(ctx, mutation, deploy) {
|
|
1609
|
-
const spawned = mutation.pendingCreates.length > 0, committed = await persist(ctx, mutation);
|
|
1610
|
-
await deployOrRollback({
|
|
1611
|
-
client: ctx.client,
|
|
1612
|
-
instanceId: ctx.instance._id,
|
|
1613
|
-
committedRev: committed._rev,
|
|
1614
|
-
restore: instanceStateFields(ctx.instance),
|
|
1615
|
-
unset: ctx.instance.completedAt === void 0 ? ["completedAt"] : [],
|
|
1616
|
-
reversible: !spawned,
|
|
1617
|
-
deploy
|
|
1618
|
-
});
|
|
1619
|
-
}
|
|
1620
|
-
async function refreshStageGuards(ctx, mutation, stageName) {
|
|
1621
|
-
await deployStageGuards({
|
|
1622
|
-
client: ctx.client,
|
|
1623
|
-
clientForGdr: ctx.clientForGdr,
|
|
1624
|
-
instance: materializeInstance(ctx.instance, mutation),
|
|
1625
|
-
definition: ctx.definition,
|
|
1626
|
-
stageName,
|
|
1627
|
-
now: ctx.now
|
|
1628
|
-
});
|
|
1629
|
-
}
|
|
1630
|
-
async function completeEffect(args) {
|
|
1631
|
-
const { client, instanceId, effectKey, status, outputs, detail, error, durationMs, options } = args, ctx = await loadContext(client, instanceId, {
|
|
1632
|
-
...options?.clock ? { clock: options.clock } : {},
|
|
1633
|
-
...options?.clientForGdr ? { clientForGdr: options.clientForGdr } : {}
|
|
1634
|
-
});
|
|
1635
|
-
return commitCompleteEffect(
|
|
1636
|
-
ctx,
|
|
1637
|
-
effectKey,
|
|
1638
|
-
status,
|
|
1639
|
-
outputs,
|
|
1640
|
-
detail,
|
|
1641
|
-
error,
|
|
1642
|
-
durationMs,
|
|
1643
|
-
options?.actor
|
|
1644
|
-
);
|
|
1645
|
-
}
|
|
1646
|
-
function buildEffectHistoryEntry(pending, outcome) {
|
|
1647
|
-
const { status, ranAt, actor, detail, error, durationMs, outputs } = outcome, resolvedActor = actor ?? pending.actor;
|
|
1648
|
-
return {
|
|
1649
|
-
_key: pending._key,
|
|
1650
|
-
name: pending.name,
|
|
1651
|
-
...pending.title !== void 0 ? { title: pending.title } : {},
|
|
1652
|
-
...pending.description !== void 0 ? { description: pending.description } : {},
|
|
1653
|
-
params: pending.params,
|
|
1654
|
-
origin: pending.origin,
|
|
1655
|
-
...resolvedActor !== void 0 ? { actor: resolvedActor } : {},
|
|
1656
|
-
ranAt,
|
|
1657
|
-
...durationMs !== void 0 ? { durationMs } : {},
|
|
1658
|
-
status,
|
|
1659
|
-
...detail !== void 0 ? { detail } : {},
|
|
1660
|
-
...error !== void 0 ? { error } : {},
|
|
1661
|
-
...outputs !== void 0 ? { outputs } : {}
|
|
1662
|
-
};
|
|
1663
|
-
}
|
|
1664
|
-
function upsertEffectsContext(mutation, effectName, outputs) {
|
|
1665
|
-
const existingIndex = mutation.effectsContext.findIndex((e) => e.name === effectName), entry = effectsContextJsonEntry(effectName, outputs);
|
|
1666
|
-
existingIndex >= 0 ? mutation.effectsContext[existingIndex] = entry : mutation.effectsContext.push(entry);
|
|
1667
|
-
}
|
|
1668
|
-
async function commitCompleteEffect(ctx, effectKey, status, outputs, detail, error, durationMs, actor) {
|
|
1669
|
-
const pending = ctx.instance.pendingEffects.find((e) => e._key === effectKey);
|
|
1670
|
-
if (pending === void 0)
|
|
1671
|
-
throw new Error(`Pending effect "${effectKey}" not found on instance ${ctx.instance._id}`);
|
|
1672
|
-
const mutation = startMutation(ctx.instance);
|
|
1673
|
-
mutation.pendingEffects = mutation.pendingEffects.filter((e) => e._key !== effectKey);
|
|
1674
|
-
const ranAt = ctx.now;
|
|
1675
|
-
mutation.effectHistory.push(
|
|
1676
|
-
buildEffectHistoryEntry(pending, { status, ranAt, actor, detail, error, durationMs, outputs })
|
|
1677
|
-
);
|
|
1678
|
-
const wroteEffectsContext = status === "done" && outputs !== void 0;
|
|
1679
|
-
return wroteEffectsContext && upsertEffectsContext(mutation, pending.name, outputs), mutation.history.push({
|
|
1680
|
-
_key: randomKey(),
|
|
1681
|
-
_type: "effectCompleted",
|
|
1682
|
-
at: ranAt,
|
|
1683
|
-
effectKey,
|
|
1684
|
-
effect: pending.name,
|
|
1685
|
-
status,
|
|
1686
|
-
...outputs !== void 0 ? { outputs } : {},
|
|
1687
|
-
...detail !== void 0 ? { detail } : {},
|
|
1688
|
-
...actor !== void 0 ? { actor } : {}
|
|
1689
|
-
}), await persistThenDeploy(
|
|
1690
|
-
ctx,
|
|
1691
|
-
mutation,
|
|
1692
|
-
() => wroteEffectsContext ? refreshStageGuards(ctx, mutation, ctx.instance.currentStage) : Promise.resolve()
|
|
1693
|
-
), { effectKey, status };
|
|
1694
|
-
}
|
|
1695
|
-
function buildQueuedEffect(effect, origin, params, actor, now) {
|
|
1696
|
-
const key = randomKey(), pending = {
|
|
1697
|
-
_key: key,
|
|
1698
|
-
_type: "pendingEffect",
|
|
1699
|
-
name: effect.name,
|
|
1700
|
-
...effect.title !== void 0 ? { title: effect.title } : {},
|
|
1701
|
-
...effect.description !== void 0 ? { description: effect.description } : {},
|
|
1702
|
-
...effect.bindings !== void 0 ? { bindings: effect.bindings } : {},
|
|
1703
|
-
params,
|
|
1704
|
-
origin,
|
|
1705
|
-
...actor !== void 0 ? { actor } : {},
|
|
1706
|
-
queuedAt: now
|
|
1707
|
-
}, history = {
|
|
1708
|
-
_key: randomKey(),
|
|
1709
|
-
_type: "effectQueued",
|
|
1710
|
-
at: now,
|
|
1711
|
-
effectKey: key,
|
|
1712
|
-
effect: effect.name,
|
|
1713
|
-
origin
|
|
1714
|
-
};
|
|
1715
|
-
return { pending, history };
|
|
1716
|
-
}
|
|
1717
|
-
async function queueEffects(ctx, mutation, effects, origin, actor, opts) {
|
|
1718
|
-
if (!effects || effects.length === 0) return;
|
|
1719
|
-
const now = ctx.now, liveCtx = { ...ctx, instance: materializeInstance(ctx.instance, mutation) }, params = await ctxConditionParams(liveCtx, {
|
|
1720
|
-
...opts?.activityName !== void 0 ? { activityName: opts.activityName } : {},
|
|
1721
|
-
...actor !== void 0 ? { actor } : {},
|
|
1722
|
-
// Caller-supplied action params, readable in bindings as `$params.<name>`.
|
|
1723
|
-
vars: { params: opts?.callerParams ?? {} }
|
|
1724
|
-
});
|
|
1725
|
-
for (const effect of effects) {
|
|
1726
|
-
const resolved = await resolveBindings({
|
|
1727
|
-
bindings: effect.bindings,
|
|
1728
|
-
staticInput: effect.input,
|
|
1729
|
-
snapshot: ctx.snapshot,
|
|
1730
|
-
params
|
|
1731
|
-
}), { pending, history } = buildQueuedEffect(effect, origin, resolved, actor, now);
|
|
1732
|
-
mutation.pendingEffects.push(pending), mutation.history.push(history);
|
|
1784
|
+
function resolveStaticValueExpr(src, ctx) {
|
|
1785
|
+
switch (src.type) {
|
|
1786
|
+
case "literal":
|
|
1787
|
+
return src.value;
|
|
1788
|
+
case "param":
|
|
1789
|
+
return ctx.params?.[src.param];
|
|
1790
|
+
case "actor":
|
|
1791
|
+
return ctx.actor;
|
|
1792
|
+
case "now":
|
|
1793
|
+
return ctx.now;
|
|
1733
1794
|
}
|
|
1734
1795
|
}
|
|
1735
1796
|
function fieldQueryDiscardedEntry(args) {
|
|
@@ -1742,7 +1803,11 @@ function fieldQueryDiscardedEntry(args) {
|
|
|
1742
1803
|
detail: args.detail
|
|
1743
1804
|
};
|
|
1744
1805
|
}
|
|
1745
|
-
function recordFieldDiscards(
|
|
1806
|
+
function recordFieldDiscards({
|
|
1807
|
+
target,
|
|
1808
|
+
scope,
|
|
1809
|
+
at
|
|
1810
|
+
}) {
|
|
1746
1811
|
return (discard) => target.push(fieldQueryDiscardedEntry({ scope, field: discard.field, detail: discard.detail, at }));
|
|
1747
1812
|
}
|
|
1748
1813
|
function applyActivityStatusChange(args) {
|
|
@@ -1790,18 +1855,6 @@ function stageTransitionHistory(args) {
|
|
|
1790
1855
|
}
|
|
1791
1856
|
];
|
|
1792
1857
|
}
|
|
1793
|
-
function resolveStaticValueExpr(src, ctx) {
|
|
1794
|
-
switch (src.type) {
|
|
1795
|
-
case "literal":
|
|
1796
|
-
return src.value;
|
|
1797
|
-
case "param":
|
|
1798
|
-
return ctx.params?.[src.param];
|
|
1799
|
-
case "actor":
|
|
1800
|
-
return ctx.actor;
|
|
1801
|
-
case "now":
|
|
1802
|
-
return ctx.now;
|
|
1803
|
-
}
|
|
1804
|
-
}
|
|
1805
1858
|
const FIELD_OP_TYPES = [
|
|
1806
1859
|
"field.set",
|
|
1807
1860
|
"field.unset",
|
|
@@ -1812,7 +1865,11 @@ const FIELD_OP_TYPES = [
|
|
|
1812
1865
|
function isFieldOp(summary) {
|
|
1813
1866
|
return FIELD_OP_TYPES.includes(summary.opType);
|
|
1814
1867
|
}
|
|
1815
|
-
function validateActionParams(
|
|
1868
|
+
function validateActionParams({
|
|
1869
|
+
action,
|
|
1870
|
+
activityName,
|
|
1871
|
+
callerParams
|
|
1872
|
+
}) {
|
|
1816
1873
|
const declared = action.params ?? [], params = callerParams ?? {}, issues = [];
|
|
1817
1874
|
for (const decl of declared) {
|
|
1818
1875
|
const value = params[decl.name], present = decl.name in params && value !== void 0 && value !== null;
|
|
@@ -1847,7 +1904,7 @@ function checkParamType(value, decl) {
|
|
|
1847
1904
|
case "doc.ref":
|
|
1848
1905
|
return hasStringField(value, "id");
|
|
1849
1906
|
case "doc.refs":
|
|
1850
|
-
return Array.isArray(value) && value.every((
|
|
1907
|
+
return Array.isArray(value) && value.every((ref) => checkParamType(ref, { type: "doc.ref" }));
|
|
1851
1908
|
case "json":
|
|
1852
1909
|
return !0;
|
|
1853
1910
|
default:
|
|
@@ -1883,12 +1940,27 @@ function opAppliedEntry(args) {
|
|
|
1883
1940
|
...origin.action !== void 0 ? { action: origin.action } : {},
|
|
1884
1941
|
...origin.transition !== void 0 ? { transition: origin.transition } : {},
|
|
1885
1942
|
...origin.edit === !0 ? { edit: !0 } : {},
|
|
1943
|
+
...origin.effect !== void 0 ? { effect: origin.effect } : {},
|
|
1886
1944
|
opType: summary.opType,
|
|
1887
1945
|
...summary.target !== void 0 ? { target: summary.target } : {},
|
|
1888
1946
|
...summary.resolved !== void 0 ? { resolved: summary.resolved } : {},
|
|
1889
1947
|
...actor !== void 0 ? { actor } : {}
|
|
1890
1948
|
};
|
|
1891
1949
|
}
|
|
1950
|
+
function validateEffectOps(ops, effectName) {
|
|
1951
|
+
const result = v.safeParse(v.array(StoredFieldOpSchema), ops);
|
|
1952
|
+
if (!result.success)
|
|
1953
|
+
throw new EffectOpsInvalidError({ effect: effectName, issues: formatIssues(result.issues) });
|
|
1954
|
+
const activityScoped = result.output.find((op) => op.target.scope === "activity");
|
|
1955
|
+
if (activityScoped !== void 0)
|
|
1956
|
+
throw new EffectOpsInvalidError({
|
|
1957
|
+
effect: effectName,
|
|
1958
|
+
issues: [
|
|
1959
|
+
`${activityScoped.type} targets an activity-scope field \u2014 an effect's completion ops write workflow- or stage-scope fields`
|
|
1960
|
+
]
|
|
1961
|
+
});
|
|
1962
|
+
return result.output;
|
|
1963
|
+
}
|
|
1892
1964
|
function applyOp(op, ctx) {
|
|
1893
1965
|
switch (op.type) {
|
|
1894
1966
|
case "field.set":
|
|
@@ -1944,7 +2016,7 @@ function applyFieldUpdateWhere(op, ctx) {
|
|
|
1944
2016
|
return setEntryValue(
|
|
1945
2017
|
entry,
|
|
1946
2018
|
rows.map(
|
|
1947
|
-
(row) => evalOpPredicate(op.where, row, ctx) ? { ...row, ...merge } : row
|
|
2019
|
+
(row) => evalOpPredicate({ p: op.where, row, ctx }) ? { ...row, ...merge } : row
|
|
1948
2020
|
)
|
|
1949
2021
|
), { opType: op.type, target: op.target, resolved: { merge } };
|
|
1950
2022
|
}
|
|
@@ -1952,7 +2024,7 @@ function applyFieldRemoveWhere(op, ctx) {
|
|
|
1952
2024
|
const entry = locateEntry(ctx, op.target), rows = requireArrayValue(entry, op);
|
|
1953
2025
|
return setEntryValue(
|
|
1954
2026
|
entry,
|
|
1955
|
-
rows.filter((row) => !evalOpPredicate(op.where, row, ctx))
|
|
2027
|
+
rows.filter((row) => !evalOpPredicate({ p: op.where, row, ctx }))
|
|
1956
2028
|
), { opType: op.type, target: op.target };
|
|
1957
2029
|
}
|
|
1958
2030
|
function requireArrayValue(entry, op) {
|
|
@@ -1963,7 +2035,7 @@ function requireArrayValue(entry, op) {
|
|
|
1963
2035
|
return entry.value;
|
|
1964
2036
|
}
|
|
1965
2037
|
function applyStatusSet(op, ctx) {
|
|
1966
|
-
const entry =
|
|
2038
|
+
const entry = findCurrentActivityEntry(ctx.mutation, op.activity);
|
|
1967
2039
|
if (entry === void 0)
|
|
1968
2040
|
throw new Error(
|
|
1969
2041
|
`status.set targets activity "${op.activity}" which has no entry in the open stage`
|
|
@@ -2018,24 +2090,247 @@ function resolveOpValue(src, ctx) {
|
|
|
2018
2090
|
}
|
|
2019
2091
|
}
|
|
2020
2092
|
}
|
|
2021
|
-
function entryValue(entries, name) {
|
|
2022
|
-
return entries?.find((s) => s.name === name)?.value;
|
|
2093
|
+
function entryValue(entries, name) {
|
|
2094
|
+
return entries?.find((s) => s.name === name)?.value;
|
|
2095
|
+
}
|
|
2096
|
+
function readEntryFromMutation(ctx, src) {
|
|
2097
|
+
const scopes = src.scope !== void 0 ? [src.scope] : ["stage", "workflow"], stageEntry = findOpenStageEntry(ctx.mutation);
|
|
2098
|
+
for (const scope of scopes) {
|
|
2099
|
+
const host = scope === "workflow" ? ctx.mutation.fields : stageEntry?.fields, value = entryValue(host, src.field);
|
|
2100
|
+
if (value !== void 0) return value;
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
function evalOpPredicate({
|
|
2104
|
+
p,
|
|
2105
|
+
row,
|
|
2106
|
+
ctx
|
|
2107
|
+
}) {
|
|
2108
|
+
switch (p.type) {
|
|
2109
|
+
case "all":
|
|
2110
|
+
return p.of.every((sub) => evalOpPredicate({ p: sub, row, ctx }));
|
|
2111
|
+
case "any":
|
|
2112
|
+
return p.of.some((sub) => evalOpPredicate({ p: sub, row, ctx }));
|
|
2113
|
+
case "field":
|
|
2114
|
+
return row[p.field] === resolveOpValue(p.equals, ctx);
|
|
2115
|
+
}
|
|
2116
|
+
}
|
|
2117
|
+
async function deployOrRollback(args) {
|
|
2118
|
+
try {
|
|
2119
|
+
await args.deploy();
|
|
2120
|
+
} catch (guardError) {
|
|
2121
|
+
if (!args.reversible)
|
|
2122
|
+
throw new WorkflowStateDivergedError({
|
|
2123
|
+
instanceId: args.instanceId,
|
|
2124
|
+
guardError,
|
|
2125
|
+
reason: "the move created child instances a parent-only rollback cannot undo"
|
|
2126
|
+
});
|
|
2127
|
+
try {
|
|
2128
|
+
let patch = args.client.patch(args.instanceId).set(args.restore);
|
|
2129
|
+
args.unset && args.unset.length > 0 && (patch = patch.unset(args.unset)), args.committedRev !== void 0 && (patch = patch.ifRevisionId(args.committedRev)), await patch.commit(SYNC_COMMIT);
|
|
2130
|
+
} catch (rollbackError) {
|
|
2131
|
+
throw new WorkflowStateDivergedError({
|
|
2132
|
+
instanceId: args.instanceId,
|
|
2133
|
+
guardError,
|
|
2134
|
+
rollbackError,
|
|
2135
|
+
reason: "rollback of the committed move failed"
|
|
2136
|
+
});
|
|
2137
|
+
}
|
|
2138
|
+
throw guardError instanceof PartialGuardDeployError ? new WorkflowStateDivergedError({
|
|
2139
|
+
instanceId: args.instanceId,
|
|
2140
|
+
guardError,
|
|
2141
|
+
reason: "a multi-guard deploy partially applied; the rolled-back move left orphaned guard locks"
|
|
2142
|
+
}) : guardError;
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
async function persistThenDeploy({
|
|
2146
|
+
ctx,
|
|
2147
|
+
mutation,
|
|
2148
|
+
deploy
|
|
2149
|
+
}) {
|
|
2150
|
+
const spawned = mutation.pendingCreates.length > 0, committed = await persist(ctx, mutation);
|
|
2151
|
+
await deployOrRollback({
|
|
2152
|
+
client: ctx.client,
|
|
2153
|
+
instanceId: ctx.instance._id,
|
|
2154
|
+
committedRev: committed._rev,
|
|
2155
|
+
restore: instanceStateFields(ctx.instance),
|
|
2156
|
+
unset: ctx.instance.completedAt === void 0 ? ["completedAt"] : [],
|
|
2157
|
+
reversible: !spawned,
|
|
2158
|
+
deploy
|
|
2159
|
+
});
|
|
2160
|
+
}
|
|
2161
|
+
async function persistThenMaybeRefresh({
|
|
2162
|
+
ctx,
|
|
2163
|
+
mutation,
|
|
2164
|
+
stageName,
|
|
2165
|
+
didChangeState
|
|
2166
|
+
}) {
|
|
2167
|
+
await persistThenDeploy({
|
|
2168
|
+
ctx,
|
|
2169
|
+
mutation,
|
|
2170
|
+
deploy: () => didChangeState ? refreshStageGuards({ ctx, mutation, stageName }) : Promise.resolve()
|
|
2171
|
+
});
|
|
2172
|
+
}
|
|
2173
|
+
async function refreshStageGuards({
|
|
2174
|
+
ctx,
|
|
2175
|
+
mutation,
|
|
2176
|
+
stageName
|
|
2177
|
+
}) {
|
|
2178
|
+
await deployStageGuards({
|
|
2179
|
+
client: ctx.client,
|
|
2180
|
+
clientForGdr: ctx.clientForGdr,
|
|
2181
|
+
instance: materializeInstance(ctx.instance, mutation),
|
|
2182
|
+
definition: ctx.definition,
|
|
2183
|
+
stageName,
|
|
2184
|
+
now: ctx.now
|
|
2185
|
+
});
|
|
2186
|
+
}
|
|
2187
|
+
async function completeEffect(args) {
|
|
2188
|
+
const { client, instanceId, effectKey, status, outputs, ops, detail, error, durationMs, options } = args, ctx = await loadCallContext({ client, instanceId, options });
|
|
2189
|
+
return commitCompleteEffect({
|
|
2190
|
+
ctx,
|
|
2191
|
+
effectKey,
|
|
2192
|
+
status,
|
|
2193
|
+
outputs,
|
|
2194
|
+
ops,
|
|
2195
|
+
detail,
|
|
2196
|
+
error,
|
|
2197
|
+
durationMs,
|
|
2198
|
+
actor: options?.actor
|
|
2199
|
+
});
|
|
2200
|
+
}
|
|
2201
|
+
function buildEffectHistoryEntry(pending, outcome) {
|
|
2202
|
+
const { status, ranAt, actor, detail, error, durationMs, outputs } = outcome, resolvedActor = actor ?? pending.actor;
|
|
2203
|
+
return {
|
|
2204
|
+
_key: pending._key,
|
|
2205
|
+
name: pending.name,
|
|
2206
|
+
...pending.title !== void 0 ? { title: pending.title } : {},
|
|
2207
|
+
...pending.description !== void 0 ? { description: pending.description } : {},
|
|
2208
|
+
params: pending.params,
|
|
2209
|
+
origin: pending.origin,
|
|
2210
|
+
...resolvedActor !== void 0 ? { actor: resolvedActor } : {},
|
|
2211
|
+
ranAt,
|
|
2212
|
+
...durationMs !== void 0 ? { durationMs } : {},
|
|
2213
|
+
status,
|
|
2214
|
+
...detail !== void 0 ? { detail } : {},
|
|
2215
|
+
...error !== void 0 ? { error } : {},
|
|
2216
|
+
...outputs !== void 0 ? { outputs } : {}
|
|
2217
|
+
};
|
|
2023
2218
|
}
|
|
2024
|
-
function
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2219
|
+
function upsertEffectsContext({
|
|
2220
|
+
mutation,
|
|
2221
|
+
effectName,
|
|
2222
|
+
outputs
|
|
2223
|
+
}) {
|
|
2224
|
+
const existingIndex = mutation.effectsContext.findIndex((e) => e.name === effectName), entry = effectsContextJsonEntry(effectName, outputs);
|
|
2225
|
+
existingIndex >= 0 ? mutation.effectsContext[existingIndex] = entry : mutation.effectsContext.push(entry);
|
|
2030
2226
|
}
|
|
2031
|
-
function
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2227
|
+
async function commitCompleteEffect({
|
|
2228
|
+
ctx,
|
|
2229
|
+
effectKey,
|
|
2230
|
+
status,
|
|
2231
|
+
outputs,
|
|
2232
|
+
ops,
|
|
2233
|
+
detail,
|
|
2234
|
+
error,
|
|
2235
|
+
durationMs,
|
|
2236
|
+
actor
|
|
2237
|
+
}) {
|
|
2238
|
+
const pending = ctx.instance.pendingEffects.find((e) => e._key === effectKey);
|
|
2239
|
+
if (pending === void 0)
|
|
2240
|
+
throw new Error(`Pending effect "${effectKey}" not found on instance ${ctx.instance._id}`);
|
|
2241
|
+
if (status === "failed" && ops !== void 0 && ops.length > 0)
|
|
2242
|
+
throw new EffectOpsInvalidError({
|
|
2243
|
+
effect: pending.name,
|
|
2244
|
+
issues: [
|
|
2245
|
+
"ops cannot accompany a failed completion \u2014 field.set the outcome on a done completion instead"
|
|
2246
|
+
]
|
|
2247
|
+
});
|
|
2248
|
+
const validatedOps = ops !== void 0 ? validateEffectOps(ops, pending.name) : [], mutation = startMutation(ctx.instance);
|
|
2249
|
+
mutation.pendingEffects = mutation.pendingEffects.filter((e) => e._key !== effectKey);
|
|
2250
|
+
const ranAt = ctx.now;
|
|
2251
|
+
mutation.effectHistory.push(
|
|
2252
|
+
buildEffectHistoryEntry(pending, { status, ranAt, actor, detail, error, durationMs, outputs })
|
|
2253
|
+
);
|
|
2254
|
+
const wroteEffectsContext = status === "done" && outputs !== void 0;
|
|
2255
|
+
wroteEffectsContext && upsertEffectsContext({ mutation, effectName: pending.name, outputs }), mutation.history.push({
|
|
2256
|
+
_key: randomKey(),
|
|
2257
|
+
_type: "effectCompleted",
|
|
2258
|
+
at: ranAt,
|
|
2259
|
+
effectKey,
|
|
2260
|
+
effect: pending.name,
|
|
2261
|
+
status,
|
|
2262
|
+
...outputs !== void 0 ? { outputs } : {},
|
|
2263
|
+
...detail !== void 0 ? { detail } : {},
|
|
2264
|
+
...actor !== void 0 ? { actor } : {}
|
|
2265
|
+
});
|
|
2266
|
+
const ranOps = runOps({
|
|
2267
|
+
ops: validatedOps,
|
|
2268
|
+
mutation,
|
|
2269
|
+
stage: ctx.instance.currentStage,
|
|
2270
|
+
origin: { effect: pending.name },
|
|
2271
|
+
params: pending.params,
|
|
2272
|
+
actor,
|
|
2273
|
+
self: selfGdr(ctx.instance),
|
|
2274
|
+
now: ranAt
|
|
2275
|
+
}), needsGuardRefresh = wroteEffectsContext || ranOps.some(isFieldOp);
|
|
2276
|
+
return await persistThenMaybeRefresh({
|
|
2277
|
+
ctx,
|
|
2278
|
+
mutation,
|
|
2279
|
+
stageName: ctx.instance.currentStage,
|
|
2280
|
+
didChangeState: needsGuardRefresh
|
|
2281
|
+
}), { effectKey, status };
|
|
2282
|
+
}
|
|
2283
|
+
function buildQueuedEffect({
|
|
2284
|
+
effect,
|
|
2285
|
+
origin,
|
|
2286
|
+
params,
|
|
2287
|
+
actor,
|
|
2288
|
+
now
|
|
2289
|
+
}) {
|
|
2290
|
+
const key = randomKey(), pending = {
|
|
2291
|
+
_key: key,
|
|
2292
|
+
_type: "pendingEffect",
|
|
2293
|
+
name: effect.name,
|
|
2294
|
+
...effect.title !== void 0 ? { title: effect.title } : {},
|
|
2295
|
+
...effect.description !== void 0 ? { description: effect.description } : {},
|
|
2296
|
+
...effect.bindings !== void 0 ? { bindings: effect.bindings } : {},
|
|
2297
|
+
params,
|
|
2298
|
+
origin,
|
|
2299
|
+
...actor !== void 0 ? { actor } : {},
|
|
2300
|
+
queuedAt: now
|
|
2301
|
+
}, history = {
|
|
2302
|
+
_key: randomKey(),
|
|
2303
|
+
_type: "effectQueued",
|
|
2304
|
+
at: now,
|
|
2305
|
+
effectKey: key,
|
|
2306
|
+
effect: effect.name,
|
|
2307
|
+
origin
|
|
2308
|
+
};
|
|
2309
|
+
return { pending, history };
|
|
2310
|
+
}
|
|
2311
|
+
async function queueEffects({
|
|
2312
|
+
ctx,
|
|
2313
|
+
mutation,
|
|
2314
|
+
effects,
|
|
2315
|
+
origin,
|
|
2316
|
+
actor,
|
|
2317
|
+
opts
|
|
2318
|
+
}) {
|
|
2319
|
+
if (!effects || effects.length === 0) return;
|
|
2320
|
+
const now = ctx.now, liveCtx = { ...ctx, instance: materializeInstance(ctx.instance, mutation) }, params = await ctxConditionParams(liveCtx, {
|
|
2321
|
+
...opts?.activityName !== void 0 ? { activityName: opts.activityName } : {},
|
|
2322
|
+
...actor !== void 0 ? { actor } : {},
|
|
2323
|
+
// Caller-supplied action params, readable in bindings as `$params.<name>`.
|
|
2324
|
+
vars: { params: opts?.callerParams ?? {} }
|
|
2325
|
+
});
|
|
2326
|
+
for (const effect of effects) {
|
|
2327
|
+
const resolved = await resolveBindings({
|
|
2328
|
+
bindings: effect.bindings,
|
|
2329
|
+
staticInput: effect.input,
|
|
2330
|
+
snapshot: ctx.snapshot,
|
|
2331
|
+
params
|
|
2332
|
+
}), { pending, history } = buildQueuedEffect({ effect, origin, params: resolved, actor, now });
|
|
2333
|
+
mutation.pendingEffects.push(pending), mutation.history.push(history);
|
|
2039
2334
|
}
|
|
2040
2335
|
}
|
|
2041
2336
|
const TAG_RE = /^[a-z0-9][a-z0-9-]*$/;
|
|
@@ -2056,13 +2351,14 @@ async function discoverItems(args) {
|
|
|
2056
2351
|
const { client, groq, params, perspective } = args, fetchOptions = perspective !== void 0 ? { perspective } : void 0;
|
|
2057
2352
|
return client.fetch(groq, paramsForLake(params), fetchOptions);
|
|
2058
2353
|
}
|
|
2059
|
-
function clientForDiscovery(
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2354
|
+
function clientForDiscovery({
|
|
2355
|
+
defaultClient,
|
|
2356
|
+
clientForGdr,
|
|
2357
|
+
subjectGdrUri
|
|
2358
|
+
}) {
|
|
2359
|
+
if (subjectGdrUri === void 0) return defaultClient;
|
|
2360
|
+
const parsed = tryParseGdr(subjectGdrUri);
|
|
2361
|
+
return parsed !== void 0 ? clientForGdr(parsed) : defaultClient;
|
|
2066
2362
|
}
|
|
2067
2363
|
const ENGINE_ACTOR_ID_PREFIX = "engine.";
|
|
2068
2364
|
function engineSystemActor(name) {
|
|
@@ -2075,25 +2371,49 @@ const MAX_SPAWN_DEPTH = 6, SUBWORKFLOWS_ALL_DONE = "count($subworkflows[status !
|
|
|
2075
2371
|
function gateActor(outcome) {
|
|
2076
2372
|
return engineSystemActor(outcome === "failed" ? "failWhen" : "completeWhen");
|
|
2077
2373
|
}
|
|
2374
|
+
function applyGateOutcome(args) {
|
|
2375
|
+
applyActivityStatusChange({
|
|
2376
|
+
entry: args.entry,
|
|
2377
|
+
history: args.history,
|
|
2378
|
+
stage: args.stage,
|
|
2379
|
+
to: args.outcome,
|
|
2380
|
+
at: args.at,
|
|
2381
|
+
actor: gateActor(args.outcome)
|
|
2382
|
+
});
|
|
2383
|
+
}
|
|
2078
2384
|
function effectiveCompleteWhen(activity) {
|
|
2079
2385
|
return activity.completeWhen ?? (activity.subworkflows !== void 0 ? SUBWORKFLOWS_ALL_DONE : void 0);
|
|
2080
2386
|
}
|
|
2081
|
-
async function evaluateResolutionGate(
|
|
2387
|
+
async function evaluateResolutionGate({
|
|
2388
|
+
ctx,
|
|
2389
|
+
activity,
|
|
2390
|
+
vars
|
|
2391
|
+
}) {
|
|
2082
2392
|
const scope = { activityName: activity.name, vars };
|
|
2083
|
-
if (activity.failWhen !== void 0 && await ctxEvaluateCondition(ctx, activity.failWhen, scope))
|
|
2393
|
+
if (activity.failWhen !== void 0 && await ctxEvaluateCondition({ ctx, condition: activity.failWhen, opts: scope }))
|
|
2084
2394
|
return "failed";
|
|
2085
2395
|
const completeWhen = effectiveCompleteWhen(activity);
|
|
2086
|
-
if (completeWhen !== void 0 && await ctxEvaluateCondition(ctx, completeWhen, scope))
|
|
2396
|
+
if (completeWhen !== void 0 && await ctxEvaluateCondition({ ctx, condition: completeWhen, opts: scope }))
|
|
2087
2397
|
return "done";
|
|
2088
2398
|
}
|
|
2089
|
-
async function spawnSubworkflows(
|
|
2399
|
+
async function spawnSubworkflows({
|
|
2400
|
+
ctx,
|
|
2401
|
+
mutation,
|
|
2402
|
+
activity,
|
|
2403
|
+
sub,
|
|
2404
|
+
actor
|
|
2405
|
+
}) {
|
|
2090
2406
|
if (ctx.instance.ancestors.length + 1 > MAX_SPAWN_DEPTH) {
|
|
2091
2407
|
const chain = [...ctx.instance.ancestors.map((a) => a.id), selfGdr(ctx.instance)].join(" \u2192 ");
|
|
2092
2408
|
throw new Error(
|
|
2093
2409
|
`Subworkflow depth limit (${MAX_SPAWN_DEPTH}) exceeded on activity "${activity.name}" of ${ctx.instance._id}. Ancestor chain: ${chain}`
|
|
2094
2410
|
);
|
|
2095
2411
|
}
|
|
2096
|
-
const now = ctx.now, definition = await resolveDefinitionRef(
|
|
2412
|
+
const now = ctx.now, definition = await resolveDefinitionRef({
|
|
2413
|
+
client: ctx.client,
|
|
2414
|
+
ref: sub.definition,
|
|
2415
|
+
tag: ctx.instance.tag
|
|
2416
|
+
});
|
|
2097
2417
|
if (definition === null) {
|
|
2098
2418
|
const versionLabel = sub.definition.version === void 0 || sub.definition.version === "latest" ? "latest" : `v${sub.definition.version}`;
|
|
2099
2419
|
throw new Error(
|
|
@@ -2103,16 +2423,16 @@ async function spawnSubworkflows(ctx, mutation, activity, sub, actor) {
|
|
|
2103
2423
|
const parentScope = await ctxConditionParams(ctx, {
|
|
2104
2424
|
activityName: activity.name,
|
|
2105
2425
|
...actor ? { actor } : {}
|
|
2106
|
-
}), { rows, discoveryResource } = await resolveSpawnRows(ctx, sub), effectsContext = await resolveContextHandoff(ctx, sub, parentScope), refs = [];
|
|
2426
|
+
}), { rows, discoveryResource } = await resolveSpawnRows(ctx, sub), effectsContext = await resolveContextHandoff({ ctx, sub, parentScope }), refs = [];
|
|
2107
2427
|
for (const row of rows) {
|
|
2108
|
-
const initialFields = await projectRowFields(
|
|
2428
|
+
const initialFields = await projectRowFields({
|
|
2109
2429
|
ctx,
|
|
2110
2430
|
sub,
|
|
2111
|
-
definition,
|
|
2431
|
+
childDefinition: definition,
|
|
2112
2432
|
parentScope,
|
|
2113
2433
|
row,
|
|
2114
2434
|
discoveryResource
|
|
2115
|
-
), { ref, body } = await prepareChildInstance({
|
|
2435
|
+
}), { ref, body } = await prepareChildInstance({
|
|
2116
2436
|
client: ctx.client,
|
|
2117
2437
|
parent: ctx.instance,
|
|
2118
2438
|
definition,
|
|
@@ -2129,7 +2449,11 @@ async function resolveSpawnRows(ctx, sub) {
|
|
|
2129
2449
|
const params = buildParams({ instance: ctx.instance, now: ctx.now, snapshot: ctx.snapshot }), groq = sub.forEach;
|
|
2130
2450
|
let value, discoveryResource;
|
|
2131
2451
|
if (groq.includes("*")) {
|
|
2132
|
-
const routingUri = entrySubjectRefs(ctx.instance.fields)[0]?.id, client = clientForDiscovery(
|
|
2452
|
+
const routingUri = entrySubjectRefs(ctx.instance.fields)[0]?.id, client = clientForDiscovery({
|
|
2453
|
+
defaultClient: ctx.client,
|
|
2454
|
+
clientForGdr: ctx.clientForGdr,
|
|
2455
|
+
subjectGdrUri: routingUri
|
|
2456
|
+
});
|
|
2133
2457
|
client !== ctx.client && routingUri !== void 0 && (discoveryResource = resourceFromGdrUri(routingUri)), value = await discoverItems({
|
|
2134
2458
|
client,
|
|
2135
2459
|
groq,
|
|
@@ -2145,7 +2469,14 @@ async function resolveSpawnRows(ctx, sub) {
|
|
|
2145
2469
|
}
|
|
2146
2470
|
return Array.isArray(value) ? { rows: value, discoveryResource } : value == null ? { rows: [], discoveryResource } : { rows: [value], discoveryResource };
|
|
2147
2471
|
}
|
|
2148
|
-
async function projectRowFields(
|
|
2472
|
+
async function projectRowFields({
|
|
2473
|
+
ctx,
|
|
2474
|
+
sub,
|
|
2475
|
+
childDefinition,
|
|
2476
|
+
parentScope,
|
|
2477
|
+
row,
|
|
2478
|
+
discoveryResource
|
|
2479
|
+
}) {
|
|
2149
2480
|
const out = [];
|
|
2150
2481
|
for (const [name, groq] of Object.entries(sub.with ?? {})) {
|
|
2151
2482
|
const declared = (childDefinition.fields ?? []).find((e) => e.name === name);
|
|
@@ -2153,11 +2484,15 @@ async function projectRowFields(ctx, sub, childDefinition, parentScope, row, dis
|
|
|
2153
2484
|
throw new Error(
|
|
2154
2485
|
`subworkflows.with["${name}"]: subworkflow "${childDefinition.name}" declares no field entry "${name}"`
|
|
2155
2486
|
);
|
|
2156
|
-
const raw = await runGroq(groq, { ...parentScope, row }, ctx.snapshot), value = canonicaliseSpawnValue(
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2487
|
+
const raw = await runGroq({ groq, params: { ...parentScope, row }, snapshot: ctx.snapshot }), value = canonicaliseSpawnValue({
|
|
2488
|
+
kind: declared.type,
|
|
2489
|
+
value: raw,
|
|
2490
|
+
cx: {
|
|
2491
|
+
parentResource: ctx.instance.workflowResource,
|
|
2492
|
+
discoveryResource,
|
|
2493
|
+
entryName: name,
|
|
2494
|
+
childName: childDefinition.name
|
|
2495
|
+
}
|
|
2161
2496
|
});
|
|
2162
2497
|
value != null && out.push({
|
|
2163
2498
|
type: declared.type,
|
|
@@ -2167,15 +2502,23 @@ async function projectRowFields(ctx, sub, childDefinition, parentScope, row, dis
|
|
|
2167
2502
|
}
|
|
2168
2503
|
return out;
|
|
2169
2504
|
}
|
|
2170
|
-
async function resolveContextHandoff(
|
|
2505
|
+
async function resolveContextHandoff({
|
|
2506
|
+
ctx,
|
|
2507
|
+
sub,
|
|
2508
|
+
parentScope
|
|
2509
|
+
}) {
|
|
2171
2510
|
const out = {};
|
|
2172
2511
|
for (const [name, groq] of Object.entries(sub.context ?? {})) {
|
|
2173
|
-
const v2 = await runGroq(groq, parentScope, ctx.snapshot);
|
|
2512
|
+
const v2 = await runGroq({ groq, params: parentScope, snapshot: ctx.snapshot });
|
|
2174
2513
|
v2 != null && (typeof v2 == "string" || typeof v2 == "number" || typeof v2 == "boolean" || typeof v2 == "object" && "id" in v2 && "type" in v2) && (out[name] = v2);
|
|
2175
2514
|
}
|
|
2176
2515
|
return out;
|
|
2177
2516
|
}
|
|
2178
|
-
function canonicaliseSpawnValue(
|
|
2517
|
+
function canonicaliseSpawnValue({
|
|
2518
|
+
kind,
|
|
2519
|
+
value,
|
|
2520
|
+
cx
|
|
2521
|
+
}) {
|
|
2179
2522
|
return kind === "doc.ref" ? coerceSpawnGdr(value, cx) : kind === "doc.refs" && Array.isArray(value) ? value.map((v2) => coerceSpawnGdr(v2, cx)).filter((v2) => v2 !== null) : value;
|
|
2180
2523
|
}
|
|
2181
2524
|
function assertBareIdRootsCorrectly(id, cx) {
|
|
@@ -2198,7 +2541,11 @@ function coerceSpawnGdr(raw, cx) {
|
|
|
2198
2541
|
}
|
|
2199
2542
|
return typeof raw == "string" && raw.length > 0 ? { id: toUri(raw), type: "document" } : null;
|
|
2200
2543
|
}
|
|
2201
|
-
async function resolveDefinitionRef(
|
|
2544
|
+
async function resolveDefinitionRef({
|
|
2545
|
+
client,
|
|
2546
|
+
ref,
|
|
2547
|
+
tag
|
|
2548
|
+
}) {
|
|
2202
2549
|
const wantsExplicit = typeof ref.version == "number", params = { definition: ref.name, tag };
|
|
2203
2550
|
return wantsExplicit && (params.version = ref.version), await client.fetch(
|
|
2204
2551
|
definitionLookupGroq(wantsExplicit),
|
|
@@ -2206,7 +2553,7 @@ async function resolveDefinitionRef(client, ref, tag) {
|
|
|
2206
2553
|
) ?? null;
|
|
2207
2554
|
}
|
|
2208
2555
|
async function prepareChildInstance(args) {
|
|
2209
|
-
const { client, parent, definition, initialFields, effectsContext, actor, now } = args, childTag = parent.tag, workflowResource = parent.workflowResource, childDocId =
|
|
2556
|
+
const { client, parent, definition, initialFields, effectsContext, actor, now } = args, childTag = parent.tag, workflowResource = parent.workflowResource, childDocId = instanceDocId(childTag), childRef = { id: gdrFromResource(workflowResource, childDocId), type: WORKFLOW_INSTANCE_TYPE }, ancestors = [
|
|
2210
2557
|
...parent.ancestors,
|
|
2211
2558
|
{
|
|
2212
2559
|
id: selfGdr(parent),
|
|
@@ -2223,7 +2570,7 @@ async function prepareChildInstance(args) {
|
|
|
2223
2570
|
workflowResource,
|
|
2224
2571
|
definitionName: definition.name,
|
|
2225
2572
|
...inheritedPerspective !== void 0 ? { perspective: inheritedPerspective } : {},
|
|
2226
|
-
recordDiscard: recordFieldDiscards(fieldDiscards, "workflow", now)
|
|
2573
|
+
recordDiscard: recordFieldDiscards({ target: fieldDiscards, scope: "workflow", at: now })
|
|
2227
2574
|
},
|
|
2228
2575
|
randomKey
|
|
2229
2576
|
}), childPerspective = derivePerspectiveFromFields(childFields) ?? inheritedPerspective, effectsContextEntries = Object.entries(effectsContext).map(
|
|
@@ -2258,7 +2605,7 @@ async function prepareChildInstance(args) {
|
|
|
2258
2605
|
}
|
|
2259
2606
|
async function subworkflowRows(ctx, refs) {
|
|
2260
2607
|
if (refs.length === 0) return [];
|
|
2261
|
-
const ids = refs.map((r) =>
|
|
2608
|
+
const ids = refs.map((r) => toBareId(r.id));
|
|
2262
2609
|
return (await ctx.client.fetch("*[_id in $ids]{_id, currentStage, completedAt}", { ids })).map((c) => ({
|
|
2263
2610
|
_id: c._id,
|
|
2264
2611
|
stage: c.currentStage,
|
|
@@ -2266,13 +2613,14 @@ async function subworkflowRows(ctx, refs) {
|
|
|
2266
2613
|
}));
|
|
2267
2614
|
}
|
|
2268
2615
|
async function invokeActivity(args) {
|
|
2269
|
-
const { client, instanceId, activity: activityName, options } = args, ctx = await
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2616
|
+
const { client, instanceId, activity: activityName, options } = args, ctx = await loadCallContext({ client, instanceId, options });
|
|
2617
|
+
return commitInvoke({ ctx, activityName, actor: options?.actor });
|
|
2618
|
+
}
|
|
2619
|
+
async function commitInvoke({
|
|
2620
|
+
ctx,
|
|
2621
|
+
activityName,
|
|
2622
|
+
actor
|
|
2623
|
+
}) {
|
|
2276
2624
|
const { activity } = findActivityInCurrentStage(ctx, activityName), entry = findCurrentActivities(ctx.instance).find((t) => t.name === activityName);
|
|
2277
2625
|
if (entry === void 0)
|
|
2278
2626
|
throw new Error(
|
|
@@ -2281,9 +2629,15 @@ async function commitInvoke(ctx, activityName, actor) {
|
|
|
2281
2629
|
if (entry.status !== "pending")
|
|
2282
2630
|
return { invoked: !1, activity: activityName };
|
|
2283
2631
|
const mutation = startMutation(ctx.instance), mutEntry = requireMutationActivityEntry(mutation, activityName);
|
|
2284
|
-
return await activateActivity(ctx, mutation, activity, mutEntry, actor), await persist(ctx, mutation), { invoked: !0, activity: activityName };
|
|
2285
|
-
}
|
|
2286
|
-
async function activateActivity(
|
|
2632
|
+
return await activateActivity({ ctx, mutation, activity, entry: mutEntry, actor }), await persist(ctx, mutation), { invoked: !0, activity: activityName };
|
|
2633
|
+
}
|
|
2634
|
+
async function activateActivity({
|
|
2635
|
+
ctx,
|
|
2636
|
+
mutation,
|
|
2637
|
+
activity,
|
|
2638
|
+
entry,
|
|
2639
|
+
actor
|
|
2640
|
+
}) {
|
|
2287
2641
|
const now = ctx.now;
|
|
2288
2642
|
if (entry.status = "active", entry.startedAt = now, activity.fields !== void 0 && activity.fields.length > 0) {
|
|
2289
2643
|
const stage = findStage(ctx.definition, mutation.currentStage);
|
|
@@ -2293,7 +2647,7 @@ async function activateActivity(ctx, mutation, activity, entry, actor) {
|
|
|
2293
2647
|
stage,
|
|
2294
2648
|
activity,
|
|
2295
2649
|
now,
|
|
2296
|
-
recordDiscard: recordFieldDiscards(mutation.history, "activity", now)
|
|
2650
|
+
recordDiscard: recordFieldDiscards({ target: mutation.history, scope: "activity", at: now })
|
|
2297
2651
|
});
|
|
2298
2652
|
}
|
|
2299
2653
|
runOps({
|
|
@@ -2312,19 +2666,25 @@ async function activateActivity(ctx, mutation, activity, entry, actor) {
|
|
|
2312
2666
|
stage: mutation.currentStage,
|
|
2313
2667
|
activity: activity.name,
|
|
2314
2668
|
...actor !== void 0 ? { actor } : {}
|
|
2315
|
-
}), await queueEffects(
|
|
2669
|
+
}), await queueEffects({
|
|
2316
2670
|
ctx,
|
|
2317
2671
|
mutation,
|
|
2318
|
-
activity.effects,
|
|
2319
|
-
{ kind: "activity", name: activity.name },
|
|
2672
|
+
effects: activity.effects,
|
|
2673
|
+
origin: { kind: "activity", name: activity.name },
|
|
2320
2674
|
actor,
|
|
2321
|
-
{
|
|
2675
|
+
opts: {
|
|
2322
2676
|
activityName: activity.name
|
|
2323
2677
|
}
|
|
2324
|
-
);
|
|
2678
|
+
});
|
|
2325
2679
|
let pendingChildren;
|
|
2326
2680
|
if (activity.subworkflows !== void 0) {
|
|
2327
|
-
const createsBefore = mutation.pendingCreates.length, refs = await spawnSubworkflows(
|
|
2681
|
+
const createsBefore = mutation.pendingCreates.length, refs = await spawnSubworkflows({
|
|
2682
|
+
ctx,
|
|
2683
|
+
mutation,
|
|
2684
|
+
activity,
|
|
2685
|
+
sub: activity.subworkflows,
|
|
2686
|
+
actor
|
|
2687
|
+
});
|
|
2328
2688
|
entry.spawnedInstances = refs, pendingChildren = mutation.pendingCreates.slice(createsBefore).map((c) => ({ _id: c._id, stage: c.currentStage, status: "active" }));
|
|
2329
2689
|
for (const ref of refs)
|
|
2330
2690
|
mutation.history.push({
|
|
@@ -2335,24 +2695,42 @@ async function activateActivity(ctx, mutation, activity, entry, actor) {
|
|
|
2335
2695
|
instanceRef: ref
|
|
2336
2696
|
});
|
|
2337
2697
|
}
|
|
2338
|
-
await autoResolveOnActivate(
|
|
2339
|
-
|
|
2340
|
-
|
|
2698
|
+
await autoResolveOnActivate({
|
|
2699
|
+
ctx,
|
|
2700
|
+
mutation,
|
|
2701
|
+
activity,
|
|
2702
|
+
entry,
|
|
2703
|
+
now,
|
|
2704
|
+
...pendingChildren !== void 0 ? { pendingChildren } : {}
|
|
2705
|
+
}) || resolveMachineStep({ mutation, activity, entry, now });
|
|
2706
|
+
}
|
|
2707
|
+
async function autoResolveOnActivate({
|
|
2708
|
+
ctx,
|
|
2709
|
+
mutation,
|
|
2710
|
+
activity,
|
|
2711
|
+
entry,
|
|
2712
|
+
now,
|
|
2713
|
+
pendingChildren
|
|
2714
|
+
}) {
|
|
2341
2715
|
if (activity.failWhen === void 0 && effectiveCompleteWhen(activity) === void 0) return !1;
|
|
2342
|
-
const vars = pendingChildren !== void 0 ? { subworkflows: pendingChildren } : await activityConditionVars(ctx, entry), outcome = await evaluateResolutionGate(ctx, activity, vars);
|
|
2343
|
-
return outcome === void 0 ? !1 : (
|
|
2716
|
+
const vars = pendingChildren !== void 0 ? { subworkflows: pendingChildren } : await activityConditionVars(ctx, entry), outcome = await evaluateResolutionGate({ ctx, activity, vars });
|
|
2717
|
+
return outcome === void 0 ? !1 : (applyGateOutcome({
|
|
2344
2718
|
entry,
|
|
2345
2719
|
history: mutation.history,
|
|
2346
2720
|
stage: mutation.currentStage,
|
|
2347
|
-
to: outcome,
|
|
2348
2721
|
at: now,
|
|
2349
|
-
|
|
2722
|
+
outcome
|
|
2350
2723
|
}), !0);
|
|
2351
2724
|
}
|
|
2352
2725
|
async function activityConditionVars(ctx, entry) {
|
|
2353
2726
|
return entry.spawnedInstances === void 0 || entry.spawnedInstances.length === 0 ? { subworkflows: [] } : { subworkflows: await subworkflowRows(ctx, entry.spawnedInstances) };
|
|
2354
2727
|
}
|
|
2355
|
-
function resolveMachineStep(
|
|
2728
|
+
function resolveMachineStep({
|
|
2729
|
+
mutation,
|
|
2730
|
+
activity,
|
|
2731
|
+
entry,
|
|
2732
|
+
now
|
|
2733
|
+
}) {
|
|
2356
2734
|
const waitsForActor = activity.actions !== void 0 && activity.actions.length > 0, waitsForCondition = activity.completeWhen !== void 0 || activity.subworkflows !== void 0;
|
|
2357
2735
|
waitsForActor || waitsForCondition || applyActivityStatusChange({
|
|
2358
2736
|
entry,
|
|
@@ -2366,19 +2744,25 @@ function resolveMachineStep(mutation, activity, entry, now) {
|
|
|
2366
2744
|
async function buildStageActivities(ctx, stage) {
|
|
2367
2745
|
const entries = [];
|
|
2368
2746
|
for (const activity of stage.activities ?? []) {
|
|
2369
|
-
const outcome = await ctxEvaluateConditionOutcome(
|
|
2370
|
-
|
|
2747
|
+
const outcome = await ctxEvaluateConditionOutcome({
|
|
2748
|
+
ctx,
|
|
2749
|
+
condition: activity.filter,
|
|
2750
|
+
opts: { activityName: activity.name }
|
|
2371
2751
|
}), inScope = outcome !== "unsatisfied";
|
|
2372
2752
|
entries.push({
|
|
2373
2753
|
_key: randomKey(),
|
|
2374
2754
|
name: activity.name,
|
|
2375
2755
|
status: inScope ? "pending" : "skipped",
|
|
2376
|
-
...activity.filter !== void 0 ? { filterEvaluation: buildFilterEvaluation(ctx.now, activity.filter, outcome) } : {}
|
|
2756
|
+
...activity.filter !== void 0 ? { filterEvaluation: buildFilterEvaluation({ at: ctx.now, filter: activity.filter, outcome }) } : {}
|
|
2377
2757
|
});
|
|
2378
2758
|
}
|
|
2379
2759
|
return entries;
|
|
2380
2760
|
}
|
|
2381
|
-
function buildFilterEvaluation(
|
|
2761
|
+
function buildFilterEvaluation({
|
|
2762
|
+
at,
|
|
2763
|
+
filter,
|
|
2764
|
+
outcome
|
|
2765
|
+
}) {
|
|
2382
2766
|
return outcome === "unevaluable" ? {
|
|
2383
2767
|
at,
|
|
2384
2768
|
truthy: !1,
|
|
@@ -2390,13 +2774,16 @@ function isTerminalStage(stage) {
|
|
|
2390
2774
|
return (stage.transitions ?? []).length === 0;
|
|
2391
2775
|
}
|
|
2392
2776
|
async function setStage(args) {
|
|
2393
|
-
const { client, instanceId, targetStage, reason, options } = args, ctx = await
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2777
|
+
const { client, instanceId, targetStage, reason, options } = args, ctx = await loadCallContext({ client, instanceId, options });
|
|
2778
|
+
return commitSetStage({ ctx, targetStage, reason, actor: options?.actor });
|
|
2779
|
+
}
|
|
2780
|
+
async function enterStage({
|
|
2781
|
+
ctx,
|
|
2782
|
+
mutation,
|
|
2783
|
+
nextStage,
|
|
2784
|
+
actor,
|
|
2785
|
+
at
|
|
2786
|
+
}) {
|
|
2400
2787
|
mutation.currentStage = nextStage.name;
|
|
2401
2788
|
const nextStageEntry = {
|
|
2402
2789
|
_key: randomKey(),
|
|
@@ -2407,7 +2794,7 @@ async function enterStage(ctx, mutation, nextStage, actor, at) {
|
|
|
2407
2794
|
instance: ctx.instance,
|
|
2408
2795
|
stage: nextStage,
|
|
2409
2796
|
now: at,
|
|
2410
|
-
recordDiscard: recordFieldDiscards(mutation.history, "stage", at)
|
|
2797
|
+
recordDiscard: recordFieldDiscards({ target: mutation.history, scope: "stage", at })
|
|
2411
2798
|
}),
|
|
2412
2799
|
activities: await buildStageActivities(ctx, nextStage)
|
|
2413
2800
|
};
|
|
@@ -2415,14 +2802,19 @@ async function enterStage(ctx, mutation, nextStage, actor, at) {
|
|
|
2415
2802
|
for (const activity of nextStage.activities ?? []) {
|
|
2416
2803
|
if (activity.activation !== "auto") continue;
|
|
2417
2804
|
const entry = nextStageEntry.activities.find((t) => t.name === activity.name);
|
|
2418
|
-
entry && entry.status === "pending" && await activateActivity(ctx, mutation, activity, entry, actor);
|
|
2805
|
+
entry && entry.status === "pending" && await activateActivity({ ctx, mutation, activity, entry, actor });
|
|
2419
2806
|
}
|
|
2420
2807
|
isTerminalStage(nextStage) && (mutation.completedAt = at);
|
|
2421
2808
|
}
|
|
2422
2809
|
function isTerminal(ctx) {
|
|
2423
2810
|
return ctx.instance.completedAt !== void 0;
|
|
2424
2811
|
}
|
|
2425
|
-
async function commitSetStage(
|
|
2812
|
+
async function commitSetStage({
|
|
2813
|
+
ctx,
|
|
2814
|
+
targetStage,
|
|
2815
|
+
reason,
|
|
2816
|
+
actor
|
|
2817
|
+
}) {
|
|
2426
2818
|
if (isTerminal(ctx))
|
|
2427
2819
|
return { fired: !1 };
|
|
2428
2820
|
const currentStage = findStage(ctx.definition, ctx.instance.currentStage), nextStage = findStage(ctx.definition, targetStage);
|
|
@@ -2441,18 +2833,28 @@ async function commitSetStage(ctx, targetStage, reason, actor) {
|
|
|
2441
2833
|
})
|
|
2442
2834
|
);
|
|
2443
2835
|
const priorEntry = findOpenStageEntry(mutation);
|
|
2444
|
-
return priorEntry !== void 0 && (priorEntry.exitedAt = at), await enterStage(ctx, mutation, nextStage, actor, at), await persistStageMove(
|
|
2836
|
+
return priorEntry !== void 0 && (priorEntry.exitedAt = at), await enterStage({ ctx, mutation, nextStage, actor, at }), await persistStageMove({
|
|
2837
|
+
ctx,
|
|
2838
|
+
mutation,
|
|
2839
|
+
exitedStage: currentStage.name,
|
|
2840
|
+
enteredStage: nextStage.name
|
|
2841
|
+
}), {
|
|
2445
2842
|
fired: !0,
|
|
2446
2843
|
fromStage: currentStage.name,
|
|
2447
2844
|
toStage: nextStage.name,
|
|
2448
2845
|
transition: transitionName
|
|
2449
2846
|
};
|
|
2450
2847
|
}
|
|
2451
|
-
async function persistStageMove(
|
|
2452
|
-
|
|
2848
|
+
async function persistStageMove({
|
|
2849
|
+
ctx,
|
|
2850
|
+
mutation,
|
|
2851
|
+
exitedStage,
|
|
2852
|
+
enteredStage
|
|
2853
|
+
}) {
|
|
2854
|
+
await persistThenDeploy({
|
|
2453
2855
|
ctx,
|
|
2454
2856
|
mutation,
|
|
2455
|
-
() => deployStageGuards({
|
|
2857
|
+
deploy: () => deployStageGuards({
|
|
2456
2858
|
client: ctx.client,
|
|
2457
2859
|
clientForGdr: ctx.clientForGdr,
|
|
2458
2860
|
instance: materializeInstance(ctx.instance, mutation),
|
|
@@ -2460,7 +2862,7 @@ async function persistStageMove(ctx, mutation, exitedStage, enteredStage) {
|
|
|
2460
2862
|
stageName: enteredStage,
|
|
2461
2863
|
now: ctx.now
|
|
2462
2864
|
})
|
|
2463
|
-
), await retractStageGuards({
|
|
2865
|
+
}), await retractStageGuards({
|
|
2464
2866
|
client: ctx.client,
|
|
2465
2867
|
clientForGdr: ctx.clientForGdr,
|
|
2466
2868
|
instance: ctx.instance,
|
|
@@ -2485,16 +2887,16 @@ async function commitTransition(ctx, actor) {
|
|
|
2485
2887
|
actor,
|
|
2486
2888
|
self: selfGdr(ctx.instance),
|
|
2487
2889
|
now: at
|
|
2488
|
-
}), await queueEffects(
|
|
2890
|
+
}), await queueEffects({
|
|
2489
2891
|
ctx,
|
|
2490
2892
|
mutation,
|
|
2491
|
-
transition.effects,
|
|
2492
|
-
{
|
|
2893
|
+
effects: transition.effects,
|
|
2894
|
+
origin: {
|
|
2493
2895
|
kind: "transition",
|
|
2494
2896
|
name: transition.name
|
|
2495
2897
|
},
|
|
2496
2898
|
actor
|
|
2497
|
-
), mutation.history.push(
|
|
2899
|
+
}), mutation.history.push(
|
|
2498
2900
|
...stageTransitionHistory({
|
|
2499
2901
|
fromStage: currentStage.name,
|
|
2500
2902
|
toStage: transition.to,
|
|
@@ -2506,7 +2908,12 @@ async function commitTransition(ctx, actor) {
|
|
|
2506
2908
|
const priorEntry = findOpenStageEntry(mutation);
|
|
2507
2909
|
priorEntry !== void 0 && (priorEntry.exitedAt = at);
|
|
2508
2910
|
const nextStage = findStage(ctx.definition, transition.to);
|
|
2509
|
-
return await enterStage(ctx, mutation, nextStage, actor, at), await persistStageMove(
|
|
2911
|
+
return await enterStage({ ctx, mutation, nextStage, actor, at }), await persistStageMove({
|
|
2912
|
+
ctx,
|
|
2913
|
+
mutation,
|
|
2914
|
+
exitedStage: currentStage.name,
|
|
2915
|
+
enteredStage: nextStage.name
|
|
2916
|
+
}), {
|
|
2510
2917
|
fired: !0,
|
|
2511
2918
|
fromStage: currentStage.name,
|
|
2512
2919
|
toStage: transition.to,
|
|
@@ -2515,20 +2922,30 @@ async function commitTransition(ctx, actor) {
|
|
|
2515
2922
|
}
|
|
2516
2923
|
async function pickTransition(ctx, stage) {
|
|
2517
2924
|
for (const candidate of stage.transitions ?? []) {
|
|
2518
|
-
const outcome = await ctxEvaluateConditionOutcome(ctx, candidate.filter);
|
|
2925
|
+
const outcome = await ctxEvaluateConditionOutcome({ ctx, condition: candidate.filter });
|
|
2519
2926
|
if (outcome === "satisfied") return candidate;
|
|
2520
2927
|
if (outcome === "unevaluable") return;
|
|
2521
2928
|
}
|
|
2522
2929
|
}
|
|
2523
2930
|
async function evaluateAutoTransitions(args) {
|
|
2524
|
-
const { client, instanceId, options, clientForGdr, clock, overlay } = args, ctx = await loadContext(
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2931
|
+
const { client, instanceId, options, clientForGdr, clock, overlay } = args, ctx = await loadContext({
|
|
2932
|
+
client,
|
|
2933
|
+
instanceId,
|
|
2934
|
+
options: {
|
|
2935
|
+
...clientForGdr ? { clientForGdr } : {},
|
|
2936
|
+
...clock ? { clock } : {},
|
|
2937
|
+
...overlay ? { overlay } : {}
|
|
2938
|
+
}
|
|
2528
2939
|
});
|
|
2529
2940
|
return commitTransition(ctx, options?.actor);
|
|
2530
2941
|
}
|
|
2531
|
-
async function primeInitialStage(
|
|
2942
|
+
async function primeInitialStage({
|
|
2943
|
+
client,
|
|
2944
|
+
instanceId,
|
|
2945
|
+
actor,
|
|
2946
|
+
clientForGdr,
|
|
2947
|
+
clock
|
|
2948
|
+
}) {
|
|
2532
2949
|
const instance = await client.getDocument(instanceId);
|
|
2533
2950
|
if (!instance || instance.stages.length > 0) return;
|
|
2534
2951
|
const definition = parseDefinitionSnapshot(instance), stage = definition.stages.find((s) => s.name === instance.currentStage);
|
|
@@ -2548,7 +2965,7 @@ async function primeInitialStage(client, instanceId, actor, clientForGdr, clock)
|
|
|
2548
2965
|
instance,
|
|
2549
2966
|
stage,
|
|
2550
2967
|
now,
|
|
2551
|
-
recordDiscard: recordFieldDiscards(discards, "stage", now)
|
|
2968
|
+
recordDiscard: recordFieldDiscards({ target: discards, scope: "stage", at: now })
|
|
2552
2969
|
}),
|
|
2553
2970
|
activities: await buildStageActivities(ctx, stage)
|
|
2554
2971
|
}, committed = await client.patch(instance._id).set({
|
|
@@ -2573,7 +2990,7 @@ async function primeInitialStage(client, instanceId, actor, clientForGdr, clock)
|
|
|
2573
2990
|
stageName: stage.name,
|
|
2574
2991
|
now
|
|
2575
2992
|
})
|
|
2576
|
-
}), await autoActivatePrimedActivities(
|
|
2993
|
+
}), await autoActivatePrimedActivities({
|
|
2577
2994
|
client,
|
|
2578
2995
|
instanceId,
|
|
2579
2996
|
definition,
|
|
@@ -2581,9 +2998,17 @@ async function primeInitialStage(client, instanceId, actor, clientForGdr, clock)
|
|
|
2581
2998
|
actor,
|
|
2582
2999
|
clientForGdr,
|
|
2583
3000
|
clock
|
|
2584
|
-
);
|
|
3001
|
+
});
|
|
2585
3002
|
}
|
|
2586
|
-
async function autoActivatePrimedActivities(
|
|
3003
|
+
async function autoActivatePrimedActivities({
|
|
3004
|
+
client,
|
|
3005
|
+
instanceId,
|
|
3006
|
+
definition,
|
|
3007
|
+
stage,
|
|
3008
|
+
actor,
|
|
3009
|
+
clientForGdr,
|
|
3010
|
+
clock
|
|
3011
|
+
}) {
|
|
2587
3012
|
const resolvedClientForGdr = clientForGdr ?? (() => client);
|
|
2588
3013
|
for (const activity of stage.activities ?? []) {
|
|
2589
3014
|
if (activity.activation !== "auto") continue;
|
|
@@ -2596,7 +3021,7 @@ async function autoActivatePrimedActivities(client, instanceId, definition, stag
|
|
|
2596
3021
|
definition,
|
|
2597
3022
|
...clock ? { clock } : {}
|
|
2598
3023
|
}), mutation = startMutation(updated), mutEntry = currentActivities(mutation).find((t) => t.name === activity.name);
|
|
2599
|
-
mutEntry && mutEntry.status === "pending" && (await activateActivity(updatedCtx, mutation, activity, mutEntry, actor), await persist(updatedCtx, mutation));
|
|
3024
|
+
mutEntry && mutEntry.status === "pending" && (await activateActivity({ ctx: updatedCtx, mutation, activity, entry: mutEntry, actor }), await persist(updatedCtx, mutation));
|
|
2600
3025
|
}
|
|
2601
3026
|
}
|
|
2602
3027
|
const CASCADE_LIMIT = 100;
|
|
@@ -2605,20 +3030,30 @@ async function gatherAutoResolveCandidates(ctx, activities) {
|
|
|
2605
3030
|
for (const activity of activities) {
|
|
2606
3031
|
const entry = currentActivitiesList.find((e) => e.name === activity.name);
|
|
2607
3032
|
if (entry?.status !== "active") continue;
|
|
2608
|
-
const outcome = await evaluateResolutionGate(
|
|
3033
|
+
const outcome = await evaluateResolutionGate({
|
|
2609
3034
|
ctx,
|
|
2610
3035
|
activity,
|
|
2611
|
-
await activityConditionVars(ctx, entry)
|
|
2612
|
-
);
|
|
3036
|
+
vars: await activityConditionVars(ctx, entry)
|
|
3037
|
+
});
|
|
2613
3038
|
outcome !== void 0 && candidates.push({ activity, outcome });
|
|
2614
3039
|
}
|
|
2615
3040
|
return candidates;
|
|
2616
3041
|
}
|
|
2617
|
-
async function resolveCompleteWhen(
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
3042
|
+
async function resolveCompleteWhen({
|
|
3043
|
+
client,
|
|
3044
|
+
instanceId,
|
|
3045
|
+
clientForGdr,
|
|
3046
|
+
clock,
|
|
3047
|
+
overlay
|
|
3048
|
+
}) {
|
|
3049
|
+
const ctx = await loadContext({
|
|
3050
|
+
client,
|
|
3051
|
+
instanceId,
|
|
3052
|
+
options: {
|
|
3053
|
+
...clientForGdr ? { clientForGdr } : {},
|
|
3054
|
+
...clock ? { clock } : {},
|
|
3055
|
+
...overlay ? { overlay } : {}
|
|
3056
|
+
}
|
|
2622
3057
|
}), stage = findStage(ctx.definition, ctx.instance.currentStage), gatedActivities = (stage.activities ?? []).filter(
|
|
2623
3058
|
(t) => effectiveCompleteWhen(t) !== void 0 || t.failWhen !== void 0
|
|
2624
3059
|
);
|
|
@@ -2629,21 +3064,27 @@ async function resolveCompleteWhen(client, instanceId, clientForGdr, clock, over
|
|
|
2629
3064
|
let count = 0;
|
|
2630
3065
|
for (const { activity, outcome } of candidates) {
|
|
2631
3066
|
const mutEntry = currentActivities(mutation).find((t) => t.name === activity.name);
|
|
2632
|
-
mutEntry === void 0 || mutEntry.status !== "active" || (
|
|
3067
|
+
mutEntry === void 0 || mutEntry.status !== "active" || (applyGateOutcome({
|
|
2633
3068
|
entry: mutEntry,
|
|
2634
3069
|
history: mutation.history,
|
|
2635
3070
|
stage: stage.name,
|
|
2636
|
-
to: outcome,
|
|
2637
3071
|
at: ctx.now,
|
|
2638
|
-
|
|
3072
|
+
outcome
|
|
2639
3073
|
}), count++);
|
|
2640
3074
|
}
|
|
2641
3075
|
return count > 0 && await persist(ctx, mutation), count;
|
|
2642
3076
|
}
|
|
2643
|
-
async function cascadeAutoTransitions(
|
|
3077
|
+
async function cascadeAutoTransitions({
|
|
3078
|
+
client,
|
|
3079
|
+
instanceId,
|
|
3080
|
+
actor,
|
|
3081
|
+
clientForGdr,
|
|
3082
|
+
clock,
|
|
3083
|
+
overlay
|
|
3084
|
+
}) {
|
|
2644
3085
|
let count = 0;
|
|
2645
3086
|
for (; ; ) {
|
|
2646
|
-
if (await resolveCompleteWhen(client, instanceId, clientForGdr, clock, overlay), !(await evaluateAutoTransitions({
|
|
3087
|
+
if (await resolveCompleteWhen({ client, instanceId, clientForGdr, clock, overlay }), !(await evaluateAutoTransitions({
|
|
2647
3088
|
client,
|
|
2648
3089
|
instanceId,
|
|
2649
3090
|
...actor !== void 0 ? { options: { actor } } : {},
|
|
@@ -2655,14 +3096,19 @@ async function cascadeAutoTransitions(client, instanceId, actor, clientForGdr, c
|
|
|
2655
3096
|
throw new CascadeLimitError({ instanceId, limit: CASCADE_LIMIT });
|
|
2656
3097
|
}
|
|
2657
3098
|
}
|
|
2658
|
-
async function findResolvedParentSpawn(
|
|
3099
|
+
async function findResolvedParentSpawn({
|
|
3100
|
+
client,
|
|
3101
|
+
child,
|
|
3102
|
+
clientForGdr,
|
|
3103
|
+
clock
|
|
3104
|
+
}) {
|
|
2659
3105
|
const parentRef = child.ancestors.at(-1);
|
|
2660
3106
|
if (parentRef === void 0) return;
|
|
2661
|
-
const parent = await client.getDocument(
|
|
3107
|
+
const parent = await client.getDocument(toBareId(parentRef.id));
|
|
2662
3108
|
if (!parent || parent._type !== WORKFLOW_INSTANCE_TYPE || typeof parent.definitionSnapshot != "string") return;
|
|
2663
3109
|
const definition = parseDefinitionSnapshot(parent), stage = definition.stages.find((s) => s.name === parent.currentStage);
|
|
2664
3110
|
if (stage === void 0) return;
|
|
2665
|
-
const parentCurrentActivities = findCurrentActivities(parent), activity = (stage.activities ?? []).find((t) => t.subworkflows === void 0 ? !1 : parentCurrentActivities.find((e) => e.name === t.name)?.spawnedInstances?.some((r) =>
|
|
3111
|
+
const parentCurrentActivities = findCurrentActivities(parent), activity = (stage.activities ?? []).find((t) => t.subworkflows === void 0 ? !1 : parentCurrentActivities.find((e) => e.name === t.name)?.spawnedInstances?.some((r) => toBareId(r.id) === child._id) ?? !1);
|
|
2666
3112
|
if (activity?.subworkflows === void 0) return;
|
|
2667
3113
|
const entry = parentCurrentActivities.find((e) => e.name === activity.name);
|
|
2668
3114
|
if (entry === void 0 || entry.status !== "active") return;
|
|
@@ -2672,18 +3118,29 @@ async function findResolvedParentSpawn(client, child, clientForGdr, clock) {
|
|
|
2672
3118
|
instance: parent,
|
|
2673
3119
|
definition,
|
|
2674
3120
|
...clock ? { clock } : {}
|
|
2675
|
-
}), outcome = await evaluateResolutionGate(
|
|
3121
|
+
}), outcome = await evaluateResolutionGate({
|
|
2676
3122
|
ctx,
|
|
2677
3123
|
activity,
|
|
2678
|
-
await activityConditionVars(ctx, entry)
|
|
2679
|
-
);
|
|
3124
|
+
vars: await activityConditionVars(ctx, entry)
|
|
3125
|
+
});
|
|
2680
3126
|
if (outcome !== void 0)
|
|
2681
3127
|
return { parent, definition, stage, activity, outcome };
|
|
2682
3128
|
}
|
|
2683
|
-
async function propagateToAncestors(
|
|
3129
|
+
async function propagateToAncestors({
|
|
3130
|
+
client,
|
|
3131
|
+
instanceId,
|
|
3132
|
+
actor,
|
|
3133
|
+
clientForGdr,
|
|
3134
|
+
clock
|
|
3135
|
+
}) {
|
|
2684
3136
|
const instance = await client.getDocument(instanceId);
|
|
2685
3137
|
if (!instance) return;
|
|
2686
|
-
const resolvedClientForGdr = clientForGdr ?? (() => client), found = await findResolvedParentSpawn(
|
|
3138
|
+
const resolvedClientForGdr = clientForGdr ?? (() => client), found = await findResolvedParentSpawn({
|
|
3139
|
+
client,
|
|
3140
|
+
child: instance,
|
|
3141
|
+
clientForGdr: resolvedClientForGdr,
|
|
3142
|
+
clock
|
|
3143
|
+
});
|
|
2687
3144
|
if (found === void 0) return;
|
|
2688
3145
|
const { parent, definition, stage, activity, outcome } = found, ctx = await buildEngineContext({
|
|
2689
3146
|
client,
|
|
@@ -2692,14 +3149,13 @@ async function propagateToAncestors(client, instanceId, actor, clientForGdr, clo
|
|
|
2692
3149
|
definition,
|
|
2693
3150
|
...clock ? { clock } : {}
|
|
2694
3151
|
}), mutation = startMutation(parent), mutEntry = currentActivities(mutation).find((e) => e.name === activity.name);
|
|
2695
|
-
mutEntry !== void 0 && (
|
|
3152
|
+
mutEntry !== void 0 && (applyGateOutcome({
|
|
2696
3153
|
entry: mutEntry,
|
|
2697
3154
|
history: mutation.history,
|
|
2698
3155
|
stage: stage.name,
|
|
2699
|
-
to: outcome,
|
|
2700
3156
|
at: ctx.now,
|
|
2701
|
-
|
|
2702
|
-
}), await persist(ctx, mutation), await cascadeAutoTransitions(client, parent._id, actor, clientForGdr, clock), await propagateToAncestors(client, parent._id, actor, clientForGdr, clock));
|
|
3157
|
+
outcome
|
|
3158
|
+
}), await persist(ctx, mutation), await cascadeAutoTransitions({ client, instanceId: parent._id, actor, clientForGdr, clock }), await propagateToAncestors({ client, instanceId: parent._id, actor, clientForGdr, clock }));
|
|
2703
3159
|
}
|
|
2704
3160
|
const parsedFilters = /* @__PURE__ */ new Map();
|
|
2705
3161
|
async function matchesFilter(args) {
|
|
@@ -2740,7 +3196,12 @@ async function resolveAccess(client, args = {}) {
|
|
|
2740
3196
|
);
|
|
2741
3197
|
return { actor: overrideActor };
|
|
2742
3198
|
}
|
|
2743
|
-
const actorPromise = overrideActor !== void 0 ? Promise.resolve(overrideActor) : cachedActor(client, requestFn), grantsPromise = resolveGrants(
|
|
3199
|
+
const actorPromise = overrideActor !== void 0 ? Promise.resolve(overrideActor) : cachedActor(client, requestFn), grantsPromise = resolveGrants({
|
|
3200
|
+
overrideGrants,
|
|
3201
|
+
grantsFromPath: args.grantsFromPath,
|
|
3202
|
+
client,
|
|
3203
|
+
requestFn
|
|
3204
|
+
}), [actor, grants] = await Promise.all([actorPromise, grantsPromise]);
|
|
2744
3205
|
if (actor === void 0)
|
|
2745
3206
|
throw new Error(
|
|
2746
3207
|
"workflow: failed to resolve actor from `/users/me`. The client is configured but the endpoint returned no usable identity \u2014 check the token, or pass `access.actor` explicitly."
|
|
@@ -2755,10 +3216,19 @@ function cachedActor(client, requestFn) {
|
|
|
2755
3216
|
});
|
|
2756
3217
|
return actorCache.set(client, pending), pending;
|
|
2757
3218
|
}
|
|
2758
|
-
function resolveGrants(
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
3219
|
+
function resolveGrants({
|
|
3220
|
+
overrideGrants,
|
|
3221
|
+
grantsFromPath,
|
|
3222
|
+
client,
|
|
3223
|
+
requestFn
|
|
3224
|
+
}) {
|
|
3225
|
+
return overrideGrants !== void 0 ? Promise.resolve(overrideGrants) : grantsFromPath !== void 0 ? cachedGrants({ client, requestFn, resourcePath: grantsFromPath }) : Promise.resolve(void 0);
|
|
3226
|
+
}
|
|
3227
|
+
function cachedGrants({
|
|
3228
|
+
client,
|
|
3229
|
+
requestFn,
|
|
3230
|
+
resourcePath
|
|
3231
|
+
}) {
|
|
2762
3232
|
let byPath = grantsCache.get(client);
|
|
2763
3233
|
byPath === void 0 && (byPath = /* @__PURE__ */ new Map(), grantsCache.set(client, byPath));
|
|
2764
3234
|
let cached = byPath.get(resourcePath);
|
|
@@ -2858,9 +3328,7 @@ function slotWindowOpen(instance, slot) {
|
|
|
2858
3328
|
if (instance.completedAt !== void 0) return { open: !1, detail: "instance completed" };
|
|
2859
3329
|
if (instance.abortedAt !== void 0) return { open: !1, detail: "instance aborted" };
|
|
2860
3330
|
if (slot.scope !== "activity") return { open: !0 };
|
|
2861
|
-
const status =
|
|
2862
|
-
(t) => t.name === slot.activity
|
|
2863
|
-
)?.status;
|
|
3331
|
+
const status = findCurrentActivityEntry(instance, slot.activity)?.status;
|
|
2864
3332
|
return status === "active" ? { open: !0 } : { open: !1, detail: `activity "${slot.activity}" is ${status ?? "not active"}` };
|
|
2865
3333
|
}
|
|
2866
3334
|
function readSlotValue(instance, slot) {
|
|
@@ -2895,7 +3363,7 @@ async function evaluateInstance(args) {
|
|
|
2895
3363
|
const { actor, grants } = await resolveAccess(client, {
|
|
2896
3364
|
...args.access !== void 0 ? { override: args.access } : {},
|
|
2897
3365
|
...args.grantsFromPath !== void 0 ? { grantsFromPath: args.grantsFromPath } : {}
|
|
2898
|
-
}), instance = await reload(client, instanceId, tag), definition = parseDefinitionSnapshot(instance), snapshot = await hydrateSnapshot({ client, clientForGdr: resourceClients !== void 0 ? (parsed) => resourceClients(parsed) ?? client : () => client, instance }), guards = await verdictGuardsForInstance(client, instance._id);
|
|
3366
|
+
}), instance = await reload({ client, instanceId, tag }), definition = parseDefinitionSnapshot(instance), snapshot = await hydrateSnapshot({ client, clientForGdr: resourceClients !== void 0 ? (parsed) => resourceClients(parsed) ?? client : () => client, instance }), guards = await verdictGuardsForInstance(client, instance._id);
|
|
2899
3367
|
return evaluateFromSnapshot({
|
|
2900
3368
|
instance,
|
|
2901
3369
|
definition,
|
|
@@ -2912,7 +3380,7 @@ async function evaluateFromSnapshot(args) {
|
|
|
2912
3380
|
throw new Error(
|
|
2913
3381
|
`Instance "${instance._id}" currentStage "${instance.currentStage}" not in definition`
|
|
2914
3382
|
);
|
|
2915
|
-
const scope = await renderScope({ instance, definition, actor, grants, snapshot, now }), currentActivityEntries = findOpenStageEntry(instance)?.activities ?? [], guardDenial = await instanceGuardReason(instance, actor, args.guards), activityEvaluations = [];
|
|
3383
|
+
const scope = await renderScope({ instance, definition, actor, grants, snapshot, now }), currentActivityEntries = findOpenStageEntry(instance)?.activities ?? [], guardDenial = await instanceGuardReason({ instance, actor, guards: args.guards }), activityEvaluations = [];
|
|
2916
3384
|
for (const activity of stage.activities ?? [])
|
|
2917
3385
|
activityEvaluations.push(
|
|
2918
3386
|
await evaluateActivity({
|
|
@@ -3015,7 +3483,7 @@ async function renderScope(args) {
|
|
|
3015
3483
|
...buildParams({ instance, now, snapshot }),
|
|
3016
3484
|
actor,
|
|
3017
3485
|
assigned: !1,
|
|
3018
|
-
can: await advisoryCan(instance, actor, grants)
|
|
3486
|
+
can: await advisoryCan({ instance, actor, grants })
|
|
3019
3487
|
};
|
|
3020
3488
|
return { ...await evaluatePredicates({
|
|
3021
3489
|
predicates: definition.predicates,
|
|
@@ -3023,7 +3491,11 @@ async function renderScope(args) {
|
|
|
3023
3491
|
params
|
|
3024
3492
|
}), ...params };
|
|
3025
3493
|
}
|
|
3026
|
-
async function advisoryCan(
|
|
3494
|
+
async function advisoryCan({
|
|
3495
|
+
instance,
|
|
3496
|
+
actor,
|
|
3497
|
+
grants
|
|
3498
|
+
}) {
|
|
3027
3499
|
if (grants === void 0) return;
|
|
3028
3500
|
const can = {};
|
|
3029
3501
|
for (const permission of DOCUMENT_VALUE_PERMISSIONS)
|
|
@@ -3041,9 +3513,9 @@ function activityScopeFor(args) {
|
|
|
3041
3513
|
...scope,
|
|
3042
3514
|
fields: {
|
|
3043
3515
|
...scope.fields,
|
|
3044
|
-
...scopedFieldOverlay(instance, snapshot, activityName)
|
|
3516
|
+
...scopedFieldOverlay({ instance, snapshot, activityName })
|
|
3045
3517
|
},
|
|
3046
|
-
assigned: assignedFor(instance, activityName, actor, roleAliases)
|
|
3518
|
+
assigned: assignedFor({ instance, activityName, actor, roleAliases })
|
|
3047
3519
|
};
|
|
3048
3520
|
}
|
|
3049
3521
|
async function evaluateActivity(args) {
|
|
@@ -3105,14 +3577,18 @@ async function evaluateAction(args) {
|
|
|
3105
3577
|
stageHasExits,
|
|
3106
3578
|
guardDenial,
|
|
3107
3579
|
requirementsReason
|
|
3108
|
-
} = args, lifecycle = lifecycleReason(instance, status, stageHasExits);
|
|
3580
|
+
} = args, lifecycle = lifecycleReason({ instance, status, stageHasExits });
|
|
3109
3581
|
return lifecycle !== void 0 ? disabled(action, lifecycle) : guardDenial !== void 0 ? disabled(action, guardDenial) : requirementsReason !== void 0 ? disabled(action, requirementsReason) : action.filter !== void 0 && !await evaluateCondition({
|
|
3110
3582
|
condition: action.filter,
|
|
3111
3583
|
snapshot,
|
|
3112
3584
|
params: activityScope
|
|
3113
3585
|
}) ? disabled(action, { kind: "filter-failed", filter: action.filter }) : { action, allowed: !0 };
|
|
3114
3586
|
}
|
|
3115
|
-
async function instanceGuardReason(
|
|
3587
|
+
async function instanceGuardReason({
|
|
3588
|
+
instance,
|
|
3589
|
+
actor,
|
|
3590
|
+
guards
|
|
3591
|
+
}) {
|
|
3116
3592
|
if (guards === void 0 || guards.length === 0) return;
|
|
3117
3593
|
const denied = await instanceWriteDenials({ instance, guards, identity: actor.id });
|
|
3118
3594
|
if (denied.length !== 0)
|
|
@@ -3122,7 +3598,11 @@ async function instanceGuardReason(instance, actor, guards) {
|
|
|
3122
3598
|
detail: denied.map((g) => g.name ?? g._id).join(", ")
|
|
3123
3599
|
};
|
|
3124
3600
|
}
|
|
3125
|
-
function lifecycleReason(
|
|
3601
|
+
function lifecycleReason({
|
|
3602
|
+
instance,
|
|
3603
|
+
status,
|
|
3604
|
+
stageHasExits
|
|
3605
|
+
}) {
|
|
3126
3606
|
if (instance.completedAt !== void 0)
|
|
3127
3607
|
return { kind: "instance-completed", completedAt: instance.completedAt };
|
|
3128
3608
|
if (!stageHasExits)
|
|
@@ -3133,10 +3613,11 @@ function lifecycleReason(instance, status, stageHasExits) {
|
|
|
3133
3613
|
function disabled(action, reason) {
|
|
3134
3614
|
return { action, allowed: !1, disabledReason: reason };
|
|
3135
3615
|
}
|
|
3136
|
-
function
|
|
3137
|
-
|
|
3138
|
-
|
|
3139
|
-
|
|
3616
|
+
async function sortByDependencies({
|
|
3617
|
+
client,
|
|
3618
|
+
definitions,
|
|
3619
|
+
tag
|
|
3620
|
+
}) {
|
|
3140
3621
|
const byName = /* @__PURE__ */ new Map();
|
|
3141
3622
|
for (const def of definitions) {
|
|
3142
3623
|
if (byName.has(def.name))
|
|
@@ -3146,18 +3627,22 @@ async function sortByDependencies(client, definitions, tag) {
|
|
|
3146
3627
|
byName.set(def.name, def);
|
|
3147
3628
|
}
|
|
3148
3629
|
const findInBatch = (ref) => findRefInBatch(byName, ref);
|
|
3149
|
-
return await assertCrossBatchRefsDeployed(client, definitions, { tag, findInBatch }), topoSortDefinitions(definitions, { findInBatch });
|
|
3630
|
+
return await assertCrossBatchRefsDeployed({ client, definitions, ctx: { tag, findInBatch } }), topoSortDefinitions(definitions, { findInBatch });
|
|
3150
3631
|
}
|
|
3151
3632
|
function findRefInBatch(byName, ref) {
|
|
3152
3633
|
if (typeof ref.version != "number")
|
|
3153
3634
|
return byName.get(ref.name);
|
|
3154
3635
|
}
|
|
3155
|
-
async function assertCrossBatchRefsDeployed(
|
|
3636
|
+
async function assertCrossBatchRefsDeployed({
|
|
3637
|
+
client,
|
|
3638
|
+
definitions,
|
|
3639
|
+
ctx
|
|
3640
|
+
}) {
|
|
3156
3641
|
const missing = [];
|
|
3157
3642
|
for (const def of definitions)
|
|
3158
3643
|
for (const ref of refsOf(def)) {
|
|
3159
3644
|
if (ctx.findInBatch(ref) !== void 0) continue;
|
|
3160
|
-
const label = await resolveDeployedRefLabel(client, ref, ctx.tag);
|
|
3645
|
+
const label = await resolveDeployedRefLabel({ client, ref, tag: ctx.tag });
|
|
3161
3646
|
label !== void 0 && missing.push({ from: def.name, ref: label });
|
|
3162
3647
|
}
|
|
3163
3648
|
if (missing.length === 0) return;
|
|
@@ -3168,7 +3653,11 @@ async function assertCrossBatchRefsDeployed(client, definitions, ctx) {
|
|
|
3168
3653
|
`)
|
|
3169
3654
|
);
|
|
3170
3655
|
}
|
|
3171
|
-
async function resolveDeployedRefLabel(
|
|
3656
|
+
async function resolveDeployedRefLabel({
|
|
3657
|
+
client,
|
|
3658
|
+
ref,
|
|
3659
|
+
tag
|
|
3660
|
+
}) {
|
|
3172
3661
|
const wantsExplicit = typeof ref.version == "number", params = { definition: ref.name, tag };
|
|
3173
3662
|
if (wantsExplicit && (params.version = ref.version), !await client.fetch(
|
|
3174
3663
|
definitionLookupGroq(wantsExplicit),
|
|
@@ -3211,9 +3700,17 @@ function hashDefinitionContent(def) {
|
|
|
3211
3700
|
hash ^= BigInt(canonical.charCodeAt(i)), hash = hash * 0x100000001b3n & 0xffffffffffffffffn;
|
|
3212
3701
|
return hash.toString(16).padStart(16, "0");
|
|
3213
3702
|
}
|
|
3214
|
-
function planDefinitionDeploy(
|
|
3215
|
-
|
|
3216
|
-
|
|
3703
|
+
function planDefinitionDeploy({
|
|
3704
|
+
def,
|
|
3705
|
+
latest,
|
|
3706
|
+
target
|
|
3707
|
+
}) {
|
|
3708
|
+
const expanded = expandResourceAliases(def, target.resourceAliases), contentHash = hashDefinitionContent(expanded), unchanged = latest !== void 0 && latest.contentHash === contentHash, version = unchanged ? latest.version : (latest?.version ?? 0) + 1, docId = definitionDocId({
|
|
3709
|
+
tag: target.tag,
|
|
3710
|
+
definition: expanded.name,
|
|
3711
|
+
version
|
|
3712
|
+
}), document = {
|
|
3713
|
+
...expanded,
|
|
3217
3714
|
_id: docId,
|
|
3218
3715
|
_type: WORKFLOW_DEFINITION_TYPE,
|
|
3219
3716
|
tag: target.tag,
|
|
@@ -3222,6 +3719,38 @@ function planDefinitionDeploy(def, latest, target) {
|
|
|
3222
3719
|
};
|
|
3223
3720
|
return { status: unchanged ? "unchanged" : "create", version, contentHash, docId, document };
|
|
3224
3721
|
}
|
|
3722
|
+
const RESOURCE_ALIAS_REF_RE = new RegExp(`@(${RESOURCE_ALIAS_NAME_SOURCE}):`, "g"), STANDALONE_RESOURCE_ALIAS_RE = new RegExp(`^@${RESOURCE_ALIAS_NAME_SOURCE}:`), PROSE_KEYS = /* @__PURE__ */ new Set(["title", "description"]);
|
|
3723
|
+
function expandResourceAliases(definition, resourceAliases) {
|
|
3724
|
+
assertResourceAliasNamesValid(resourceAliases);
|
|
3725
|
+
const map = resourceAliases ?? {};
|
|
3726
|
+
return mapJsonStrings(
|
|
3727
|
+
definition,
|
|
3728
|
+
(value, key) => key !== void 0 && PROSE_KEYS.has(key) ? value : expandAliasString({ value, map, definitionName: definition.name })
|
|
3729
|
+
);
|
|
3730
|
+
}
|
|
3731
|
+
function expandAliasString({
|
|
3732
|
+
value,
|
|
3733
|
+
map,
|
|
3734
|
+
definitionName
|
|
3735
|
+
}) {
|
|
3736
|
+
const expanded = value.replace(RESOURCE_ALIAS_REF_RE, (_match, alias) => {
|
|
3737
|
+
if (!Object.hasOwn(map, alias))
|
|
3738
|
+
throw new Error(
|
|
3739
|
+
`workflow.deployDefinitions: definition "${definitionName}" references unbound resource alias "@${alias}". Bind it in the deploy target's resource map, or remove the reference.`
|
|
3740
|
+
);
|
|
3741
|
+
return gdrResourcePrefix(map[alias]);
|
|
3742
|
+
});
|
|
3743
|
+
if (expanded === value) return value;
|
|
3744
|
+
if (STANDALONE_RESOURCE_ALIAS_RE.test(value) && !isGdrUri(expanded))
|
|
3745
|
+
throw new Error(
|
|
3746
|
+
`workflow.deployDefinitions: definition "${definitionName}" reference "${value}" expands to the malformed GDR "${expanded}". A resource alias reference must be "@<alias>:<documentId>" with a single document id.`
|
|
3747
|
+
);
|
|
3748
|
+
return expanded;
|
|
3749
|
+
}
|
|
3750
|
+
function assertResourceAliasNamesValid(resourceAliases) {
|
|
3751
|
+
for (const name of Object.keys(resourceAliases ?? {}))
|
|
3752
|
+
validateResourceAliasName(name);
|
|
3753
|
+
}
|
|
3225
3754
|
function stripSystemFields(doc) {
|
|
3226
3755
|
const out = {};
|
|
3227
3756
|
for (const [k, v2] of Object.entries(doc))
|
|
@@ -3233,7 +3762,13 @@ function stableStringify(value) {
|
|
|
3233
3762
|
([a], [b]) => a.localeCompare(b)
|
|
3234
3763
|
).map(([k, v2]) => `${JSON.stringify(k)}:${stableStringify(v2)}`).join(",")}}`;
|
|
3235
3764
|
}
|
|
3236
|
-
|
|
3765
|
+
const noDeployedDefinitionMessage = (definition) => `No deployed definition for workflow ${definition}`;
|
|
3766
|
+
async function loadDefinition({
|
|
3767
|
+
client,
|
|
3768
|
+
definition,
|
|
3769
|
+
version,
|
|
3770
|
+
tag
|
|
3771
|
+
}) {
|
|
3237
3772
|
if (version !== void 0) {
|
|
3238
3773
|
const doc = await client.fetch(definitionLookupGroq(!0), {
|
|
3239
3774
|
definition,
|
|
@@ -3244,31 +3779,50 @@ async function loadDefinition(client, definition, version, tag) {
|
|
|
3244
3779
|
throw new Error(`Workflow definition ${definition} v${version} not deployed`);
|
|
3245
3780
|
return doc;
|
|
3246
3781
|
}
|
|
3247
|
-
const latest = await loadLatestDeployed(client, definition, tag);
|
|
3782
|
+
const latest = await loadLatestDeployed({ client, definition, tag });
|
|
3248
3783
|
if (!latest)
|
|
3249
|
-
throw new Error(
|
|
3784
|
+
throw new Error(noDeployedDefinitionMessage(definition));
|
|
3250
3785
|
return latest;
|
|
3251
3786
|
}
|
|
3252
|
-
async function loadLatestDeployed(
|
|
3787
|
+
async function loadLatestDeployed({
|
|
3788
|
+
client,
|
|
3789
|
+
definition,
|
|
3790
|
+
tag
|
|
3791
|
+
}) {
|
|
3253
3792
|
return await client.fetch(definitionLookupGroq(!1), {
|
|
3254
3793
|
definition,
|
|
3255
3794
|
tag
|
|
3256
3795
|
}) ?? void 0;
|
|
3257
3796
|
}
|
|
3258
|
-
async function loadDefinitionVersions(
|
|
3797
|
+
async function loadDefinitionVersions({
|
|
3798
|
+
client,
|
|
3799
|
+
definition,
|
|
3800
|
+
tag
|
|
3801
|
+
}) {
|
|
3259
3802
|
return client.fetch(
|
|
3260
3803
|
`*[_type == "${WORKFLOW_DEFINITION_TYPE}" && name == $definition && ${tagScopeFilter()}] | order(version desc)`,
|
|
3261
3804
|
{ definition, tag }
|
|
3262
3805
|
);
|
|
3263
3806
|
}
|
|
3264
|
-
async function
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3807
|
+
async function loadDefinitionVersionsOrThrow({
|
|
3808
|
+
client,
|
|
3809
|
+
definition,
|
|
3810
|
+
tag
|
|
3811
|
+
}) {
|
|
3812
|
+
const versions = await loadDefinitionVersions({ client, definition, tag });
|
|
3813
|
+
if (versions.length === 0)
|
|
3814
|
+
throw new Error(noDeployedDefinitionMessage(definition));
|
|
3815
|
+
return versions;
|
|
3270
3816
|
}
|
|
3271
|
-
async function
|
|
3817
|
+
async function abortInstance(args) {
|
|
3818
|
+
const { client, instanceId, reason, options } = args, ctx = await loadCallContext({ client, instanceId, options });
|
|
3819
|
+
return commitAbort({ ctx, reason, actor: options?.actor });
|
|
3820
|
+
}
|
|
3821
|
+
async function commitAbort({
|
|
3822
|
+
ctx,
|
|
3823
|
+
reason,
|
|
3824
|
+
actor
|
|
3825
|
+
}) {
|
|
3272
3826
|
if (ctx.instance.completedAt !== void 0)
|
|
3273
3827
|
return { fired: !1 };
|
|
3274
3828
|
const stage = findStage(ctx.definition, ctx.instance.currentStage), mutation = startMutation(ctx.instance), at = ctx.now, openEntry = findOpenStageEntry(mutation);
|
|
@@ -3302,30 +3856,44 @@ async function commitAbort(ctx, reason, actor) {
|
|
|
3302
3856
|
}
|
|
3303
3857
|
async function fireAction(args) {
|
|
3304
3858
|
const { client, instanceId, activity, action, params, options } = args;
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
try {
|
|
3308
|
-
return await commitAction(ctx, activity, action, params, options);
|
|
3309
|
-
} catch (error) {
|
|
3310
|
-
if (!isRevisionConflict(error)) throw error;
|
|
3311
|
-
}
|
|
3312
|
-
}
|
|
3313
|
-
throw new ConcurrentFireActionError({
|
|
3859
|
+
return retryOnRevisionConflict({
|
|
3860
|
+
client,
|
|
3314
3861
|
instanceId,
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3862
|
+
options,
|
|
3863
|
+
commit: (ctx) => commitAction({
|
|
3864
|
+
ctx,
|
|
3865
|
+
activityName: activity,
|
|
3866
|
+
actionName: action,
|
|
3867
|
+
callerParams: params,
|
|
3868
|
+
options
|
|
3869
|
+
}),
|
|
3870
|
+
onExhausted: () => new ConcurrentFireActionError({
|
|
3871
|
+
instanceId,
|
|
3872
|
+
activity,
|
|
3873
|
+
action,
|
|
3874
|
+
attempts: CONCURRENT_FIRE_ACTION_MAX_ATTEMPTS
|
|
3875
|
+
})
|
|
3318
3876
|
});
|
|
3319
3877
|
}
|
|
3320
|
-
async function resolveActionCommit(
|
|
3878
|
+
async function resolveActionCommit({
|
|
3879
|
+
ctx,
|
|
3880
|
+
activityName,
|
|
3881
|
+
actionName,
|
|
3882
|
+
callerParams,
|
|
3883
|
+
options
|
|
3884
|
+
}) {
|
|
3321
3885
|
const actor = options?.actor, { stage, activity } = findActivityInCurrentStage(ctx, activityName), action = (activity.actions ?? []).find((a) => a.name === actionName);
|
|
3322
3886
|
if (action === void 0)
|
|
3323
3887
|
throw new Error(`Action "${actionName}" not declared on activity "${activityName}"`);
|
|
3324
|
-
const can = options?.grants !== void 0 && actor !== void 0 ? await advisoryCan(ctx.instance, actor, options.grants) : void 0;
|
|
3325
|
-
if (!await ctxEvaluateCondition(
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3888
|
+
const can = options?.grants !== void 0 && actor !== void 0 ? await advisoryCan({ instance: ctx.instance, actor, grants: options.grants }) : void 0;
|
|
3889
|
+
if (!await ctxEvaluateCondition({
|
|
3890
|
+
ctx,
|
|
3891
|
+
condition: action.filter,
|
|
3892
|
+
opts: {
|
|
3893
|
+
activityName,
|
|
3894
|
+
...actor !== void 0 ? { actor } : {},
|
|
3895
|
+
...can !== void 0 ? { vars: { can } } : {}
|
|
3896
|
+
}
|
|
3329
3897
|
}))
|
|
3330
3898
|
throw new Error(`Action "${actionName}" filter rejected on activity "${activityName}"`);
|
|
3331
3899
|
const entry = findCurrentActivities(ctx.instance).find((t) => t.name === activityName);
|
|
@@ -3333,17 +3901,23 @@ async function resolveActionCommit(ctx, activityName, actionName, callerParams,
|
|
|
3333
3901
|
throw new Error(
|
|
3334
3902
|
`Activity "${activityName}" must be active to fire action "${actionName}"; status is ${entry?.status ?? "missing"}`
|
|
3335
3903
|
);
|
|
3336
|
-
const params = validateActionParams(action, activityName, callerParams);
|
|
3904
|
+
const params = validateActionParams({ action, activityName, callerParams });
|
|
3337
3905
|
return { stage, activity, action, params };
|
|
3338
3906
|
}
|
|
3339
|
-
async function commitAction(
|
|
3340
|
-
|
|
3907
|
+
async function commitAction({
|
|
3908
|
+
ctx,
|
|
3909
|
+
activityName,
|
|
3910
|
+
actionName,
|
|
3911
|
+
callerParams,
|
|
3912
|
+
options
|
|
3913
|
+
}) {
|
|
3914
|
+
const actor = options?.actor, { stage, action, params } = await resolveActionCommit({
|
|
3341
3915
|
ctx,
|
|
3342
3916
|
activityName,
|
|
3343
3917
|
actionName,
|
|
3344
3918
|
callerParams,
|
|
3345
3919
|
options
|
|
3346
|
-
), mutation = startMutation(ctx.instance), mutEntry = requireMutationActivityEntry(mutation, activityName), now = ctx.now;
|
|
3920
|
+
}), mutation = startMutation(ctx.instance), mutEntry = requireMutationActivityEntry(mutation, activityName), now = ctx.now;
|
|
3347
3921
|
mutation.history.push({
|
|
3348
3922
|
_key: randomKey(),
|
|
3349
3923
|
_type: "actionFired",
|
|
@@ -3363,14 +3937,19 @@ async function commitAction(ctx, activityName, actionName, callerParams, options
|
|
|
3363
3937
|
self: selfGdr(ctx.instance),
|
|
3364
3938
|
now
|
|
3365
3939
|
}), newStatus = mutEntry.status !== statusBefore ? mutEntry.status : void 0;
|
|
3366
|
-
return await queueEffects(
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3940
|
+
return await queueEffects({
|
|
3941
|
+
ctx,
|
|
3942
|
+
mutation,
|
|
3943
|
+
effects: action.effects,
|
|
3944
|
+
origin: { kind: "action", name: actionName },
|
|
3945
|
+
actor,
|
|
3946
|
+
opts: { callerParams: params, activityName }
|
|
3947
|
+
}), await persistThenMaybeRefresh({
|
|
3370
3948
|
ctx,
|
|
3371
3949
|
mutation,
|
|
3372
|
-
|
|
3373
|
-
|
|
3950
|
+
stageName: stage.name,
|
|
3951
|
+
didChangeState: ranOps.some(isFieldOp)
|
|
3952
|
+
}), {
|
|
3374
3953
|
fired: !0,
|
|
3375
3954
|
activity: activityName,
|
|
3376
3955
|
action: actionName,
|
|
@@ -3383,7 +3962,7 @@ class ActionDisabledError extends Error {
|
|
|
3383
3962
|
activity;
|
|
3384
3963
|
action;
|
|
3385
3964
|
constructor(args) {
|
|
3386
|
-
super(formatDisabledReason(args.activity, args.action, args.reason)), this.name = "ActionDisabledError", this.reason = args.reason, this.activity = args.activity, this.action = args.action;
|
|
3965
|
+
super(formatDisabledReason({ activity: args.activity, action: args.action, reason: args.reason })), this.name = "ActionDisabledError", this.reason = args.reason, this.activity = args.activity, this.action = args.action;
|
|
3387
3966
|
}
|
|
3388
3967
|
}
|
|
3389
3968
|
const disabledReasonDetail = {
|
|
@@ -3394,7 +3973,11 @@ const disabledReasonDetail = {
|
|
|
3394
3973
|
"mutation-guard-denied": (r) => `blocked by mutation guard(s) [${r.guardIds.join(", ")}]${r.detail ? ` (${r.detail})` : ""}`,
|
|
3395
3974
|
"requirements-unmet": (r) => `unmet requirement(s): ${r.unmetRequirements.join(", ")}`
|
|
3396
3975
|
};
|
|
3397
|
-
function formatDisabledReason(
|
|
3976
|
+
function formatDisabledReason({
|
|
3977
|
+
activity,
|
|
3978
|
+
action,
|
|
3979
|
+
reason
|
|
3980
|
+
}) {
|
|
3398
3981
|
const detail = disabledReasonDetail[reason.kind](reason);
|
|
3399
3982
|
return `Action "${activity}:${action}" is not allowed: ${detail}`;
|
|
3400
3983
|
}
|
|
@@ -3420,28 +4003,32 @@ function formatEditDisabledReason(target, reason) {
|
|
|
3420
4003
|
}
|
|
3421
4004
|
async function editField(args) {
|
|
3422
4005
|
const { client, instanceId, target, mode = "set", value, options } = args;
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
try {
|
|
3426
|
-
return await commitEdit(ctx, target, mode, value, options);
|
|
3427
|
-
} catch (error) {
|
|
3428
|
-
if (!isRevisionConflict(error)) throw error;
|
|
3429
|
-
}
|
|
3430
|
-
}
|
|
3431
|
-
throw new ConcurrentEditFieldError({
|
|
4006
|
+
return retryOnRevisionConflict({
|
|
4007
|
+
client,
|
|
3432
4008
|
instanceId,
|
|
3433
|
-
|
|
3434
|
-
|
|
4009
|
+
options,
|
|
4010
|
+
commit: (ctx) => commitEdit({ ctx, target, mode, value, options }),
|
|
4011
|
+
onExhausted: () => new ConcurrentEditFieldError({
|
|
4012
|
+
instanceId,
|
|
4013
|
+
target: labelTarget(target),
|
|
4014
|
+
attempts: CONCURRENT_FIRE_ACTION_MAX_ATTEMPTS
|
|
4015
|
+
})
|
|
3435
4016
|
});
|
|
3436
4017
|
}
|
|
3437
|
-
async function commitEdit(
|
|
4018
|
+
async function commitEdit({
|
|
4019
|
+
ctx,
|
|
4020
|
+
target,
|
|
4021
|
+
mode,
|
|
4022
|
+
value,
|
|
4023
|
+
options
|
|
4024
|
+
}) {
|
|
3438
4025
|
const actor = options?.actor, stage = findStage(ctx.definition, ctx.instance.currentStage), slot = resolveEditTarget({ definition: ctx.definition, stage, target });
|
|
3439
4026
|
if (slot === void 0)
|
|
3440
4027
|
throw new Error(
|
|
3441
4028
|
`No field slot "${describeTarget(target)}" resolves in current stage "${stage.name}" of ${ctx.definition.name}`
|
|
3442
4029
|
);
|
|
3443
|
-
await assertSlotEditable(ctx, slot, options);
|
|
3444
|
-
const op = buildEditOp(slot, mode, value), mutation = startMutation(ctx.instance), ranOps = runOps({
|
|
4030
|
+
await assertSlotEditable({ ctx, slot, options });
|
|
4031
|
+
const op = buildEditOp({ slot, mode, value }), mutation = startMutation(ctx.instance), ranOps = runOps({
|
|
3445
4032
|
ops: [op],
|
|
3446
4033
|
mutation,
|
|
3447
4034
|
stage: stage.name,
|
|
@@ -3454,21 +4041,30 @@ async function commitEdit(ctx, target, mode, value, options) {
|
|
|
3454
4041
|
self: selfGdr(ctx.instance),
|
|
3455
4042
|
now: ctx.now
|
|
3456
4043
|
});
|
|
3457
|
-
return await
|
|
4044
|
+
return await persistThenMaybeRefresh({
|
|
3458
4045
|
ctx,
|
|
3459
4046
|
mutation,
|
|
3460
|
-
|
|
3461
|
-
|
|
4047
|
+
stageName: stage.name,
|
|
4048
|
+
didChangeState: ranOps.some(isFieldOp)
|
|
4049
|
+
}), {
|
|
3462
4050
|
edited: !0,
|
|
3463
4051
|
target: slotTarget(slot),
|
|
3464
4052
|
...ranOps.length > 0 ? { ranOps } : {}
|
|
3465
4053
|
};
|
|
3466
4054
|
}
|
|
3467
|
-
async function assertSlotEditable(
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
4055
|
+
async function assertSlotEditable({
|
|
4056
|
+
ctx,
|
|
4057
|
+
slot,
|
|
4058
|
+
options
|
|
4059
|
+
}) {
|
|
4060
|
+
const actor = options?.actor, window = slotWindowOpen(ctx.instance, slot), can = options?.grants !== void 0 && actor !== void 0 ? await advisoryCan({ instance: ctx.instance, actor, grants: options.grants }) : void 0, predicateSatisfied = window.open && slot.effective !== !0 && slot.effective !== void 0 ? await ctxEvaluateCondition({
|
|
4061
|
+
ctx,
|
|
4062
|
+
condition: slot.effective,
|
|
4063
|
+
opts: {
|
|
4064
|
+
...slot.activity !== void 0 ? { activityName: slot.activity } : {},
|
|
4065
|
+
...actor !== void 0 ? { actor } : {},
|
|
4066
|
+
...can !== void 0 ? { vars: { can } } : {}
|
|
4067
|
+
}
|
|
3472
4068
|
}) : slot.effective === !0, reason = editDisabledReason({
|
|
3473
4069
|
effective: slot.effective,
|
|
3474
4070
|
instance: ctx.instance,
|
|
@@ -3478,7 +4074,11 @@ async function assertSlotEditable(ctx, slot, options) {
|
|
|
3478
4074
|
});
|
|
3479
4075
|
if (reason !== void 0) throw new EditFieldDeniedError({ target: slotTarget(slot), reason });
|
|
3480
4076
|
}
|
|
3481
|
-
function buildEditOp(
|
|
4077
|
+
function buildEditOp({
|
|
4078
|
+
slot,
|
|
4079
|
+
mode,
|
|
4080
|
+
value
|
|
4081
|
+
}) {
|
|
3482
4082
|
if (mode === "unset") return { type: "field.unset", target: slot.ref };
|
|
3483
4083
|
if (value === void 0)
|
|
3484
4084
|
throw new Error(`editField mode "${mode}" requires a value for slot "${slot.name}"`);
|
|
@@ -3526,25 +4126,45 @@ async function resolveOperationContext(args) {
|
|
|
3526
4126
|
clientForGdr: buildClientForGdr(args.client, args.resourceClients)
|
|
3527
4127
|
};
|
|
3528
4128
|
}
|
|
3529
|
-
async function cascade(
|
|
3530
|
-
|
|
4129
|
+
async function cascade({
|
|
4130
|
+
client,
|
|
4131
|
+
instanceId,
|
|
4132
|
+
actor,
|
|
4133
|
+
clientForGdr,
|
|
4134
|
+
clock,
|
|
4135
|
+
overlay
|
|
4136
|
+
}) {
|
|
4137
|
+
const count = await cascadeAutoTransitions({
|
|
3531
4138
|
client,
|
|
3532
4139
|
instanceId,
|
|
3533
4140
|
actor,
|
|
3534
|
-
clientForGdr,
|
|
3535
|
-
clock,
|
|
3536
|
-
overlay
|
|
3537
|
-
);
|
|
3538
|
-
return await propagateToAncestors(
|
|
4141
|
+
...clientForGdr !== void 0 ? { clientForGdr } : {},
|
|
4142
|
+
...clock !== void 0 ? { clock } : {},
|
|
4143
|
+
...overlay !== void 0 ? { overlay } : {}
|
|
4144
|
+
});
|
|
4145
|
+
return await propagateToAncestors({
|
|
4146
|
+
client,
|
|
4147
|
+
instanceId,
|
|
4148
|
+
actor,
|
|
4149
|
+
...clientForGdr !== void 0 ? { clientForGdr } : {},
|
|
4150
|
+
...clock !== void 0 ? { clock } : {}
|
|
4151
|
+
}), count;
|
|
4152
|
+
}
|
|
4153
|
+
function engineOptionsForActor({
|
|
4154
|
+
actor,
|
|
4155
|
+
clock,
|
|
4156
|
+
clientForGdr
|
|
4157
|
+
}) {
|
|
4158
|
+
return { ...actor !== void 0 ? { actor } : {}, clock, clientForGdr };
|
|
3539
4159
|
}
|
|
3540
4160
|
async function abortAndPropagate(args) {
|
|
3541
4161
|
const { client, instanceId, reason, actor, clientForGdr, clock } = args, result = await abortInstance({
|
|
3542
4162
|
client,
|
|
3543
4163
|
instanceId,
|
|
3544
4164
|
...reason !== void 0 ? { reason } : {},
|
|
3545
|
-
options: {
|
|
4165
|
+
options: engineOptionsForActor({ actor, clock, clientForGdr })
|
|
3546
4166
|
});
|
|
3547
|
-
return result.fired && await propagateToAncestors(client, instanceId, actor, clientForGdr, clock), result.fired;
|
|
4167
|
+
return result.fired && await propagateToAncestors({ client, instanceId, actor, clientForGdr, clock }), result.fired;
|
|
3548
4168
|
}
|
|
3549
4169
|
function buildClientForGdr(defaultClient, resolver) {
|
|
3550
4170
|
return resolver === void 0 ? () => defaultClient : (parsed) => resolver(parsed) ?? defaultClient;
|
|
@@ -3560,15 +4180,25 @@ async function dispatchGatedWrite(args) {
|
|
|
3560
4180
|
access,
|
|
3561
4181
|
clock: args.clock,
|
|
3562
4182
|
...resourceClients !== void 0 ? { resourceClients } : {}
|
|
3563
|
-
}), ranOps = await apply(args.before, evaluation), cascaded = await cascade(
|
|
4183
|
+
}), ranOps = await apply(args.before, evaluation), cascaded = await cascade({
|
|
4184
|
+
client,
|
|
4185
|
+
instanceId,
|
|
4186
|
+
actor: args.actor,
|
|
4187
|
+
clientForGdr: args.clientForGdr,
|
|
4188
|
+
clock: args.clock
|
|
4189
|
+
});
|
|
3564
4190
|
return {
|
|
3565
|
-
instance: await reload(client, instanceId, tag),
|
|
4191
|
+
instance: await reload({ client, instanceId, tag }),
|
|
3566
4192
|
cascaded,
|
|
3567
4193
|
fired: !0,
|
|
3568
4194
|
...ranOps !== void 0 ? { ranOps } : {}
|
|
3569
4195
|
};
|
|
3570
4196
|
}
|
|
3571
|
-
function assertActionAllowed(
|
|
4197
|
+
function assertActionAllowed({
|
|
4198
|
+
evaluation,
|
|
4199
|
+
activity,
|
|
4200
|
+
action
|
|
4201
|
+
}) {
|
|
3572
4202
|
const actionEval = evaluation.currentStage.activities.find((t) => t.activity.name === activity)?.actions.find((a) => a.action.name === action);
|
|
3573
4203
|
if (actionEval?.allowed === !1 && actionEval.disabledReason)
|
|
3574
4204
|
throw new ActionDisabledError({ activity, action, reason: actionEval.disabledReason });
|
|
@@ -3606,7 +4236,7 @@ function buildEngineCallOptions(args) {
|
|
|
3606
4236
|
}
|
|
3607
4237
|
async function applyAction(args) {
|
|
3608
4238
|
const { client, instance, activity, action, params, actor, grants, clock, clientForGdr } = args, options = buildEngineCallOptions({ actor, grants, clock, clientForGdr });
|
|
3609
|
-
return
|
|
4239
|
+
return findCurrentActivityEntry(instance, activity)?.status === "pending" && await invokeActivity({ client, instanceId: instance._id, activity, options }), (await fireAction({
|
|
3610
4240
|
client,
|
|
3611
4241
|
instanceId: instance._id,
|
|
3612
4242
|
activity,
|
|
@@ -3616,15 +4246,12 @@ async function applyAction(args) {
|
|
|
3616
4246
|
})).ranOps;
|
|
3617
4247
|
}
|
|
3618
4248
|
async function deleteDefinition(args) {
|
|
3619
|
-
const { client, tag, definition, version, cascade: cascade2, reason } = args, { actor, clientForGdr } = await resolveOperationContext(args), clock = args.clock ?? wallClock, versions = await
|
|
3620
|
-
if (versions.length === 0)
|
|
3621
|
-
throw new Error(`No deployed definition for workflow ${definition}`);
|
|
3622
|
-
const targets = version === void 0 ? versions : versions.filter((d) => d.version === version);
|
|
4249
|
+
const { client, tag, definition, version, cascade: cascade2, reason } = args, { actor, clientForGdr } = await resolveOperationContext(args), clock = args.clock ?? wallClock, versions = await loadDefinitionVersionsOrThrow({ client, definition, tag }), targets = version === void 0 ? versions : versions.filter((d) => d.version === version);
|
|
3623
4250
|
if (targets.length === 0)
|
|
3624
4251
|
throw new Error(`Workflow definition ${definition} v${version} not deployed`);
|
|
3625
4252
|
const lastVersionGoes = targets.length === versions.length;
|
|
3626
|
-
await assertNoSpawnReferrers(client, tag, definition, targets, lastVersionGoes);
|
|
3627
|
-
const instanceIds = await nonTerminalInstanceIds(client, tag, definition, version);
|
|
4253
|
+
await assertNoSpawnReferrers({ client, tag, definition, targets, lastVersionGoes });
|
|
4254
|
+
const instanceIds = await nonTerminalInstanceIds({ client, tag, definition, version });
|
|
3628
4255
|
if (instanceIds.length > 0 && cascade2 !== !0)
|
|
3629
4256
|
throw new Error(
|
|
3630
4257
|
`Cannot delete ${definition}: ${instanceIds.length} non-terminal instance(s) exist (${previewIds(instanceIds)}). Pass cascade to abort them first \u2014 instances are aborted in place, never deleted.`
|
|
@@ -3654,7 +4281,13 @@ async function deleteDefinition(args) {
|
|
|
3654
4281
|
deletedGuardCount
|
|
3655
4282
|
};
|
|
3656
4283
|
}
|
|
3657
|
-
async function assertNoSpawnReferrers(
|
|
4284
|
+
async function assertNoSpawnReferrers({
|
|
4285
|
+
client,
|
|
4286
|
+
tag,
|
|
4287
|
+
definition,
|
|
4288
|
+
targets,
|
|
4289
|
+
lastVersionGoes
|
|
4290
|
+
}) {
|
|
3658
4291
|
const deployed = await client.fetch(
|
|
3659
4292
|
`*[_type == "${WORKFLOW_DEFINITION_TYPE}" && ${tagScopeFilter()}]`,
|
|
3660
4293
|
{ tag }
|
|
@@ -3670,7 +4303,12 @@ async function assertNoSpawnReferrers(client, tag, definition, targets, lastVers
|
|
|
3670
4303
|
);
|
|
3671
4304
|
}
|
|
3672
4305
|
}
|
|
3673
|
-
async function nonTerminalInstanceIds(
|
|
4306
|
+
async function nonTerminalInstanceIds({
|
|
4307
|
+
client,
|
|
4308
|
+
tag,
|
|
4309
|
+
definition,
|
|
4310
|
+
version
|
|
4311
|
+
}) {
|
|
3674
4312
|
const versionFilter = version === void 0 ? "" : " && pinnedVersion == $version";
|
|
3675
4313
|
return (await client.fetch(
|
|
3676
4314
|
`*[_type == "${WORKFLOW_INSTANCE_TYPE}" && definition == $definition && ${tagScopeFilter()} && !defined(completedAt)${versionFilter}] | order(_id asc){_id}`,
|
|
@@ -3707,14 +4345,19 @@ const workflow = {
|
|
|
3707
4345
|
* Cycles in the dependency graph error before any write happens.
|
|
3708
4346
|
*/
|
|
3709
4347
|
deployDefinitions: async (args) => {
|
|
3710
|
-
const { client, tag,
|
|
3711
|
-
validateTag(tag);
|
|
3712
|
-
|
|
3713
|
-
validateDefinition(def);
|
|
3714
|
-
const ordered = await sortByDependencies(client, definitions, tag), results = [], tx = client.transaction();
|
|
4348
|
+
const { client, tag, resourceAliases, definitions } = args;
|
|
4349
|
+
validateTag(tag), definitions.forEach(validateDefinition);
|
|
4350
|
+
const ordered = await sortByDependencies({ client, definitions, tag }), results = [], tx = client.transaction();
|
|
3715
4351
|
let hasWrites = !1;
|
|
3716
4352
|
for (const def of ordered) {
|
|
3717
|
-
const latest = await loadLatestDeployed(client, def.name, tag), plan = planDefinitionDeploy(
|
|
4353
|
+
const latest = await loadLatestDeployed({ client, definition: def.name, tag }), plan = planDefinitionDeploy({
|
|
4354
|
+
def,
|
|
4355
|
+
latest,
|
|
4356
|
+
target: {
|
|
4357
|
+
tag,
|
|
4358
|
+
...resourceAliases !== void 0 ? { resourceAliases } : {}
|
|
4359
|
+
}
|
|
4360
|
+
});
|
|
3718
4361
|
if (plan.status === "unchanged") {
|
|
3719
4362
|
results.push({ definition: def.name, version: plan.version, status: "unchanged" });
|
|
3720
4363
|
continue;
|
|
@@ -3751,7 +4394,7 @@ const workflow = {
|
|
|
3751
4394
|
effectsContext,
|
|
3752
4395
|
instanceId,
|
|
3753
4396
|
perspective
|
|
3754
|
-
} = args, { actor, clientForGdr } = await resolveOperationContext(args), clock = args.clock ?? wallClock, definition = await loadDefinition(client, definitionName, version, tag), id = instanceId ??
|
|
4397
|
+
} = args, { actor, clientForGdr } = await resolveOperationContext(args), clock = args.clock ?? wallClock, definition = await loadDefinition({ client, definition: definitionName, version, tag }), id = instanceId ?? instanceDocId(tag);
|
|
3755
4398
|
if (definition.stages.find((s) => s.name === definition.initialStage) === void 0)
|
|
3756
4399
|
throw new Error(
|
|
3757
4400
|
`Initial stage "${definition.initialStage}" missing in ${definitionName} v${definition.version}`
|
|
@@ -3767,7 +4410,7 @@ const workflow = {
|
|
|
3767
4410
|
workflowResource,
|
|
3768
4411
|
definitionName: definition.name,
|
|
3769
4412
|
...perspective !== void 0 ? { perspective } : {},
|
|
3770
|
-
recordDiscard: recordFieldDiscards(fieldDiscards, "workflow", now)
|
|
4413
|
+
recordDiscard: recordFieldDiscards({ target: fieldDiscards, scope: "workflow", at: now })
|
|
3771
4414
|
},
|
|
3772
4415
|
randomKey
|
|
3773
4416
|
}), effectivePerspective = perspective ?? derivePerspectiveFromFields(resolvedFields), base = buildInstanceBase({
|
|
@@ -3787,7 +4430,7 @@ const workflow = {
|
|
|
3787
4430
|
actor,
|
|
3788
4431
|
...fieldDiscards.length > 0 ? { extraHistory: fieldDiscards } : {}
|
|
3789
4432
|
});
|
|
3790
|
-
return await client.create(base, SYNC_COMMIT), await primeInitialStage(client, id, actor, clientForGdr, clock), await cascade(client, id, actor, clientForGdr, clock), reload(client, id, tag);
|
|
4433
|
+
return await client.create(base, SYNC_COMMIT), await primeInitialStage({ client, instanceId: id, actor, clientForGdr, clock }), await cascade({ client, instanceId: id, actor, clientForGdr, clock }), reload({ client, instanceId: id, tag });
|
|
3791
4434
|
},
|
|
3792
4435
|
/**
|
|
3793
4436
|
* Fire an action against an activity. If the activity is pending, it is
|
|
@@ -3809,8 +4452,8 @@ const workflow = {
|
|
|
3809
4452
|
params,
|
|
3810
4453
|
idempotent,
|
|
3811
4454
|
resourceClients
|
|
3812
|
-
} = args, { access, actor, clientForGdr } = await resolveOperationContext(args), clock = args.clock ?? wallClock, before = await reload(client, instanceId, tag);
|
|
3813
|
-
return
|
|
4455
|
+
} = args, { access, actor, clientForGdr } = await resolveOperationContext(args), clock = args.clock ?? wallClock, before = await reload({ client, instanceId, tag });
|
|
4456
|
+
return findCurrentActivityEntry(before, activity) === void 0 && idempotent === !0 ? { instance: before, cascaded: 0, fired: !1 } : dispatchGatedWrite({
|
|
3814
4457
|
client,
|
|
3815
4458
|
tag,
|
|
3816
4459
|
instanceId,
|
|
@@ -3820,7 +4463,7 @@ const workflow = {
|
|
|
3820
4463
|
clock,
|
|
3821
4464
|
before,
|
|
3822
4465
|
...resourceClients !== void 0 ? { resourceClients } : {},
|
|
3823
|
-
apply: (instance, evaluation) => (assertActionAllowed(evaluation, activity, action), applyAction({
|
|
4466
|
+
apply: (instance, evaluation) => (assertActionAllowed({ evaluation, activity, action }), applyAction({
|
|
3824
4467
|
client,
|
|
3825
4468
|
instance,
|
|
3826
4469
|
activity,
|
|
@@ -3849,7 +4492,7 @@ const workflow = {
|
|
|
3849
4492
|
* never an `onChange` per keystroke.
|
|
3850
4493
|
*/
|
|
3851
4494
|
editField: async (args) => {
|
|
3852
|
-
const { client, tag, instanceId, target, mode, value, resourceClients } = args, { access, actor, clientForGdr } = await resolveOperationContext(args), clock = args.clock ?? wallClock, before = await reload(client, instanceId, tag);
|
|
4495
|
+
const { client, tag, instanceId, target, mode, value, resourceClients } = args, { access, actor, clientForGdr } = await resolveOperationContext(args), clock = args.clock ?? wallClock, before = await reload({ client, instanceId, tag });
|
|
3853
4496
|
return dispatchGatedWrite({
|
|
3854
4497
|
client,
|
|
3855
4498
|
tag,
|
|
@@ -3878,24 +4521,27 @@ const workflow = {
|
|
|
3878
4521
|
* appends an `effectHistory` entry, and (if `outputs` are supplied
|
|
3879
4522
|
* and the effect succeeded) upserts those values into
|
|
3880
4523
|
* `effectsContext` by key — so downstream effect bindings can
|
|
3881
|
-
* reference them.
|
|
4524
|
+
* reference them. Any `ops` the handler returned (`field.*`) are
|
|
4525
|
+
* validated and applied to the instance in the same commit, through the
|
|
4526
|
+
* same op applier an action's field ops use. Cascades after.
|
|
3882
4527
|
*/
|
|
3883
4528
|
completeEffect: async (args) => {
|
|
3884
|
-
const { client, tag, instanceId, effectKey, status, outputs, detail, error, durationMs } = args, { actor, clientForGdr } = await resolveOperationContext(args), clock = args.clock ?? wallClock;
|
|
4529
|
+
const { client, tag, instanceId, effectKey, status, outputs, ops, detail, error, durationMs } = args, { actor, clientForGdr } = await resolveOperationContext(args), clock = args.clock ?? wallClock;
|
|
3885
4530
|
await completeEffect({
|
|
3886
4531
|
client,
|
|
3887
4532
|
instanceId,
|
|
3888
4533
|
effectKey,
|
|
3889
4534
|
status,
|
|
3890
4535
|
...outputs !== void 0 ? { outputs } : {},
|
|
4536
|
+
...ops !== void 0 ? { ops } : {},
|
|
3891
4537
|
...detail !== void 0 ? { detail } : {},
|
|
3892
4538
|
...error !== void 0 ? { error } : {},
|
|
3893
4539
|
...durationMs !== void 0 ? { durationMs } : {},
|
|
3894
|
-
options: {
|
|
4540
|
+
options: engineOptionsForActor({ actor, clock, clientForGdr })
|
|
3895
4541
|
});
|
|
3896
|
-
const cascaded = await cascade(client, instanceId, actor, clientForGdr, clock);
|
|
4542
|
+
const cascaded = await cascade({ client, instanceId, actor, clientForGdr, clock });
|
|
3897
4543
|
return {
|
|
3898
|
-
instance: await reload(client, instanceId, tag),
|
|
4544
|
+
instance: await reload({ client, instanceId, tag }),
|
|
3899
4545
|
cascaded,
|
|
3900
4546
|
fired: !0
|
|
3901
4547
|
};
|
|
@@ -3910,11 +4556,11 @@ const workflow = {
|
|
|
3910
4556
|
* affected instances and the engine re-evaluates.
|
|
3911
4557
|
*/
|
|
3912
4558
|
tick: async (args) => {
|
|
3913
|
-
const { client, tag, instanceId } = args, { actor, clientForGdr } = await resolveOperationContext(args), clock = args.clock ?? wallClock, current = await reload(client, instanceId, tag), guards = await verdictGuardsForInstance(client, current._id);
|
|
4559
|
+
const { client, tag, instanceId } = args, { actor, clientForGdr } = await resolveOperationContext(args), clock = args.clock ?? wallClock, current = await reload({ client, instanceId, tag }), guards = await verdictGuardsForInstance(client, current._id);
|
|
3914
4560
|
await assertInstanceWriteAllowed({ instance: current, guards, identity: actor.id });
|
|
3915
|
-
const cascaded = await cascade(client, instanceId, actor, clientForGdr, clock);
|
|
4561
|
+
const cascaded = await cascade({ client, instanceId, actor, clientForGdr, clock });
|
|
3916
4562
|
return {
|
|
3917
|
-
instance: await reload(client, instanceId, tag),
|
|
4563
|
+
instance: await reload({ client, instanceId, tag }),
|
|
3918
4564
|
cascaded,
|
|
3919
4565
|
fired: cascaded > 0
|
|
3920
4566
|
};
|
|
@@ -3926,16 +4572,16 @@ const workflow = {
|
|
|
3926
4572
|
*/
|
|
3927
4573
|
setStage: async (args) => {
|
|
3928
4574
|
const { client, tag, instanceId, targetStage, reason } = args, { actor, clientForGdr } = await resolveOperationContext(args), clock = args.clock ?? wallClock;
|
|
3929
|
-
await reload(client, instanceId, tag);
|
|
4575
|
+
await reload({ client, instanceId, tag });
|
|
3930
4576
|
const result = await setStage({
|
|
3931
4577
|
client,
|
|
3932
4578
|
instanceId,
|
|
3933
4579
|
targetStage,
|
|
3934
4580
|
...reason !== void 0 ? { reason } : {},
|
|
3935
|
-
options: {
|
|
3936
|
-
}), cascaded = result.fired ? await cascade(client, instanceId, actor, clientForGdr, clock) : 0;
|
|
4581
|
+
options: engineOptionsForActor({ actor, clock, clientForGdr })
|
|
4582
|
+
}), cascaded = result.fired ? await cascade({ client, instanceId, actor, clientForGdr, clock }) : 0;
|
|
3937
4583
|
return {
|
|
3938
|
-
instance: await reload(client, instanceId, tag),
|
|
4584
|
+
instance: await reload({ client, instanceId, tag }),
|
|
3939
4585
|
cascaded,
|
|
3940
4586
|
fired: result.fired
|
|
3941
4587
|
};
|
|
@@ -3948,10 +4594,10 @@ const workflow = {
|
|
|
3948
4594
|
*/
|
|
3949
4595
|
abortInstance: async (args) => {
|
|
3950
4596
|
const { client, tag, instanceId, reason } = args, { actor, clientForGdr } = await resolveOperationContext(args), clock = args.clock ?? wallClock;
|
|
3951
|
-
await reload(client, instanceId, tag);
|
|
4597
|
+
await reload({ client, instanceId, tag });
|
|
3952
4598
|
const fired = await abortAndPropagate({ client, instanceId, reason, actor, clientForGdr, clock });
|
|
3953
4599
|
return {
|
|
3954
|
-
instance: await reload(client, instanceId, tag),
|
|
4600
|
+
instance: await reload({ client, instanceId, tag }),
|
|
3955
4601
|
cascaded: 0,
|
|
3956
4602
|
fired
|
|
3957
4603
|
};
|
|
@@ -3963,12 +4609,12 @@ const workflow = {
|
|
|
3963
4609
|
*/
|
|
3964
4610
|
getInstance: async (args) => {
|
|
3965
4611
|
const { client, tag, instanceId } = args;
|
|
3966
|
-
return validateTag(tag), reload(client, instanceId, tag);
|
|
4612
|
+
return validateTag(tag), reload({ client, instanceId, tag });
|
|
3967
4613
|
},
|
|
3968
4614
|
guardsForInstance: async (args) => {
|
|
3969
4615
|
const { client, tag, instanceId, resourceClients } = args;
|
|
3970
4616
|
validateTag(tag);
|
|
3971
|
-
const instance = await reload(client, instanceId, tag);
|
|
4617
|
+
const instance = await reload({ client, instanceId, tag });
|
|
3972
4618
|
return guardsForInstance({
|
|
3973
4619
|
client,
|
|
3974
4620
|
clientForGdr: buildClientForGdr(client, resourceClients),
|
|
@@ -3978,9 +4624,7 @@ const workflow = {
|
|
|
3978
4624
|
guardsForDefinition: async (args) => {
|
|
3979
4625
|
const { client, tag, workflowResource, definition, resourceClients } = args;
|
|
3980
4626
|
validateTag(tag);
|
|
3981
|
-
const definitions = await
|
|
3982
|
-
if (definitions.length === 0)
|
|
3983
|
-
throw new Error(`No deployed definition for workflow ${definition}`);
|
|
4627
|
+
const definitions = await loadDefinitionVersionsOrThrow({ client, definition, tag });
|
|
3984
4628
|
return guardsForDefinition({
|
|
3985
4629
|
client,
|
|
3986
4630
|
clientForGdr: buildClientForGdr(client, resourceClients),
|
|
@@ -4025,7 +4669,7 @@ const workflow = {
|
|
|
4025
4669
|
queryInScope: async (args) => {
|
|
4026
4670
|
const { client, tag, instanceId, groq, params, resourceClients } = args, clock = args.clock ?? wallClock;
|
|
4027
4671
|
validateTag(tag);
|
|
4028
|
-
const instance = await reload(client, instanceId, tag), clientForGdr = buildClientForGdr(client, resourceClients), snapshot = await hydrateSnapshot({ client, clientForGdr, instance }), reserved = buildParams({ instance, now: clock(), snapshot }), tree = parse(groq, { params: { ...reserved, ...params } });
|
|
4672
|
+
const instance = await reload({ client, instanceId, tag }), clientForGdr = buildClientForGdr(client, resourceClients), snapshot = await hydrateSnapshot({ client, clientForGdr, instance }), reserved = buildParams({ instance, now: clock(), snapshot }), tree = parse(groq, { params: { ...reserved, ...params } });
|
|
4029
4673
|
return await (await evaluate(tree, {
|
|
4030
4674
|
dataset: snapshot.docs,
|
|
4031
4675
|
params: { ...reserved, ...params }
|
|
@@ -4035,13 +4679,13 @@ const workflow = {
|
|
|
4035
4679
|
* List every pending effect on the instance. Returns the same entries
|
|
4036
4680
|
* the runtime would see — claimed and unclaimed alike.
|
|
4037
4681
|
*/
|
|
4038
|
-
listPendingEffects: async (args) => (await reload(args.client, args.instanceId, args.tag)).pendingEffects,
|
|
4682
|
+
listPendingEffects: async (args) => (await reload({ client: args.client, instanceId: args.instanceId, tag: args.tag })).pendingEffects,
|
|
4039
4683
|
/**
|
|
4040
4684
|
* Filter pending effects on the instance by criteria. `claimed`
|
|
4041
4685
|
* filters on claim presence; `names` restricts to specific effect
|
|
4042
4686
|
* names. Both filters compose (AND).
|
|
4043
4687
|
*/
|
|
4044
|
-
findPendingEffects: async (args) => (await reload(args.client, args.instanceId, args.tag)).pendingEffects.filter((e) => !(args.claimed === !0 && e.claim === void 0 || args.claimed === !1 && e.claim !== void 0 || args.names !== void 0 && !args.names.includes(e.name))),
|
|
4688
|
+
findPendingEffects: async (args) => (await reload({ client: args.client, instanceId: args.instanceId, tag: args.tag })).pendingEffects.filter((e) => !(args.claimed === !0 && e.claim === void 0 || args.claimed === !1 && e.claim !== void 0 || args.names !== void 0 && !args.names.includes(e.name))),
|
|
4045
4689
|
/**
|
|
4046
4690
|
* Project the instance from a given actor's perspective. Returns a
|
|
4047
4691
|
* `WorkflowEvaluation` with per-action verdicts (`allowed` + a
|
|
@@ -4090,7 +4734,7 @@ const workflow = {
|
|
|
4090
4734
|
children: async (args) => {
|
|
4091
4735
|
const { client, tag, instanceId, activity } = args;
|
|
4092
4736
|
validateTag(tag);
|
|
4093
|
-
const parent = await reload(client, instanceId, tag), isSpawned = (h) => h._type === "spawned", ids = parent.history.filter(isSpawned).filter((h) => activity === void 0 || h.activity === activity).flatMap((h) => bareIdFromSpawnRef(h.instanceRef.id));
|
|
4737
|
+
const parent = await reload({ client, instanceId, tag }), isSpawned = (h) => h._type === "spawned", ids = parent.history.filter(isSpawned).filter((h) => activity === void 0 || h.activity === activity).flatMap((h) => bareIdFromSpawnRef(h.instanceRef.id));
|
|
4094
4738
|
return ids.length === 0 ? [] : client.fetch(
|
|
4095
4739
|
`*[_id in $ids && ${tagScopeFilter()}] | order(startedAt asc)`,
|
|
4096
4740
|
{ ids, tag }
|
|
@@ -4186,7 +4830,7 @@ async function verifyDeployedDefinitionsInternal(args) {
|
|
|
4186
4830
|
locations: m.locations,
|
|
4187
4831
|
definitionId: m.definitionId
|
|
4188
4832
|
};
|
|
4189
|
-
if (await applyMissingHandler(missingHandler, info, log) === "fail")
|
|
4833
|
+
if (await applyMissingHandler({ policy: missingHandler, info, log }) === "fail")
|
|
4190
4834
|
throw new Error(
|
|
4191
4835
|
`verifyDeployedDefinitions: missing handler for "${m.name}" referenced by ${m.definitionId} at ${m.locations.join(", ")}`
|
|
4192
4836
|
);
|
|
@@ -4227,21 +4871,30 @@ async function drainEffectsInternal(args) {
|
|
|
4227
4871
|
validateTag(tag);
|
|
4228
4872
|
const log = logger("drainEffects"), drained = [], failed = [], skipped = [], skippedKeys = /* @__PURE__ */ new Set();
|
|
4229
4873
|
for (; ; ) {
|
|
4230
|
-
const before = await reload(client, instanceId, tag), candidate = before.pendingEffects.find(
|
|
4874
|
+
const before = await reload({ client, instanceId, tag }), candidate = before.pendingEffects.find(
|
|
4231
4875
|
(e) => e.claim === void 0 && !skippedKeys.has(e._key)
|
|
4232
4876
|
);
|
|
4233
4877
|
if (candidate === void 0) break;
|
|
4234
4878
|
const handler = effectHandlers[candidate.name];
|
|
4235
4879
|
if (handler === void 0) {
|
|
4236
|
-
await assertSkippableOrThrow(missingHandler, candidate, instanceId, log), skipped.push(candidate), skippedKeys.add(candidate._key);
|
|
4880
|
+
await assertSkippableOrThrow({ missingHandler, candidate, instanceId, log }), skipped.push(candidate), skippedKeys.add(candidate._key);
|
|
4237
4881
|
continue;
|
|
4238
4882
|
}
|
|
4239
|
-
if (!await claimPendingEffect(
|
|
4240
|
-
const { outputs, dispatchError } = await dispatchEffect(handler, candidate, {
|
|
4883
|
+
if (!await claimPendingEffect({
|
|
4241
4884
|
client,
|
|
4242
|
-
|
|
4243
|
-
|
|
4244
|
-
|
|
4885
|
+
instance: before,
|
|
4886
|
+
effectKey: candidate._key,
|
|
4887
|
+
drainerActor
|
|
4888
|
+
})) continue;
|
|
4889
|
+
const { outputs, ops, dispatchError } = await dispatchEffect({
|
|
4890
|
+
handler,
|
|
4891
|
+
candidate,
|
|
4892
|
+
ctx: {
|
|
4893
|
+
client,
|
|
4894
|
+
clientFor,
|
|
4895
|
+
instanceId,
|
|
4896
|
+
logger
|
|
4897
|
+
}
|
|
4245
4898
|
});
|
|
4246
4899
|
await reportEffectOutcome({
|
|
4247
4900
|
client,
|
|
@@ -4252,32 +4905,44 @@ async function drainEffectsInternal(args) {
|
|
|
4252
4905
|
effectKey: candidate._key,
|
|
4253
4906
|
access: completionAccess,
|
|
4254
4907
|
outputs,
|
|
4908
|
+
ops,
|
|
4255
4909
|
dispatchError
|
|
4256
4910
|
}), dispatchError === void 0 ? drained.push(candidate) : failed.push(candidate);
|
|
4257
4911
|
}
|
|
4258
4912
|
return { drained, failed, skipped };
|
|
4259
4913
|
}
|
|
4260
4914
|
async function reportEffectOutcome(args) {
|
|
4261
|
-
const { outputs, dispatchError, resourceClients, ...rest } = args;
|
|
4915
|
+
const { outputs, ops, dispatchError, resourceClients, ...rest } = args;
|
|
4262
4916
|
await workflow.completeEffect({
|
|
4263
4917
|
...rest,
|
|
4264
4918
|
...resourceClients !== void 0 ? { resourceClients } : {},
|
|
4265
4919
|
status: dispatchError === void 0 ? "done" : "failed",
|
|
4266
4920
|
...outputs !== void 0 ? { outputs } : {},
|
|
4921
|
+
...ops !== void 0 ? { ops } : {},
|
|
4267
4922
|
...dispatchError !== void 0 ? { error: dispatchError } : {}
|
|
4268
4923
|
});
|
|
4269
4924
|
}
|
|
4270
|
-
async function assertSkippableOrThrow(
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4925
|
+
async function assertSkippableOrThrow({
|
|
4926
|
+
missingHandler,
|
|
4927
|
+
candidate,
|
|
4928
|
+
instanceId,
|
|
4929
|
+
log
|
|
4930
|
+
}) {
|
|
4931
|
+
if (await applyMissingHandler({
|
|
4932
|
+
policy: missingHandler,
|
|
4933
|
+
info: { phase: "drain", name: candidate.name, effectKey: candidate._key, instanceId },
|
|
4274
4934
|
log
|
|
4275
|
-
) === "fail")
|
|
4935
|
+
}) === "fail")
|
|
4276
4936
|
throw new Error(
|
|
4277
4937
|
`drainEffects: no handler registered for "${candidate.name}" (effectKey=${candidate._key}, instanceId=${instanceId})`
|
|
4278
4938
|
);
|
|
4279
4939
|
}
|
|
4280
|
-
async function claimPendingEffect(
|
|
4940
|
+
async function claimPendingEffect({
|
|
4941
|
+
client,
|
|
4942
|
+
instance,
|
|
4943
|
+
effectKey,
|
|
4944
|
+
drainerActor
|
|
4945
|
+
}) {
|
|
4281
4946
|
const claim = {
|
|
4282
4947
|
_type: "pendingEffect.claim",
|
|
4283
4948
|
claimedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -4290,7 +4955,11 @@ async function claimPendingEffect(client, instance, effectKey, drainerActor) {
|
|
|
4290
4955
|
return !1;
|
|
4291
4956
|
}
|
|
4292
4957
|
}
|
|
4293
|
-
async function dispatchEffect(
|
|
4958
|
+
async function dispatchEffect({
|
|
4959
|
+
handler,
|
|
4960
|
+
candidate,
|
|
4961
|
+
ctx
|
|
4962
|
+
}) {
|
|
4294
4963
|
try {
|
|
4295
4964
|
const result = await handler(candidate.params, {
|
|
4296
4965
|
client: ctx.client,
|
|
@@ -4299,13 +4968,20 @@ async function dispatchEffect(handler, candidate, ctx) {
|
|
|
4299
4968
|
effectKey: candidate._key,
|
|
4300
4969
|
log: (message, extra) => ctx.logger(`effect.${candidate.name}`).info(message, extra)
|
|
4301
4970
|
});
|
|
4302
|
-
return
|
|
4971
|
+
return {
|
|
4972
|
+
...result?.outputs !== void 0 ? { outputs: result.outputs } : {},
|
|
4973
|
+
...result?.ops !== void 0 ? { ops: result.ops } : {}
|
|
4974
|
+
};
|
|
4303
4975
|
} catch (err) {
|
|
4304
4976
|
const message = err instanceof Error ? err.message : String(err), stack = err instanceof Error && err.stack !== void 0 ? err.stack : void 0;
|
|
4305
4977
|
return { dispatchError: stack !== void 0 ? { message, stack } : { message } };
|
|
4306
4978
|
}
|
|
4307
4979
|
}
|
|
4308
|
-
async function applyMissingHandler(
|
|
4980
|
+
async function applyMissingHandler({
|
|
4981
|
+
policy,
|
|
4982
|
+
info,
|
|
4983
|
+
log
|
|
4984
|
+
}) {
|
|
4309
4985
|
if (policy === "fail") return "fail";
|
|
4310
4986
|
if (policy === "skip")
|
|
4311
4987
|
return log.warn(`Missing effect handler "${info.name}" \u2014 skipping`, { info }), "skip";
|
|
@@ -4343,9 +5019,20 @@ function createInstanceSession(args) {
|
|
|
4343
5019
|
...clock !== void 0 ? { now: clock() } : {},
|
|
4344
5020
|
...grants !== void 0 ? { grants } : {}
|
|
4345
5021
|
});
|
|
4346
|
-
}, settleAfterApply = async (
|
|
4347
|
-
|
|
4348
|
-
|
|
5022
|
+
}, settleAfterApply = async ({
|
|
5023
|
+
actor,
|
|
5024
|
+
held,
|
|
5025
|
+
ranOps
|
|
5026
|
+
}) => {
|
|
5027
|
+
const cascaded = await cascade({
|
|
5028
|
+
client,
|
|
5029
|
+
instanceId: instance._id,
|
|
5030
|
+
actor,
|
|
5031
|
+
clientForGdr,
|
|
5032
|
+
...clock !== void 0 ? { clock } : {},
|
|
5033
|
+
overlay: held
|
|
5034
|
+
});
|
|
5035
|
+
return instance = await reload({ client, instanceId: instance._id, tag }), { instance, cascaded, fired: !0, ...ranOps !== void 0 ? { ranOps } : {} };
|
|
4349
5036
|
}, commit = async (run) => {
|
|
4350
5037
|
committing = !0;
|
|
4351
5038
|
const held = new Map(overlay);
|
|
@@ -4379,13 +5066,20 @@ function createInstanceSession(args) {
|
|
|
4379
5066
|
tick() {
|
|
4380
5067
|
return commit(async (actor, held) => {
|
|
4381
5068
|
await assertInstanceWriteAllowed({ instance, guards: heldGuards, identity: actor.id });
|
|
4382
|
-
const cascaded = await cascade(
|
|
4383
|
-
|
|
5069
|
+
const cascaded = await cascade({
|
|
5070
|
+
client,
|
|
5071
|
+
instanceId: instance._id,
|
|
5072
|
+
actor,
|
|
5073
|
+
clientForGdr,
|
|
5074
|
+
...clock !== void 0 ? { clock } : {},
|
|
5075
|
+
overlay: held
|
|
5076
|
+
});
|
|
5077
|
+
return instance = await reload({ client, instanceId: instance._id, tag }), { instance, cascaded, fired: cascaded > 0 };
|
|
4384
5078
|
});
|
|
4385
5079
|
},
|
|
4386
5080
|
fireAction({ activity, action, params }) {
|
|
4387
5081
|
return commit(async (actor, held) => {
|
|
4388
|
-
assertActionAllowed(await evaluateWith(held, heldGuards), activity, action);
|
|
5082
|
+
assertActionAllowed({ evaluation: await evaluateWith(held, heldGuards), activity, action });
|
|
4389
5083
|
const { grants } = await access(), ranOps = await applyAction({
|
|
4390
5084
|
client,
|
|
4391
5085
|
instance,
|
|
@@ -4397,7 +5091,7 @@ function createInstanceSession(args) {
|
|
|
4397
5091
|
...clock !== void 0 ? { clock } : {},
|
|
4398
5092
|
...params !== void 0 ? { params } : {}
|
|
4399
5093
|
});
|
|
4400
|
-
return settleAfterApply(actor, held, ranOps);
|
|
5094
|
+
return settleAfterApply({ actor, held, ranOps });
|
|
4401
5095
|
});
|
|
4402
5096
|
},
|
|
4403
5097
|
editField({ target, mode, value }) {
|
|
@@ -4414,7 +5108,7 @@ function createInstanceSession(args) {
|
|
|
4414
5108
|
...grants !== void 0 ? { grants } : {},
|
|
4415
5109
|
...clock !== void 0 ? { clock } : {}
|
|
4416
5110
|
});
|
|
4417
|
-
return settleAfterApply(actor, held, ranOps);
|
|
5111
|
+
return settleAfterApply({ actor, held, ranOps });
|
|
4418
5112
|
});
|
|
4419
5113
|
}
|
|
4420
5114
|
};
|
|
@@ -4549,8 +5243,12 @@ function createEngine(args) {
|
|
|
4549
5243
|
})
|
|
4550
5244
|
};
|
|
4551
5245
|
}
|
|
4552
|
-
function diffEntry(
|
|
4553
|
-
|
|
5246
|
+
function diffEntry({
|
|
5247
|
+
def,
|
|
5248
|
+
latestRaw,
|
|
5249
|
+
target
|
|
5250
|
+
}) {
|
|
5251
|
+
const plan = planDefinitionDeploy({ def, latest: asLatest(latestRaw), target }), existing = latestRaw ? stripSystemFields(latestRaw) : void 0;
|
|
4554
5252
|
return {
|
|
4555
5253
|
name: def.name,
|
|
4556
5254
|
version: plan.version,
|
|
@@ -4563,11 +5261,15 @@ function diffEntry(def, latestRaw, target) {
|
|
|
4563
5261
|
function asLatest(raw) {
|
|
4564
5262
|
return raw === void 0 ? void 0 : { version: typeof raw.version == "number" ? raw.version : 0, ...typeof raw.contentHash == "string" ? { contentHash: raw.contentHash } : {} };
|
|
4565
5263
|
}
|
|
4566
|
-
async function computeDiffEntries(
|
|
5264
|
+
async function computeDiffEntries({
|
|
5265
|
+
client,
|
|
5266
|
+
defs,
|
|
5267
|
+
target
|
|
5268
|
+
}) {
|
|
4567
5269
|
const entries = [];
|
|
4568
5270
|
for (const def of defs) {
|
|
4569
|
-
const latest = await loadLatestDeployed(client, def.name, target.tag);
|
|
4570
|
-
entries.push(diffEntry(def, latest, target));
|
|
5271
|
+
const latest = await loadLatestDeployed({ client, definition: def.name, tag: target.tag });
|
|
5272
|
+
entries.push(diffEntry({ def, latestRaw: latest, target }));
|
|
4571
5273
|
}
|
|
4572
5274
|
return entries;
|
|
4573
5275
|
}
|
|
@@ -4802,6 +5504,7 @@ export {
|
|
|
4802
5504
|
DRIVER_KIND_DISPLAY,
|
|
4803
5505
|
EFFECTS_CONTEXT_DISPLAY,
|
|
4804
5506
|
EditFieldDeniedError,
|
|
5507
|
+
EffectOpsInvalidError,
|
|
4805
5508
|
FIELD_SLOT_DISPLAY,
|
|
4806
5509
|
GUARD_DOC_TYPE,
|
|
4807
5510
|
GUARD_LIFTED_PREDICATE,
|