@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.cjs
CHANGED
|
@@ -20,6 +20,28 @@ var v__namespace = /* @__PURE__ */ _interopNamespaceCompat(v);
|
|
|
20
20
|
function findOpenStageEntry(host) {
|
|
21
21
|
return host.stages.find((s) => s.name === host.currentStage && s.exitedAt === void 0);
|
|
22
22
|
}
|
|
23
|
+
function findCurrentActivityEntry(host, activityName) {
|
|
24
|
+
return findOpenStageEntry(host)?.activities.find((a) => a.name === activityName);
|
|
25
|
+
}
|
|
26
|
+
function errorMessage(err) {
|
|
27
|
+
return err instanceof Error ? err.message : String(err);
|
|
28
|
+
}
|
|
29
|
+
function rethrowWithContext(err, context) {
|
|
30
|
+
throw new Error(`${context}: ${errorMessage(err)}`, { cause: err });
|
|
31
|
+
}
|
|
32
|
+
function mapJsonStrings(value, transform) {
|
|
33
|
+
const walk = (val, key) => {
|
|
34
|
+
if (typeof val == "string") return transform(val, key);
|
|
35
|
+
if (Array.isArray(val)) return val.map((item) => walk(item, key));
|
|
36
|
+
if (val !== null && typeof val == "object") {
|
|
37
|
+
const out = {};
|
|
38
|
+
for (const [k, v2] of Object.entries(val)) out[k] = walk(v2, k);
|
|
39
|
+
return out;
|
|
40
|
+
}
|
|
41
|
+
return val;
|
|
42
|
+
};
|
|
43
|
+
return walk(value, void 0);
|
|
44
|
+
}
|
|
23
45
|
const KNOWN_SCHEMES = /* @__PURE__ */ new Set(["dataset", "canvas", "media-library", "dashboard"]);
|
|
24
46
|
function parseGdr(uri) {
|
|
25
47
|
const colon = uri.indexOf(":");
|
|
@@ -59,22 +81,56 @@ function parseGdr(uri) {
|
|
|
59
81
|
documentId: parts[1]
|
|
60
82
|
};
|
|
61
83
|
}
|
|
84
|
+
function tryParseGdr(uri) {
|
|
85
|
+
try {
|
|
86
|
+
return parseGdr(uri);
|
|
87
|
+
} catch {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
62
91
|
function gdrUri(parts) {
|
|
63
92
|
return parts.scheme === "dataset" ? `dataset:${parts.projectId}:${parts.dataset}:${parts.documentId}` : `${parts.scheme}:${parts.resourceId}:${parts.documentId}`;
|
|
64
93
|
}
|
|
65
94
|
function extractDocumentId(gdrUriString) {
|
|
66
95
|
return parseGdr(gdrUriString).documentId;
|
|
67
96
|
}
|
|
68
|
-
|
|
97
|
+
const RESOURCE_ALIAS_NAME_SOURCE = "[a-z0-9][a-z0-9-]*", RESOURCE_ALIAS_NAME_RE = new RegExp(`^${RESOURCE_ALIAS_NAME_SOURCE}$`);
|
|
98
|
+
function validateResourceAliasName(name) {
|
|
99
|
+
if (!RESOURCE_ALIAS_NAME_RE.test(name))
|
|
100
|
+
throw new Error(
|
|
101
|
+
`Invalid resource alias name "${name}": expected lowercase letters, digits, and dashes (no leading dash).`
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
function toPhysicalGdr(id, home) {
|
|
105
|
+
return isGdrUri(id) ? id : gdrFromResource(home, id);
|
|
106
|
+
}
|
|
107
|
+
function refDataset({
|
|
108
|
+
projectId,
|
|
109
|
+
dataset,
|
|
110
|
+
documentId,
|
|
111
|
+
type
|
|
112
|
+
}) {
|
|
69
113
|
return { id: gdrUri({ scheme: "dataset", projectId, dataset, documentId }), type };
|
|
70
114
|
}
|
|
71
|
-
function refCanvas(
|
|
115
|
+
function refCanvas({
|
|
116
|
+
resourceId,
|
|
117
|
+
documentId,
|
|
118
|
+
type
|
|
119
|
+
}) {
|
|
72
120
|
return { id: gdrUri({ scheme: "canvas", resourceId, documentId }), type };
|
|
73
121
|
}
|
|
74
|
-
function refMediaLibrary(
|
|
122
|
+
function refMediaLibrary({
|
|
123
|
+
resourceId,
|
|
124
|
+
documentId,
|
|
125
|
+
type
|
|
126
|
+
}) {
|
|
75
127
|
return { id: gdrUri({ scheme: "media-library", resourceId, documentId }), type };
|
|
76
128
|
}
|
|
77
|
-
function refDashboard(
|
|
129
|
+
function refDashboard({
|
|
130
|
+
resourceId,
|
|
131
|
+
documentId,
|
|
132
|
+
type
|
|
133
|
+
}) {
|
|
78
134
|
return { id: gdrUri({ scheme: "dashboard", resourceId, documentId }), type };
|
|
79
135
|
}
|
|
80
136
|
function datasetResourceParts(id) {
|
|
@@ -90,17 +146,16 @@ function gdrFromResource(res, documentId) {
|
|
|
90
146
|
}
|
|
91
147
|
return gdrUri({ scheme: res.type, resourceId: res.id, documentId });
|
|
92
148
|
}
|
|
149
|
+
function gdrResourcePrefix(res) {
|
|
150
|
+
const full = gdrFromResource(res, "x");
|
|
151
|
+
return full.slice(0, full.length - 1);
|
|
152
|
+
}
|
|
153
|
+
function resourceFromParsed(parsed) {
|
|
154
|
+
return parsed.scheme === "dataset" ? { type: "dataset", id: `${parsed.projectId}.${parsed.dataset}` } : { type: parsed.scheme, id: parsed.resourceId ?? "" };
|
|
155
|
+
}
|
|
93
156
|
function resourceFromGdrUri(uri) {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
parsed = parseGdr(uri);
|
|
97
|
-
} catch {
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
if (parsed.scheme === "dataset")
|
|
101
|
-
return parsed.projectId === void 0 || parsed.dataset === void 0 ? void 0 : { type: "dataset", id: `${parsed.projectId}.${parsed.dataset}` };
|
|
102
|
-
if (parsed.resourceId !== void 0)
|
|
103
|
-
return { type: parsed.scheme, id: parsed.resourceId };
|
|
157
|
+
const parsed = tryParseGdr(uri);
|
|
158
|
+
return parsed === void 0 ? void 0 : resourceFromParsed(parsed);
|
|
104
159
|
}
|
|
105
160
|
function sameResource(a, b) {
|
|
106
161
|
return a.type === b.type && a.id === b.id;
|
|
@@ -108,15 +163,24 @@ function sameResource(a, b) {
|
|
|
108
163
|
function selfGdr(doc) {
|
|
109
164
|
return gdrFromResource(doc.workflowResource, doc._id);
|
|
110
165
|
}
|
|
166
|
+
function definitionDocId({
|
|
167
|
+
tag,
|
|
168
|
+
definition,
|
|
169
|
+
version
|
|
170
|
+
}) {
|
|
171
|
+
return `${tag}.${definition}.v${version}`;
|
|
172
|
+
}
|
|
111
173
|
function isGdrUri(value) {
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
return !1;
|
|
117
|
-
}
|
|
174
|
+
return typeof value == "string" && tryParseGdr(value) !== void 0;
|
|
175
|
+
}
|
|
176
|
+
function toBareId(id) {
|
|
177
|
+
return isGdrUri(id) ? extractDocumentId(id) : id;
|
|
118
178
|
}
|
|
119
|
-
function gdrRef(
|
|
179
|
+
function gdrRef({
|
|
180
|
+
res,
|
|
181
|
+
documentId,
|
|
182
|
+
type
|
|
183
|
+
}) {
|
|
120
184
|
return { id: gdrFromResource(res, documentId), type };
|
|
121
185
|
}
|
|
122
186
|
function isGdr(value) {
|
|
@@ -151,17 +215,26 @@ function renderedFields(entries, snapshot) {
|
|
|
151
215
|
function renderedValue(entry, snapshot) {
|
|
152
216
|
return entry._type !== "doc.ref" || entry.value === null || snapshot === void 0 ? entry.value : snapshot.docs.find((d) => d._id === entry.value.id) ?? entry.value;
|
|
153
217
|
}
|
|
154
|
-
function scopedFieldOverlay(
|
|
218
|
+
function scopedFieldOverlay({
|
|
219
|
+
instance,
|
|
220
|
+
snapshot,
|
|
221
|
+
activityName
|
|
222
|
+
}) {
|
|
155
223
|
const stageEntry = findOpenStageEntry(instance);
|
|
156
224
|
if (stageEntry === void 0) return {};
|
|
157
225
|
const stageFields = renderedFields(stageEntry.fields ?? [], snapshot), activity = activityName ? stageEntry.activities.find((t) => t.name === activityName) : void 0;
|
|
158
226
|
return { ...stageFields, ...renderedFields(activity?.fields ?? [], snapshot) };
|
|
159
227
|
}
|
|
160
|
-
function assignedFor(
|
|
228
|
+
function assignedFor({
|
|
229
|
+
instance,
|
|
230
|
+
activityName,
|
|
231
|
+
actor,
|
|
232
|
+
roleAliases
|
|
233
|
+
}) {
|
|
161
234
|
if (actor === void 0) return !1;
|
|
162
|
-
const entry =
|
|
235
|
+
const entry = findCurrentActivityEntry(instance, activityName)?.fields?.find((s) => s._type === "assignees");
|
|
163
236
|
return entry === void 0 ? !1 : entry.value.some(
|
|
164
|
-
(a) => a.type === "user" ? a.id === actor.id : schema.actorFulfillsRole(actor.roles, a.role, roleAliases)
|
|
237
|
+
(a) => a.type === "user" ? a.id === actor.id : schema.actorFulfillsRole({ actorRoles: actor.roles, required: a.role, aliases: roleAliases })
|
|
165
238
|
);
|
|
166
239
|
}
|
|
167
240
|
function effectsContextMap(instance) {
|
|
@@ -174,35 +247,20 @@ function parseJsonContextEntry(entry) {
|
|
|
174
247
|
try {
|
|
175
248
|
return JSON.parse(entry.value);
|
|
176
249
|
} catch (err) {
|
|
177
|
-
|
|
178
|
-
`effectsContext entry "${entry.name}" holds unparseable JSON: ${err instanceof Error ? err.message : String(err)}`,
|
|
179
|
-
{ cause: err }
|
|
180
|
-
);
|
|
250
|
+
rethrowWithContext(err, `effectsContext entry "${entry.name}" holds unparseable JSON`);
|
|
181
251
|
}
|
|
182
252
|
}
|
|
183
253
|
function paramsForLake(params) {
|
|
184
254
|
return {
|
|
185
255
|
...params,
|
|
186
|
-
self: typeof params.self == "string" ?
|
|
187
|
-
parent: typeof params.parent == "string" ?
|
|
188
|
-
ancestors: Array.isArray(params.ancestors) ? params.ancestors.map((a) => typeof a == "string" ?
|
|
256
|
+
self: typeof params.self == "string" ? toBareId(params.self) : params.self,
|
|
257
|
+
parent: typeof params.parent == "string" ? toBareId(params.parent) : params.parent,
|
|
258
|
+
ancestors: Array.isArray(params.ancestors) ? params.ancestors.map((a) => typeof a == "string" ? toBareId(a) : a) : params.ancestors,
|
|
189
259
|
fields: stripFieldsForLake(params.fields)
|
|
190
260
|
};
|
|
191
261
|
}
|
|
192
262
|
function stripFieldsForLake(value) {
|
|
193
|
-
|
|
194
|
-
if (typeof value == "string") return bareId(value);
|
|
195
|
-
if (Array.isArray(value)) return value.map(stripFieldsForLake);
|
|
196
|
-
if (typeof value == "object") {
|
|
197
|
-
const out = {};
|
|
198
|
-
for (const [k, v2] of Object.entries(value))
|
|
199
|
-
out[k] = stripFieldsForLake(v2);
|
|
200
|
-
return out;
|
|
201
|
-
}
|
|
202
|
-
return value;
|
|
203
|
-
}
|
|
204
|
-
function bareId(id) {
|
|
205
|
-
return isGdrUri(id) ? extractDocumentId(id) : id;
|
|
263
|
+
return mapJsonStrings(value, (s) => toBareId(s));
|
|
206
264
|
}
|
|
207
265
|
function getPath(value, path) {
|
|
208
266
|
let current = value;
|
|
@@ -212,9 +270,6 @@ function getPath(value, path) {
|
|
|
212
270
|
}
|
|
213
271
|
return current;
|
|
214
272
|
}
|
|
215
|
-
function resourceOf(p) {
|
|
216
|
-
return p.scheme === "dataset" ? { type: "dataset", id: `${p.projectId}.${p.dataset}` } : { type: p.scheme, id: p.resourceId ?? "" };
|
|
217
|
-
}
|
|
218
273
|
const FIELD_READ = /^\$fields\.(\w+)(?:\.(.+))?$/, EFFECTS_READ = /^\$effects\['([^']+)'\](?:\.(.+))?$/;
|
|
219
274
|
function isGuardReadExpr(expr) {
|
|
220
275
|
return expr === "$self" || expr === "$now" || FIELD_READ.test(expr) || EFFECTS_READ.test(expr);
|
|
@@ -257,9 +312,9 @@ function resolveIdRefTargets(idRefs, ctx) {
|
|
|
257
312
|
return targets.length === 0 ? null : targets;
|
|
258
313
|
}
|
|
259
314
|
function assertSingleResource(targets) {
|
|
260
|
-
const resource =
|
|
315
|
+
const resource = resourceFromParsed(targets[0].parsed);
|
|
261
316
|
for (const g of targets) {
|
|
262
|
-
const r =
|
|
317
|
+
const r = resourceFromParsed(g.parsed);
|
|
263
318
|
if (r.type !== resource.type || r.id !== resource.id)
|
|
264
319
|
throw new Error(
|
|
265
320
|
`Guard targets span multiple resources (${resource.type}:${resource.id} vs ${r.type}:${r.id}); a guard is single-resource.`
|
|
@@ -323,23 +378,31 @@ function validateStage(v2, stage) {
|
|
|
323
378
|
for (const entry of stage.fields ?? [])
|
|
324
379
|
v2.checkEntry(entry, `stage "${stage.name}".fields "${entry.name}"`);
|
|
325
380
|
for (const guard of stage.guards ?? [])
|
|
326
|
-
validateGuardReads(v2, stage.name, guard);
|
|
381
|
+
validateGuardReads({ v: v2, stageName: stage.name, guard });
|
|
327
382
|
for (const t of stage.transitions ?? []) {
|
|
328
383
|
v2.checkCondition(t.filter, `transition "${t.name}" (${stage.name} \u2192 ${t.to}) filter`);
|
|
329
384
|
for (const [key, groq] of effectBindings(t.effects))
|
|
330
385
|
v2.tryParse(groq, `transition "${t.name}" effect binding "${key}"`);
|
|
331
386
|
}
|
|
332
387
|
for (const activity of stage.activities ?? [])
|
|
333
|
-
validateActivity(v2, stage.name, activity);
|
|
388
|
+
validateActivity({ v: v2, stageName: stage.name, activity });
|
|
334
389
|
}
|
|
335
|
-
function validateGuardReads(
|
|
390
|
+
function validateGuardReads({
|
|
391
|
+
v: v2,
|
|
392
|
+
stageName,
|
|
393
|
+
guard
|
|
394
|
+
}) {
|
|
336
395
|
const where = `stage "${stageName}" guard "${guard.name}"`;
|
|
337
396
|
for (const expr of guard.match.idRefs ?? [])
|
|
338
397
|
v2.checkGuardRead(expr, `${where} match.idRefs`);
|
|
339
398
|
for (const [key, expr] of Object.entries(guard.metadata ?? {}))
|
|
340
399
|
v2.checkGuardRead(expr, `${where} metadata "${key}"`);
|
|
341
400
|
}
|
|
342
|
-
function validateActivity(
|
|
401
|
+
function validateActivity({
|
|
402
|
+
v: v2,
|
|
403
|
+
stageName,
|
|
404
|
+
activity
|
|
405
|
+
}) {
|
|
343
406
|
const where = `stage "${stageName}" activity "${activity.name}"`;
|
|
344
407
|
for (const entry of activity.fields ?? [])
|
|
345
408
|
v2.checkEntry(entry, `${where}.fields "${entry.name}"`);
|
|
@@ -348,7 +411,7 @@ function validateActivity(v2, stageName, activity) {
|
|
|
348
411
|
v2.checkCondition(groq, `${where}.requirements "${name}"`);
|
|
349
412
|
for (const [key, groq] of effectBindings(activity.effects))
|
|
350
413
|
v2.tryParse(groq, `${where} effect binding "${key}"`);
|
|
351
|
-
validateActivityActions(v2, activity), activity.subworkflows !== void 0 && validateSubworkflows(v2, where, activity.subworkflows);
|
|
414
|
+
validateActivityActions(v2, activity), activity.subworkflows !== void 0 && validateSubworkflows({ v: v2, where, sub: activity.subworkflows });
|
|
352
415
|
}
|
|
353
416
|
function validateActivityActions(v2, activity) {
|
|
354
417
|
for (const a of activity.actions ?? []) {
|
|
@@ -357,7 +420,11 @@ function validateActivityActions(v2, activity) {
|
|
|
357
420
|
v2.tryParse(groq, `activity "${activity.name}" action "${a.name}" effect binding "${key}"`);
|
|
358
421
|
}
|
|
359
422
|
}
|
|
360
|
-
function validateSubworkflows(
|
|
423
|
+
function validateSubworkflows({
|
|
424
|
+
v: v2,
|
|
425
|
+
where,
|
|
426
|
+
sub
|
|
427
|
+
}) {
|
|
361
428
|
v2.tryParse(sub.forEach, `${where}.subworkflows.forEach`);
|
|
362
429
|
for (const [key, groq] of Object.entries(sub.with ?? {}))
|
|
363
430
|
v2.tryParse(groq, `${where}.subworkflows.with "${key}"`);
|
|
@@ -436,7 +503,11 @@ function globMatch(pattern, value) {
|
|
|
436
503
|
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
437
504
|
return new RegExp(`^${escaped}$`).test(value);
|
|
438
505
|
}
|
|
439
|
-
function guardMatches(
|
|
506
|
+
function guardMatches({
|
|
507
|
+
guard,
|
|
508
|
+
doc,
|
|
509
|
+
action
|
|
510
|
+
}) {
|
|
440
511
|
const m = guard.match;
|
|
441
512
|
if (!m.actions.includes(action) || m.types && m.types.length > 0 && !(doc.type !== void 0 && m.types.includes(doc.type)))
|
|
442
513
|
return !1;
|
|
@@ -465,7 +536,7 @@ async function evaluateMutationGuard(args) {
|
|
|
465
536
|
async function denyingGuards(args) {
|
|
466
537
|
const { guards, doc, context } = args, denied = [];
|
|
467
538
|
for (const guard of guards)
|
|
468
|
-
guardMatches(guard, doc, context.action) && (await evaluateMutationGuard({ guard, context }) || denied.push(guard));
|
|
539
|
+
guardMatches({ guard, doc, action: context.action }) && (await evaluateMutationGuard({ guard, context }) || denied.push(guard));
|
|
469
540
|
return denied;
|
|
470
541
|
}
|
|
471
542
|
async function instanceWriteDenials(args) {
|
|
@@ -636,6 +707,16 @@ ${lines}`
|
|
|
636
707
|
), this.name = "ActionParamsInvalidError", this.action = args.action, this.activity = args.activity, this.issues = args.issues;
|
|
637
708
|
}
|
|
638
709
|
}
|
|
710
|
+
class EffectOpsInvalidError extends Error {
|
|
711
|
+
effect;
|
|
712
|
+
issues;
|
|
713
|
+
constructor(args) {
|
|
714
|
+
const lines = args.issues.map((i) => ` - ${i}`).join(`
|
|
715
|
+
`);
|
|
716
|
+
super(`Effect "${args.effect}" returned invalid completion ops:
|
|
717
|
+
${lines}`), this.name = "EffectOpsInvalidError", this.effect = args.effect, this.issues = args.issues;
|
|
718
|
+
}
|
|
719
|
+
}
|
|
639
720
|
class RequiredFieldNotProvidedError extends Error {
|
|
640
721
|
definition;
|
|
641
722
|
missing;
|
|
@@ -712,38 +793,39 @@ class CascadeLimitError extends Error {
|
|
|
712
793
|
function buildSnapshot(args) {
|
|
713
794
|
const docs = [], knownIds = /* @__PURE__ */ new Set();
|
|
714
795
|
for (const { doc, resource } of args.docs) {
|
|
715
|
-
const uri = gdrFromResource(resource, doc._id), restamped = restampForSnapshot(doc, uri, resource);
|
|
796
|
+
const uri = gdrFromResource(resource, doc._id), restamped = restampForSnapshot({ doc, gdrUri: uri, resource });
|
|
716
797
|
docs.push(restamped), knownIds.add(uri);
|
|
717
798
|
}
|
|
718
799
|
return { docs, knownIds };
|
|
719
800
|
}
|
|
720
|
-
function restampForSnapshot(
|
|
801
|
+
function restampForSnapshot({
|
|
802
|
+
doc,
|
|
803
|
+
gdrUri: gdrUri2,
|
|
804
|
+
resource
|
|
805
|
+
}) {
|
|
721
806
|
return { ...rewriteRefsRecursive(doc, resource), _id: gdrUri2 };
|
|
722
807
|
}
|
|
723
808
|
function rewriteRefsRecursive(value, resource) {
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
809
|
+
return mapJsonStrings(
|
|
810
|
+
value,
|
|
811
|
+
(str, key) => (
|
|
812
|
+
// Only `_ref` string values become URIs; already-qualified ones pass through.
|
|
813
|
+
key === "_ref" && !str.includes(":") ? gdrFromResource(resource, str) : str
|
|
814
|
+
)
|
|
815
|
+
);
|
|
731
816
|
}
|
|
732
817
|
const WORKFLOW_INSTANCE_TYPE = "sanity.workflow.instance";
|
|
733
818
|
function parseDefinitionSnapshot(instance) {
|
|
734
819
|
try {
|
|
735
820
|
return JSON.parse(instance.definitionSnapshot);
|
|
736
821
|
} catch (err) {
|
|
737
|
-
|
|
738
|
-
`Failed to parse definitionSnapshot on instance "${instance._id}": ${err instanceof Error ? err.message : String(err)}`,
|
|
739
|
-
{ cause: err }
|
|
740
|
-
);
|
|
822
|
+
rethrowWithContext(err, `Failed to parse definitionSnapshot on instance "${instance._id}"`);
|
|
741
823
|
}
|
|
742
824
|
}
|
|
743
825
|
function collectWatchRefs(instance) {
|
|
744
826
|
const stage = findOpenStageEntry(instance);
|
|
745
827
|
return [
|
|
746
|
-
gdrRef(instance.workflowResource, instance._id, instance._type),
|
|
828
|
+
gdrRef({ res: instance.workflowResource, documentId: instance._id, type: instance._type }),
|
|
747
829
|
...instance.ancestors,
|
|
748
830
|
...entryDocRefs(instance.fields),
|
|
749
831
|
...entryDocRefs(stage?.fields),
|
|
@@ -801,13 +883,13 @@ async function hydrateSnapshot(args) {
|
|
|
801
883
|
loaded.push(held), visited.add(uri);
|
|
802
884
|
return;
|
|
803
885
|
}
|
|
804
|
-
const fetched = await loadByGdr(
|
|
805
|
-
client,
|
|
886
|
+
const fetched = await loadByGdr({
|
|
887
|
+
defaultClient: client,
|
|
806
888
|
clientForGdr,
|
|
807
|
-
instance.workflowResource,
|
|
889
|
+
defaultResource: instance.workflowResource,
|
|
808
890
|
uri,
|
|
809
891
|
perspective
|
|
810
|
-
);
|
|
892
|
+
});
|
|
811
893
|
fetched && (loaded.push(fetched), visited.add(uri));
|
|
812
894
|
};
|
|
813
895
|
loaded.push({ doc: instance, resource: instance.workflowResource }), visited.add(selfGdr(instance));
|
|
@@ -818,23 +900,28 @@ async function hydrateSnapshot(args) {
|
|
|
818
900
|
);
|
|
819
901
|
return buildSnapshot({ docs: loaded });
|
|
820
902
|
}
|
|
821
|
-
async function loadByGdr(
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
903
|
+
async function loadByGdr({
|
|
904
|
+
defaultClient,
|
|
905
|
+
clientForGdr,
|
|
906
|
+
defaultResource,
|
|
907
|
+
uri,
|
|
908
|
+
perspective
|
|
909
|
+
}) {
|
|
910
|
+
const parsed = tryParseGdr(uri);
|
|
911
|
+
if (parsed === void 0) {
|
|
912
|
+
const doc2 = await readDoc({ client: defaultClient, id: uri, perspective });
|
|
827
913
|
return doc2 ? { doc: doc2, resource: defaultResource } : null;
|
|
828
914
|
}
|
|
829
|
-
const routed = clientForGdr(parsed), doc = await readDoc(routed, parsed.documentId, perspective);
|
|
915
|
+
const routed = clientForGdr(parsed), doc = await readDoc({ client: routed, id: parsed.documentId, perspective });
|
|
830
916
|
return doc ? { doc, resource: resourceFromParsed(parsed) } : null;
|
|
831
917
|
}
|
|
832
|
-
async function readDoc(
|
|
918
|
+
async function readDoc({
|
|
919
|
+
client,
|
|
920
|
+
id,
|
|
921
|
+
perspective
|
|
922
|
+
}) {
|
|
833
923
|
return perspective === "raw" ? await client.getDocument(id) ?? null : await client.fetch("*[_id == $id][0]", { id }, { perspective }) ?? null;
|
|
834
924
|
}
|
|
835
|
-
function resourceFromParsed(parsed) {
|
|
836
|
-
return parsed.scheme === "dataset" ? { type: "dataset", id: `${parsed.projectId}.${parsed.dataset}` } : { type: parsed.scheme, id: parsed.resourceId ?? "" };
|
|
837
|
-
}
|
|
838
925
|
function collectEntryDocUris(resolvedFieldEntries) {
|
|
839
926
|
return entryDocRefs(resolvedFieldEntries).map((ref) => ref.id);
|
|
840
927
|
}
|
|
@@ -856,13 +943,13 @@ async function guardsForInstance(args) {
|
|
|
856
943
|
const { client, clientForGdr, instance } = args, stateUris = [
|
|
857
944
|
...collectEntryDocUris(instance.fields),
|
|
858
945
|
...instance.stages.flatMap((stage) => collectEntryDocUris(stage.fields))
|
|
859
|
-
], clients = resourceClientMap(
|
|
860
|
-
instance.workflowResource,
|
|
861
|
-
client,
|
|
946
|
+
], clients = resourceClientMap({
|
|
947
|
+
base: instance.workflowResource,
|
|
948
|
+
baseClient: client,
|
|
862
949
|
clientForGdr,
|
|
863
|
-
stateUris.map((uri) => tryParseGdr(uri))
|
|
864
|
-
), { query, params } = instanceGuardQuery(instance._id);
|
|
865
|
-
return queryGuardsAcross(clients.values(), query, params);
|
|
950
|
+
gdrs: stateUris.map((uri) => tryParseGdr(uri))
|
|
951
|
+
}), { query, params } = instanceGuardQuery(instance._id);
|
|
952
|
+
return queryGuardsAcross({ clients: clients.values(), query, params });
|
|
866
953
|
}
|
|
867
954
|
async function guardsForDefinition(args) {
|
|
868
955
|
const perClient = await guardsForDefinitionByClient(args);
|
|
@@ -870,8 +957,13 @@ async function guardsForDefinition(args) {
|
|
|
870
957
|
}
|
|
871
958
|
async function guardsForDefinitionByClient(args) {
|
|
872
959
|
const { client, clientForGdr, workflowResource, definition, definitions } = args, gdrs = definitions.flatMap(
|
|
873
|
-
(def) => guardIdRefs(def).map(({ idRef, stage }) => staticIdRefGdr(idRef, stage, def))
|
|
874
|
-
), clients = resourceClientMap(
|
|
960
|
+
(def) => guardIdRefs(def).map(({ idRef, stage }) => staticIdRefGdr({ idRef, stage, definition: def }))
|
|
961
|
+
), clients = resourceClientMap({
|
|
962
|
+
base: workflowResource,
|
|
963
|
+
baseClient: client,
|
|
964
|
+
clientForGdr,
|
|
965
|
+
gdrs
|
|
966
|
+
}), query = "*[_type == $t && sourceDefinition == $definition]", params = { t: GUARD_DOC_TYPE, definition };
|
|
875
967
|
return Promise.all(
|
|
876
968
|
[...clients.values()].map(async (resourceClient) => ({
|
|
877
969
|
client: resourceClient,
|
|
@@ -886,7 +978,11 @@ function guardIdRefs(definition) {
|
|
|
886
978
|
)
|
|
887
979
|
);
|
|
888
980
|
}
|
|
889
|
-
function staticIdRefGdr(
|
|
981
|
+
function staticIdRefGdr({
|
|
982
|
+
idRef,
|
|
983
|
+
stage,
|
|
984
|
+
definition
|
|
985
|
+
}) {
|
|
890
986
|
const read = /^\$fields\.([\w-]+)/.exec(idRef);
|
|
891
987
|
if (read === null) return;
|
|
892
988
|
const seed = entriesInScope(stage, definition).find((e) => e.name === read[1])?.initialValue;
|
|
@@ -906,14 +1002,12 @@ function gdrFromValue(value) {
|
|
|
906
1002
|
return typeof id == "string" ? tryParseGdr(id) : void 0;
|
|
907
1003
|
}
|
|
908
1004
|
}
|
|
909
|
-
function
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
}
|
|
916
|
-
function resourceClientMap(base, baseClient, clientForGdr, gdrs) {
|
|
1005
|
+
function resourceClientMap({
|
|
1006
|
+
base,
|
|
1007
|
+
baseClient,
|
|
1008
|
+
clientForGdr,
|
|
1009
|
+
gdrs
|
|
1010
|
+
}) {
|
|
917
1011
|
const map = /* @__PURE__ */ new Map([[resourceKey(base), baseClient]]);
|
|
918
1012
|
for (const parsed of gdrs) {
|
|
919
1013
|
if (parsed === void 0) continue;
|
|
@@ -922,7 +1016,11 @@ function resourceClientMap(base, baseClient, clientForGdr, gdrs) {
|
|
|
922
1016
|
}
|
|
923
1017
|
return map;
|
|
924
1018
|
}
|
|
925
|
-
async function queryGuardsAcross(
|
|
1019
|
+
async function queryGuardsAcross({
|
|
1020
|
+
clients,
|
|
1021
|
+
query,
|
|
1022
|
+
params
|
|
1023
|
+
}) {
|
|
926
1024
|
const perResource = await Promise.all(
|
|
927
1025
|
[...clients].map((c) => c.fetch(query, params))
|
|
928
1026
|
);
|
|
@@ -932,7 +1030,12 @@ function dedupById(guards) {
|
|
|
932
1030
|
return [...new Map(guards.map((g) => [g._id, g])).values()].sort((a, b) => a._id.localeCompare(b._id));
|
|
933
1031
|
}
|
|
934
1032
|
const SYNC_COMMIT = { visibility: "sync" }, GUARD_OWNER = "robot:workflow-engine", GUARD_LIFTED_PREDICATE = "true";
|
|
935
|
-
function resolveGuard(
|
|
1033
|
+
function resolveGuard({
|
|
1034
|
+
guard,
|
|
1035
|
+
instance,
|
|
1036
|
+
stageName,
|
|
1037
|
+
now
|
|
1038
|
+
}) {
|
|
936
1039
|
const ctx = { instance, now }, targets = resolveIdRefTargets(guard.match.idRefs, ctx);
|
|
937
1040
|
if (targets === null) return null;
|
|
938
1041
|
const resource = assertSingleResource(targets), types = resolveMatchTypes(targets, guard.match.types);
|
|
@@ -967,7 +1070,12 @@ async function upsertGuard(client, doc) {
|
|
|
967
1070
|
function resolvedStageGuards(args) {
|
|
968
1071
|
const stage = args.definition.stages.find((s) => s.name === args.stageName), out = [];
|
|
969
1072
|
for (const guard of stage?.guards ?? []) {
|
|
970
|
-
const resolved = resolveGuard(
|
|
1073
|
+
const resolved = resolveGuard({
|
|
1074
|
+
guard,
|
|
1075
|
+
instance: args.instance,
|
|
1076
|
+
stageName: args.stageName,
|
|
1077
|
+
now: args.now
|
|
1078
|
+
});
|
|
971
1079
|
resolved !== null && out.push({ client: args.clientForGdr(resolved.routeGdr), doc: resolved.doc });
|
|
972
1080
|
}
|
|
973
1081
|
return out;
|
|
@@ -1016,13 +1124,16 @@ function randomKey(length = 12) {
|
|
|
1016
1124
|
const bytes = new Uint8Array(length);
|
|
1017
1125
|
return globalThis.crypto.getRandomValues(bytes), [...bytes].map((b) => b.toString(16).padStart(2, "0")).join("").slice(0, length);
|
|
1018
1126
|
}
|
|
1127
|
+
function instanceDocId(tag) {
|
|
1128
|
+
return `${tag}.wf-instance.${randomKey()}`;
|
|
1129
|
+
}
|
|
1019
1130
|
function isUnevaluable(result) {
|
|
1020
1131
|
return result == null;
|
|
1021
1132
|
}
|
|
1022
1133
|
async function evaluateConditionOutcome(args) {
|
|
1023
1134
|
const { condition, snapshot, params } = args;
|
|
1024
1135
|
if (condition === void 0) return "satisfied";
|
|
1025
|
-
const result = await runGroq(condition, params, snapshot);
|
|
1136
|
+
const result = await runGroq({ groq: condition, params, snapshot });
|
|
1026
1137
|
return isUnevaluable(result) ? "unevaluable" : result ? "satisfied" : "unsatisfied";
|
|
1027
1138
|
}
|
|
1028
1139
|
async function evaluateCondition(args) {
|
|
@@ -1031,7 +1142,7 @@ async function evaluateCondition(args) {
|
|
|
1031
1142
|
async function evaluatePredicates(args) {
|
|
1032
1143
|
const out = {};
|
|
1033
1144
|
for (const [name, groq] of Object.entries(args.predicates ?? {})) {
|
|
1034
|
-
const result = await runGroq(groq, args.params, args.snapshot);
|
|
1145
|
+
const result = await runGroq({ groq, params: args.params, snapshot: args.snapshot });
|
|
1035
1146
|
out[name] = isUnevaluable(result) ? null : !!result;
|
|
1036
1147
|
}
|
|
1037
1148
|
return out;
|
|
@@ -1042,7 +1153,11 @@ async function evaluateRequirements(args) {
|
|
|
1042
1153
|
await evaluateCondition({ condition, snapshot: args.snapshot, params: args.params }) || unmet.push(name);
|
|
1043
1154
|
return unmet;
|
|
1044
1155
|
}
|
|
1045
|
-
async function runGroq(
|
|
1156
|
+
async function runGroq({
|
|
1157
|
+
groq,
|
|
1158
|
+
params,
|
|
1159
|
+
snapshot
|
|
1160
|
+
}) {
|
|
1046
1161
|
const tree = groqJs.parse(groq, { params });
|
|
1047
1162
|
return (await groqJs.evaluate(tree, { dataset: snapshot.docs, params })).get();
|
|
1048
1163
|
}
|
|
@@ -1172,15 +1287,19 @@ function derivePerspectiveFromFields(entries) {
|
|
|
1172
1287
|
async function resolveDeclaredFields(args) {
|
|
1173
1288
|
const { entryDefs, initialFields, ctx, randomKey: randomKey2 } = args;
|
|
1174
1289
|
if (entryDefs === void 0 || entryDefs.length === 0) return [];
|
|
1175
|
-
assertRequiredInputProvided(entryDefs, initialFields, ctx.definitionName);
|
|
1290
|
+
assertRequiredInputProvided({ entryDefs, initialFields, definitionName: ctx.definitionName });
|
|
1176
1291
|
const out = [];
|
|
1177
1292
|
for (const entry of entryDefs) {
|
|
1178
1293
|
const scopedCtx = { ...ctx, resolvedFields: out };
|
|
1179
|
-
out.push(await resolveOneEntry(entry, initialFields, scopedCtx, randomKey2));
|
|
1294
|
+
out.push(await resolveOneEntry({ entry, initialFields, ctx: scopedCtx, randomKey: randomKey2 }));
|
|
1180
1295
|
}
|
|
1181
1296
|
return out;
|
|
1182
1297
|
}
|
|
1183
|
-
function assertRequiredInputProvided(
|
|
1298
|
+
function assertRequiredInputProvided({
|
|
1299
|
+
entryDefs,
|
|
1300
|
+
initialFields,
|
|
1301
|
+
definitionName
|
|
1302
|
+
}) {
|
|
1184
1303
|
const missing = entryDefs.filter((entry) => entry.required === !0 && entry.initialValue?.type === "input").filter((entry) => {
|
|
1185
1304
|
const match = initialFields.find((s) => s.name === entry.name && s.type === entry.type);
|
|
1186
1305
|
return match === void 0 || match.value === void 0 || match.value === null;
|
|
@@ -1195,15 +1314,29 @@ const ARRAY_SLOT_TYPES = /* @__PURE__ */ new Set(["doc.refs", "array", "assignee
|
|
|
1195
1314
|
function defaultEntryValue(entryType) {
|
|
1196
1315
|
return ARRAY_SLOT_TYPES.has(entryType) ? [] : null;
|
|
1197
1316
|
}
|
|
1198
|
-
function resolveInputValue(
|
|
1317
|
+
function resolveInputValue({
|
|
1318
|
+
entry,
|
|
1319
|
+
initialFields,
|
|
1320
|
+
defaultValue
|
|
1321
|
+
}) {
|
|
1199
1322
|
const initMatch = initialFields.find((s) => s.name === entry.name && s.type === entry.type);
|
|
1200
1323
|
return initMatch === void 0 ? defaultValue : (assertInputValueShape(entry, initMatch.value), initMatch.value);
|
|
1201
1324
|
}
|
|
1202
|
-
function resolveFieldReadValue(
|
|
1325
|
+
function resolveFieldReadValue({
|
|
1326
|
+
source,
|
|
1327
|
+
ctx,
|
|
1328
|
+
defaultValue
|
|
1329
|
+
}) {
|
|
1203
1330
|
const target = (source.scope === "workflow" ? ctx.workflowFields ?? [] : ctx.resolvedFields ?? []).find((s) => s.name === source.field), targetValue = target === void 0 ? void 0 : target.value;
|
|
1204
1331
|
return (source.path !== void 0 ? getPath(targetValue, source.path) : targetValue) ?? defaultValue;
|
|
1205
1332
|
}
|
|
1206
|
-
async function resolveQueryValue(
|
|
1333
|
+
async function resolveQueryValue({
|
|
1334
|
+
entry,
|
|
1335
|
+
source,
|
|
1336
|
+
ctx,
|
|
1337
|
+
client,
|
|
1338
|
+
defaultValue
|
|
1339
|
+
}) {
|
|
1207
1340
|
const params = paramsForLake({
|
|
1208
1341
|
self: ctx.selfId ?? null,
|
|
1209
1342
|
fields: fieldMapFromResolved(ctx.resolvedFields ?? []),
|
|
@@ -1214,19 +1347,30 @@ async function resolveQueryValue(entry, source, ctx, client, defaultValue) {
|
|
|
1214
1347
|
});
|
|
1215
1348
|
try {
|
|
1216
1349
|
const fetchOptions = { perspective: ctx.perspective ?? DEFAULT_CONTENT_PERSPECTIVE }, result = await client.fetch(source.query, params, fetchOptions);
|
|
1217
|
-
return normalizeQueryResult(
|
|
1350
|
+
return normalizeQueryResult({
|
|
1351
|
+
entryType: entry.type,
|
|
1352
|
+
raw: result,
|
|
1353
|
+
workflowResource: ctx.workflowResource
|
|
1354
|
+
}) ?? defaultValue;
|
|
1218
1355
|
} catch (err) {
|
|
1219
|
-
|
|
1220
|
-
`Failed to resolve query entry "${entry.name}" (${entry.type}): ${err instanceof Error ? err.message : String(err)}`,
|
|
1221
|
-
{ cause: err }
|
|
1222
|
-
);
|
|
1356
|
+
rethrowWithContext(err, `Failed to resolve query entry "${entry.name}" (${entry.type})`);
|
|
1223
1357
|
}
|
|
1224
1358
|
}
|
|
1225
|
-
async function resolveEntryValue(
|
|
1359
|
+
async function resolveEntryValue({
|
|
1360
|
+
entry,
|
|
1361
|
+
initialFields,
|
|
1362
|
+
ctx,
|
|
1363
|
+
defaultValue
|
|
1364
|
+
}) {
|
|
1226
1365
|
const source = entry.initialValue;
|
|
1227
|
-
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;
|
|
1228
|
-
}
|
|
1229
|
-
function buildResolvedEntry(
|
|
1366
|
+
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;
|
|
1367
|
+
}
|
|
1368
|
+
function buildResolvedEntry({
|
|
1369
|
+
entry,
|
|
1370
|
+
value,
|
|
1371
|
+
_key,
|
|
1372
|
+
now
|
|
1373
|
+
}) {
|
|
1230
1374
|
return {
|
|
1231
1375
|
_key,
|
|
1232
1376
|
_type: entry.type,
|
|
@@ -1239,8 +1383,13 @@ function buildResolvedEntry(entry, value, _key, now) {
|
|
|
1239
1383
|
...entry.type === "array" ? { of: entry.of ?? [] } : {}
|
|
1240
1384
|
};
|
|
1241
1385
|
}
|
|
1242
|
-
async function resolveOneEntry(
|
|
1243
|
-
|
|
1386
|
+
async function resolveOneEntry({
|
|
1387
|
+
entry,
|
|
1388
|
+
initialFields,
|
|
1389
|
+
ctx,
|
|
1390
|
+
randomKey: randomKey2
|
|
1391
|
+
}) {
|
|
1392
|
+
const defaultValue = defaultEntryValue(entry.type), value = await resolveEntryValue({ entry, initialFields, ctx, defaultValue });
|
|
1244
1393
|
if (entry.initialValue?.type === "query") {
|
|
1245
1394
|
const issues = checkFieldValue({
|
|
1246
1395
|
entryType: entry.type,
|
|
@@ -1248,7 +1397,7 @@ async function resolveOneEntry(entry, initialFields, ctx, randomKey2) {
|
|
|
1248
1397
|
fields: entry.fields,
|
|
1249
1398
|
of: entry.of
|
|
1250
1399
|
});
|
|
1251
|
-
return issues !== void 0 ? (ctx.recordDiscard?.({ field: entry.name, detail: issues.join("; ") }), buildResolvedEntry(entry, defaultValue, randomKey2(), ctx.now)) : buildResolvedEntry(entry, value, randomKey2(), ctx.now);
|
|
1400
|
+
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 });
|
|
1252
1401
|
}
|
|
1253
1402
|
return validateFieldValue({
|
|
1254
1403
|
entryType: entry.type,
|
|
@@ -1256,7 +1405,7 @@ async function resolveOneEntry(entry, initialFields, ctx, randomKey2) {
|
|
|
1256
1405
|
value,
|
|
1257
1406
|
fields: entry.fields,
|
|
1258
1407
|
of: entry.of
|
|
1259
|
-
}), buildResolvedEntry(entry, value, randomKey2(), ctx.now);
|
|
1408
|
+
}), buildResolvedEntry({ entry, value, _key: randomKey2(), now: ctx.now });
|
|
1260
1409
|
}
|
|
1261
1410
|
function fieldMapFromResolved(entries) {
|
|
1262
1411
|
const out = {};
|
|
@@ -1301,35 +1450,57 @@ function assertGdrShape(value, context) {
|
|
|
1301
1450
|
`Invalid GDR for ${context}: \`type\` (schema name) must be a non-empty string. Got ${JSON.stringify(v2.type)}.`
|
|
1302
1451
|
);
|
|
1303
1452
|
}
|
|
1304
|
-
function normalizeQueryResult(
|
|
1453
|
+
function normalizeQueryResult({
|
|
1454
|
+
entryType,
|
|
1455
|
+
raw,
|
|
1456
|
+
workflowResource
|
|
1457
|
+
}) {
|
|
1305
1458
|
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;
|
|
1306
1459
|
}
|
|
1307
|
-
function toGdrUri(docId, workflowResource) {
|
|
1308
|
-
return isGdrUri(docId) ? docId : gdrFromResource(workflowResource, docId);
|
|
1309
|
-
}
|
|
1310
1460
|
function coerceGdrShape(raw, workflowResource) {
|
|
1311
1461
|
if (!("id" in raw) || !("type" in raw)) return null;
|
|
1312
1462
|
const r = raw;
|
|
1313
|
-
return typeof r.id != "string" || typeof r.type != "string" ? null : isGdrUri(r.id) ? { id: r.id, type: r.type } : workflowResource ? { id:
|
|
1463
|
+
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;
|
|
1314
1464
|
}
|
|
1315
1465
|
function coerceRefEnvelope(raw, workflowResource) {
|
|
1316
1466
|
if (!("_ref" in raw)) return null;
|
|
1317
1467
|
const r = raw;
|
|
1318
|
-
return typeof r._ref != "string" || !workflowResource ? null : { id:
|
|
1468
|
+
return typeof r._ref != "string" || !workflowResource ? null : { id: toPhysicalGdr(r._ref, workflowResource), type: "document" };
|
|
1319
1469
|
}
|
|
1320
1470
|
function coerceToGdr(raw, workflowResource) {
|
|
1321
|
-
return raw == null ? null : typeof raw == "object" ? coerceGdrShape(raw, workflowResource) ?? coerceRefEnvelope(raw, workflowResource) : typeof raw == "string" && workflowResource ? { id:
|
|
1322
|
-
}
|
|
1323
|
-
function
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1471
|
+
return raw == null ? null : typeof raw == "object" ? coerceGdrShape(raw, workflowResource) ?? coerceRefEnvelope(raw, workflowResource) : typeof raw == "string" && workflowResource ? { id: toPhysicalGdr(raw, workflowResource), type: "document" } : null;
|
|
1472
|
+
}
|
|
1473
|
+
function loadCallContext({
|
|
1474
|
+
client,
|
|
1475
|
+
instanceId,
|
|
1476
|
+
options
|
|
1477
|
+
}) {
|
|
1478
|
+
return loadContext({
|
|
1479
|
+
client,
|
|
1480
|
+
instanceId,
|
|
1481
|
+
options: {
|
|
1482
|
+
...options?.clock ? { clock: options.clock } : {},
|
|
1483
|
+
...options?.clientForGdr ? { clientForGdr: options.clientForGdr } : {}
|
|
1484
|
+
}
|
|
1330
1485
|
});
|
|
1331
1486
|
}
|
|
1332
|
-
async function
|
|
1487
|
+
async function retryOnRevisionConflict(args) {
|
|
1488
|
+
const { client, instanceId, options, commit, onExhausted } = args;
|
|
1489
|
+
for (let attempt = 1; attempt <= CONCURRENT_FIRE_ACTION_MAX_ATTEMPTS; attempt++) {
|
|
1490
|
+
const ctx = await loadCallContext({ client, instanceId, options });
|
|
1491
|
+
try {
|
|
1492
|
+
return await commit(ctx);
|
|
1493
|
+
} catch (error) {
|
|
1494
|
+
if (!isRevisionConflict(error)) throw error;
|
|
1495
|
+
}
|
|
1496
|
+
}
|
|
1497
|
+
throw onExhausted();
|
|
1498
|
+
}
|
|
1499
|
+
async function loadContext({
|
|
1500
|
+
client,
|
|
1501
|
+
instanceId,
|
|
1502
|
+
options
|
|
1503
|
+
}) {
|
|
1333
1504
|
const instance = await client.getDocument(instanceId);
|
|
1334
1505
|
if (!instance)
|
|
1335
1506
|
throw new Error(`Workflow instance ${instanceId} not found`);
|
|
@@ -1344,12 +1515,21 @@ async function loadContext(client, instanceId, options) {
|
|
|
1344
1515
|
async function ctxConditionParams(ctx, opts) {
|
|
1345
1516
|
const base = buildParams({ instance: ctx.instance, now: ctx.now, snapshot: ctx.snapshot }), fields = {
|
|
1346
1517
|
...base.fields,
|
|
1347
|
-
...scopedFieldOverlay(
|
|
1518
|
+
...scopedFieldOverlay({
|
|
1519
|
+
instance: ctx.instance,
|
|
1520
|
+
snapshot: ctx.snapshot,
|
|
1521
|
+
activityName: opts?.activityName
|
|
1522
|
+
})
|
|
1348
1523
|
}, params = {
|
|
1349
1524
|
...base,
|
|
1350
1525
|
fields,
|
|
1351
1526
|
actor: opts?.actor,
|
|
1352
|
-
assigned: opts?.activityName !== void 0 ? assignedFor(
|
|
1527
|
+
assigned: opts?.activityName !== void 0 ? assignedFor({
|
|
1528
|
+
instance: ctx.instance,
|
|
1529
|
+
activityName: opts.activityName,
|
|
1530
|
+
actor: opts?.actor,
|
|
1531
|
+
roleAliases: ctx.definition.roleAliases
|
|
1532
|
+
}) : !1,
|
|
1353
1533
|
...opts?.vars
|
|
1354
1534
|
};
|
|
1355
1535
|
return { ...await evaluatePredicates({
|
|
@@ -1358,15 +1538,23 @@ async function ctxConditionParams(ctx, opts) {
|
|
|
1358
1538
|
params
|
|
1359
1539
|
}), ...params };
|
|
1360
1540
|
}
|
|
1361
|
-
async function ctxEvaluateConditionOutcome(
|
|
1541
|
+
async function ctxEvaluateConditionOutcome({
|
|
1542
|
+
ctx,
|
|
1543
|
+
condition,
|
|
1544
|
+
opts
|
|
1545
|
+
}) {
|
|
1362
1546
|
return condition === void 0 ? "satisfied" : evaluateConditionOutcome({
|
|
1363
1547
|
condition,
|
|
1364
1548
|
snapshot: ctx.snapshot,
|
|
1365
1549
|
params: await ctxConditionParams(ctx, opts)
|
|
1366
1550
|
});
|
|
1367
1551
|
}
|
|
1368
|
-
async function ctxEvaluateCondition(
|
|
1369
|
-
|
|
1552
|
+
async function ctxEvaluateCondition({
|
|
1553
|
+
ctx,
|
|
1554
|
+
condition,
|
|
1555
|
+
opts
|
|
1556
|
+
}) {
|
|
1557
|
+
return await ctxEvaluateConditionOutcome({ ctx, condition, opts }) === "satisfied";
|
|
1370
1558
|
}
|
|
1371
1559
|
async function buildEngineContext(args) {
|
|
1372
1560
|
const { client, clientForGdr, instance, definition } = args, clock = args.clock ?? wallClock;
|
|
@@ -1430,10 +1618,14 @@ async function resolveActivityFieldEntries(args) {
|
|
|
1430
1618
|
async function resolveBindings(args) {
|
|
1431
1619
|
const resolved = {};
|
|
1432
1620
|
for (const [key, groq] of Object.entries(args.bindings ?? {}))
|
|
1433
|
-
resolved[key] = await runGroq(groq, args.params, args.snapshot);
|
|
1621
|
+
resolved[key] = await runGroq({ groq, params: args.params, snapshot: args.snapshot });
|
|
1434
1622
|
return { ...resolved, ...args.staticInput };
|
|
1435
1623
|
}
|
|
1436
|
-
async function reload(
|
|
1624
|
+
async function reload({
|
|
1625
|
+
client,
|
|
1626
|
+
instanceId,
|
|
1627
|
+
tag
|
|
1628
|
+
}) {
|
|
1437
1629
|
const doc = await client.getDocument(instanceId);
|
|
1438
1630
|
if (!doc)
|
|
1439
1631
|
throw new Error(`Workflow instance ${instanceId} not found`);
|
|
@@ -1542,13 +1734,25 @@ async function persist(ctx, mutation) {
|
|
|
1542
1734
|
const actorForPriming = void 0;
|
|
1543
1735
|
for (const body of pendingCreates)
|
|
1544
1736
|
try {
|
|
1545
|
-
await primeInitialStage(
|
|
1546
|
-
ctx.client,
|
|
1547
|
-
body._id,
|
|
1548
|
-
actorForPriming,
|
|
1549
|
-
ctx.clientForGdr,
|
|
1550
|
-
ctx.clock
|
|
1551
|
-
), await
|
|
1737
|
+
await primeInitialStage({
|
|
1738
|
+
client: ctx.client,
|
|
1739
|
+
instanceId: body._id,
|
|
1740
|
+
actor: actorForPriming,
|
|
1741
|
+
clientForGdr: ctx.clientForGdr,
|
|
1742
|
+
clock: ctx.clock
|
|
1743
|
+
}), await cascadeAutoTransitions({
|
|
1744
|
+
client: ctx.client,
|
|
1745
|
+
instanceId: body._id,
|
|
1746
|
+
actor: actorForPriming,
|
|
1747
|
+
clientForGdr: ctx.clientForGdr,
|
|
1748
|
+
clock: ctx.clock
|
|
1749
|
+
}), await propagateToAncestors({
|
|
1750
|
+
client: ctx.client,
|
|
1751
|
+
instanceId: body._id,
|
|
1752
|
+
actor: actorForPriming,
|
|
1753
|
+
clientForGdr: ctx.clientForGdr,
|
|
1754
|
+
clock: ctx.clock
|
|
1755
|
+
});
|
|
1552
1756
|
} catch (cause) {
|
|
1553
1757
|
throw cause instanceof WorkflowStateDivergedError ? cause : new WorkflowStateDivergedError({
|
|
1554
1758
|
instanceId: ctx.instance._id,
|
|
@@ -1592,159 +1796,16 @@ function findCurrentStageEntry(instance) {
|
|
|
1592
1796
|
function findCurrentActivities(instance) {
|
|
1593
1797
|
return findCurrentStageEntry(instance)?.activities ?? [];
|
|
1594
1798
|
}
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
try {
|
|
1606
|
-
let patch = args.client.patch(args.instanceId).set(args.restore);
|
|
1607
|
-
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);
|
|
1608
|
-
} catch (rollbackError) {
|
|
1609
|
-
throw new WorkflowStateDivergedError({
|
|
1610
|
-
instanceId: args.instanceId,
|
|
1611
|
-
guardError,
|
|
1612
|
-
rollbackError,
|
|
1613
|
-
reason: "rollback of the committed move failed"
|
|
1614
|
-
});
|
|
1615
|
-
}
|
|
1616
|
-
throw guardError instanceof PartialGuardDeployError ? new WorkflowStateDivergedError({
|
|
1617
|
-
instanceId: args.instanceId,
|
|
1618
|
-
guardError,
|
|
1619
|
-
reason: "a multi-guard deploy partially applied; the rolled-back move left orphaned guard locks"
|
|
1620
|
-
}) : guardError;
|
|
1621
|
-
}
|
|
1622
|
-
}
|
|
1623
|
-
async function persistThenDeploy(ctx, mutation, deploy) {
|
|
1624
|
-
const spawned = mutation.pendingCreates.length > 0, committed = await persist(ctx, mutation);
|
|
1625
|
-
await deployOrRollback({
|
|
1626
|
-
client: ctx.client,
|
|
1627
|
-
instanceId: ctx.instance._id,
|
|
1628
|
-
committedRev: committed._rev,
|
|
1629
|
-
restore: instanceStateFields(ctx.instance),
|
|
1630
|
-
unset: ctx.instance.completedAt === void 0 ? ["completedAt"] : [],
|
|
1631
|
-
reversible: !spawned,
|
|
1632
|
-
deploy
|
|
1633
|
-
});
|
|
1634
|
-
}
|
|
1635
|
-
async function refreshStageGuards(ctx, mutation, stageName) {
|
|
1636
|
-
await deployStageGuards({
|
|
1637
|
-
client: ctx.client,
|
|
1638
|
-
clientForGdr: ctx.clientForGdr,
|
|
1639
|
-
instance: materializeInstance(ctx.instance, mutation),
|
|
1640
|
-
definition: ctx.definition,
|
|
1641
|
-
stageName,
|
|
1642
|
-
now: ctx.now
|
|
1643
|
-
});
|
|
1644
|
-
}
|
|
1645
|
-
async function completeEffect(args) {
|
|
1646
|
-
const { client, instanceId, effectKey, status, outputs, detail, error, durationMs, options } = args, ctx = await loadContext(client, instanceId, {
|
|
1647
|
-
...options?.clock ? { clock: options.clock } : {},
|
|
1648
|
-
...options?.clientForGdr ? { clientForGdr: options.clientForGdr } : {}
|
|
1649
|
-
});
|
|
1650
|
-
return commitCompleteEffect(
|
|
1651
|
-
ctx,
|
|
1652
|
-
effectKey,
|
|
1653
|
-
status,
|
|
1654
|
-
outputs,
|
|
1655
|
-
detail,
|
|
1656
|
-
error,
|
|
1657
|
-
durationMs,
|
|
1658
|
-
options?.actor
|
|
1659
|
-
);
|
|
1660
|
-
}
|
|
1661
|
-
function buildEffectHistoryEntry(pending, outcome) {
|
|
1662
|
-
const { status, ranAt, actor, detail, error, durationMs, outputs } = outcome, resolvedActor = actor ?? pending.actor;
|
|
1663
|
-
return {
|
|
1664
|
-
_key: pending._key,
|
|
1665
|
-
name: pending.name,
|
|
1666
|
-
...pending.title !== void 0 ? { title: pending.title } : {},
|
|
1667
|
-
...pending.description !== void 0 ? { description: pending.description } : {},
|
|
1668
|
-
params: pending.params,
|
|
1669
|
-
origin: pending.origin,
|
|
1670
|
-
...resolvedActor !== void 0 ? { actor: resolvedActor } : {},
|
|
1671
|
-
ranAt,
|
|
1672
|
-
...durationMs !== void 0 ? { durationMs } : {},
|
|
1673
|
-
status,
|
|
1674
|
-
...detail !== void 0 ? { detail } : {},
|
|
1675
|
-
...error !== void 0 ? { error } : {},
|
|
1676
|
-
...outputs !== void 0 ? { outputs } : {}
|
|
1677
|
-
};
|
|
1678
|
-
}
|
|
1679
|
-
function upsertEffectsContext(mutation, effectName, outputs) {
|
|
1680
|
-
const existingIndex = mutation.effectsContext.findIndex((e) => e.name === effectName), entry = effectsContextJsonEntry(effectName, outputs);
|
|
1681
|
-
existingIndex >= 0 ? mutation.effectsContext[existingIndex] = entry : mutation.effectsContext.push(entry);
|
|
1682
|
-
}
|
|
1683
|
-
async function commitCompleteEffect(ctx, effectKey, status, outputs, detail, error, durationMs, actor) {
|
|
1684
|
-
const pending = ctx.instance.pendingEffects.find((e) => e._key === effectKey);
|
|
1685
|
-
if (pending === void 0)
|
|
1686
|
-
throw new Error(`Pending effect "${effectKey}" not found on instance ${ctx.instance._id}`);
|
|
1687
|
-
const mutation = startMutation(ctx.instance);
|
|
1688
|
-
mutation.pendingEffects = mutation.pendingEffects.filter((e) => e._key !== effectKey);
|
|
1689
|
-
const ranAt = ctx.now;
|
|
1690
|
-
mutation.effectHistory.push(
|
|
1691
|
-
buildEffectHistoryEntry(pending, { status, ranAt, actor, detail, error, durationMs, outputs })
|
|
1692
|
-
);
|
|
1693
|
-
const wroteEffectsContext = status === "done" && outputs !== void 0;
|
|
1694
|
-
return wroteEffectsContext && upsertEffectsContext(mutation, pending.name, outputs), mutation.history.push({
|
|
1695
|
-
_key: randomKey(),
|
|
1696
|
-
_type: "effectCompleted",
|
|
1697
|
-
at: ranAt,
|
|
1698
|
-
effectKey,
|
|
1699
|
-
effect: pending.name,
|
|
1700
|
-
status,
|
|
1701
|
-
...outputs !== void 0 ? { outputs } : {},
|
|
1702
|
-
...detail !== void 0 ? { detail } : {},
|
|
1703
|
-
...actor !== void 0 ? { actor } : {}
|
|
1704
|
-
}), await persistThenDeploy(
|
|
1705
|
-
ctx,
|
|
1706
|
-
mutation,
|
|
1707
|
-
() => wroteEffectsContext ? refreshStageGuards(ctx, mutation, ctx.instance.currentStage) : Promise.resolve()
|
|
1708
|
-
), { effectKey, status };
|
|
1709
|
-
}
|
|
1710
|
-
function buildQueuedEffect(effect, origin, params, actor, now) {
|
|
1711
|
-
const key = randomKey(), pending = {
|
|
1712
|
-
_key: key,
|
|
1713
|
-
_type: "pendingEffect",
|
|
1714
|
-
name: effect.name,
|
|
1715
|
-
...effect.title !== void 0 ? { title: effect.title } : {},
|
|
1716
|
-
...effect.description !== void 0 ? { description: effect.description } : {},
|
|
1717
|
-
...effect.bindings !== void 0 ? { bindings: effect.bindings } : {},
|
|
1718
|
-
params,
|
|
1719
|
-
origin,
|
|
1720
|
-
...actor !== void 0 ? { actor } : {},
|
|
1721
|
-
queuedAt: now
|
|
1722
|
-
}, history = {
|
|
1723
|
-
_key: randomKey(),
|
|
1724
|
-
_type: "effectQueued",
|
|
1725
|
-
at: now,
|
|
1726
|
-
effectKey: key,
|
|
1727
|
-
effect: effect.name,
|
|
1728
|
-
origin
|
|
1729
|
-
};
|
|
1730
|
-
return { pending, history };
|
|
1731
|
-
}
|
|
1732
|
-
async function queueEffects(ctx, mutation, effects, origin, actor, opts) {
|
|
1733
|
-
if (!effects || effects.length === 0) return;
|
|
1734
|
-
const now = ctx.now, liveCtx = { ...ctx, instance: materializeInstance(ctx.instance, mutation) }, params = await ctxConditionParams(liveCtx, {
|
|
1735
|
-
...opts?.activityName !== void 0 ? { activityName: opts.activityName } : {},
|
|
1736
|
-
...actor !== void 0 ? { actor } : {},
|
|
1737
|
-
// Caller-supplied action params, readable in bindings as `$params.<name>`.
|
|
1738
|
-
vars: { params: opts?.callerParams ?? {} }
|
|
1739
|
-
});
|
|
1740
|
-
for (const effect of effects) {
|
|
1741
|
-
const resolved = await resolveBindings({
|
|
1742
|
-
bindings: effect.bindings,
|
|
1743
|
-
staticInput: effect.input,
|
|
1744
|
-
snapshot: ctx.snapshot,
|
|
1745
|
-
params
|
|
1746
|
-
}), { pending, history } = buildQueuedEffect(effect, origin, resolved, actor, now);
|
|
1747
|
-
mutation.pendingEffects.push(pending), mutation.history.push(history);
|
|
1799
|
+
function resolveStaticValueExpr(src, ctx) {
|
|
1800
|
+
switch (src.type) {
|
|
1801
|
+
case "literal":
|
|
1802
|
+
return src.value;
|
|
1803
|
+
case "param":
|
|
1804
|
+
return ctx.params?.[src.param];
|
|
1805
|
+
case "actor":
|
|
1806
|
+
return ctx.actor;
|
|
1807
|
+
case "now":
|
|
1808
|
+
return ctx.now;
|
|
1748
1809
|
}
|
|
1749
1810
|
}
|
|
1750
1811
|
function fieldQueryDiscardedEntry(args) {
|
|
@@ -1757,7 +1818,11 @@ function fieldQueryDiscardedEntry(args) {
|
|
|
1757
1818
|
detail: args.detail
|
|
1758
1819
|
};
|
|
1759
1820
|
}
|
|
1760
|
-
function recordFieldDiscards(
|
|
1821
|
+
function recordFieldDiscards({
|
|
1822
|
+
target,
|
|
1823
|
+
scope,
|
|
1824
|
+
at
|
|
1825
|
+
}) {
|
|
1761
1826
|
return (discard) => target.push(fieldQueryDiscardedEntry({ scope, field: discard.field, detail: discard.detail, at }));
|
|
1762
1827
|
}
|
|
1763
1828
|
function applyActivityStatusChange(args) {
|
|
@@ -1805,18 +1870,6 @@ function stageTransitionHistory(args) {
|
|
|
1805
1870
|
}
|
|
1806
1871
|
];
|
|
1807
1872
|
}
|
|
1808
|
-
function resolveStaticValueExpr(src, ctx) {
|
|
1809
|
-
switch (src.type) {
|
|
1810
|
-
case "literal":
|
|
1811
|
-
return src.value;
|
|
1812
|
-
case "param":
|
|
1813
|
-
return ctx.params?.[src.param];
|
|
1814
|
-
case "actor":
|
|
1815
|
-
return ctx.actor;
|
|
1816
|
-
case "now":
|
|
1817
|
-
return ctx.now;
|
|
1818
|
-
}
|
|
1819
|
-
}
|
|
1820
1873
|
const FIELD_OP_TYPES = [
|
|
1821
1874
|
"field.set",
|
|
1822
1875
|
"field.unset",
|
|
@@ -1827,7 +1880,11 @@ const FIELD_OP_TYPES = [
|
|
|
1827
1880
|
function isFieldOp(summary) {
|
|
1828
1881
|
return FIELD_OP_TYPES.includes(summary.opType);
|
|
1829
1882
|
}
|
|
1830
|
-
function validateActionParams(
|
|
1883
|
+
function validateActionParams({
|
|
1884
|
+
action,
|
|
1885
|
+
activityName,
|
|
1886
|
+
callerParams
|
|
1887
|
+
}) {
|
|
1831
1888
|
const declared = action.params ?? [], params = callerParams ?? {}, issues = [];
|
|
1832
1889
|
for (const decl of declared) {
|
|
1833
1890
|
const value = params[decl.name], present = decl.name in params && value !== void 0 && value !== null;
|
|
@@ -1862,7 +1919,7 @@ function checkParamType(value, decl) {
|
|
|
1862
1919
|
case "doc.ref":
|
|
1863
1920
|
return hasStringField(value, "id");
|
|
1864
1921
|
case "doc.refs":
|
|
1865
|
-
return Array.isArray(value) && value.every((
|
|
1922
|
+
return Array.isArray(value) && value.every((ref) => checkParamType(ref, { type: "doc.ref" }));
|
|
1866
1923
|
case "json":
|
|
1867
1924
|
return !0;
|
|
1868
1925
|
default:
|
|
@@ -1898,12 +1955,27 @@ function opAppliedEntry(args) {
|
|
|
1898
1955
|
...origin.action !== void 0 ? { action: origin.action } : {},
|
|
1899
1956
|
...origin.transition !== void 0 ? { transition: origin.transition } : {},
|
|
1900
1957
|
...origin.edit === !0 ? { edit: !0 } : {},
|
|
1958
|
+
...origin.effect !== void 0 ? { effect: origin.effect } : {},
|
|
1901
1959
|
opType: summary.opType,
|
|
1902
1960
|
...summary.target !== void 0 ? { target: summary.target } : {},
|
|
1903
1961
|
...summary.resolved !== void 0 ? { resolved: summary.resolved } : {},
|
|
1904
1962
|
...actor !== void 0 ? { actor } : {}
|
|
1905
1963
|
};
|
|
1906
1964
|
}
|
|
1965
|
+
function validateEffectOps(ops, effectName) {
|
|
1966
|
+
const result = v__namespace.safeParse(v__namespace.array(schema.StoredFieldOpSchema), ops);
|
|
1967
|
+
if (!result.success)
|
|
1968
|
+
throw new EffectOpsInvalidError({ effect: effectName, issues: formatIssues(result.issues) });
|
|
1969
|
+
const activityScoped = result.output.find((op) => op.target.scope === "activity");
|
|
1970
|
+
if (activityScoped !== void 0)
|
|
1971
|
+
throw new EffectOpsInvalidError({
|
|
1972
|
+
effect: effectName,
|
|
1973
|
+
issues: [
|
|
1974
|
+
`${activityScoped.type} targets an activity-scope field \u2014 an effect's completion ops write workflow- or stage-scope fields`
|
|
1975
|
+
]
|
|
1976
|
+
});
|
|
1977
|
+
return result.output;
|
|
1978
|
+
}
|
|
1907
1979
|
function applyOp(op, ctx) {
|
|
1908
1980
|
switch (op.type) {
|
|
1909
1981
|
case "field.set":
|
|
@@ -1959,7 +2031,7 @@ function applyFieldUpdateWhere(op, ctx) {
|
|
|
1959
2031
|
return setEntryValue(
|
|
1960
2032
|
entry,
|
|
1961
2033
|
rows.map(
|
|
1962
|
-
(row) => evalOpPredicate(op.where, row, ctx) ? { ...row, ...merge } : row
|
|
2034
|
+
(row) => evalOpPredicate({ p: op.where, row, ctx }) ? { ...row, ...merge } : row
|
|
1963
2035
|
)
|
|
1964
2036
|
), { opType: op.type, target: op.target, resolved: { merge } };
|
|
1965
2037
|
}
|
|
@@ -1967,7 +2039,7 @@ function applyFieldRemoveWhere(op, ctx) {
|
|
|
1967
2039
|
const entry = locateEntry(ctx, op.target), rows = requireArrayValue(entry, op);
|
|
1968
2040
|
return setEntryValue(
|
|
1969
2041
|
entry,
|
|
1970
|
-
rows.filter((row) => !evalOpPredicate(op.where, row, ctx))
|
|
2042
|
+
rows.filter((row) => !evalOpPredicate({ p: op.where, row, ctx }))
|
|
1971
2043
|
), { opType: op.type, target: op.target };
|
|
1972
2044
|
}
|
|
1973
2045
|
function requireArrayValue(entry, op) {
|
|
@@ -1978,7 +2050,7 @@ function requireArrayValue(entry, op) {
|
|
|
1978
2050
|
return entry.value;
|
|
1979
2051
|
}
|
|
1980
2052
|
function applyStatusSet(op, ctx) {
|
|
1981
|
-
const entry =
|
|
2053
|
+
const entry = findCurrentActivityEntry(ctx.mutation, op.activity);
|
|
1982
2054
|
if (entry === void 0)
|
|
1983
2055
|
throw new Error(
|
|
1984
2056
|
`status.set targets activity "${op.activity}" which has no entry in the open stage`
|
|
@@ -2033,24 +2105,247 @@ function resolveOpValue(src, ctx) {
|
|
|
2033
2105
|
}
|
|
2034
2106
|
}
|
|
2035
2107
|
}
|
|
2036
|
-
function entryValue(entries, name) {
|
|
2037
|
-
return entries?.find((s) => s.name === name)?.value;
|
|
2108
|
+
function entryValue(entries, name) {
|
|
2109
|
+
return entries?.find((s) => s.name === name)?.value;
|
|
2110
|
+
}
|
|
2111
|
+
function readEntryFromMutation(ctx, src) {
|
|
2112
|
+
const scopes = src.scope !== void 0 ? [src.scope] : ["stage", "workflow"], stageEntry = findOpenStageEntry(ctx.mutation);
|
|
2113
|
+
for (const scope of scopes) {
|
|
2114
|
+
const host = scope === "workflow" ? ctx.mutation.fields : stageEntry?.fields, value = entryValue(host, src.field);
|
|
2115
|
+
if (value !== void 0) return value;
|
|
2116
|
+
}
|
|
2117
|
+
}
|
|
2118
|
+
function evalOpPredicate({
|
|
2119
|
+
p,
|
|
2120
|
+
row,
|
|
2121
|
+
ctx
|
|
2122
|
+
}) {
|
|
2123
|
+
switch (p.type) {
|
|
2124
|
+
case "all":
|
|
2125
|
+
return p.of.every((sub) => evalOpPredicate({ p: sub, row, ctx }));
|
|
2126
|
+
case "any":
|
|
2127
|
+
return p.of.some((sub) => evalOpPredicate({ p: sub, row, ctx }));
|
|
2128
|
+
case "field":
|
|
2129
|
+
return row[p.field] === resolveOpValue(p.equals, ctx);
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
async function deployOrRollback(args) {
|
|
2133
|
+
try {
|
|
2134
|
+
await args.deploy();
|
|
2135
|
+
} catch (guardError) {
|
|
2136
|
+
if (!args.reversible)
|
|
2137
|
+
throw new WorkflowStateDivergedError({
|
|
2138
|
+
instanceId: args.instanceId,
|
|
2139
|
+
guardError,
|
|
2140
|
+
reason: "the move created child instances a parent-only rollback cannot undo"
|
|
2141
|
+
});
|
|
2142
|
+
try {
|
|
2143
|
+
let patch = args.client.patch(args.instanceId).set(args.restore);
|
|
2144
|
+
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);
|
|
2145
|
+
} catch (rollbackError) {
|
|
2146
|
+
throw new WorkflowStateDivergedError({
|
|
2147
|
+
instanceId: args.instanceId,
|
|
2148
|
+
guardError,
|
|
2149
|
+
rollbackError,
|
|
2150
|
+
reason: "rollback of the committed move failed"
|
|
2151
|
+
});
|
|
2152
|
+
}
|
|
2153
|
+
throw guardError instanceof PartialGuardDeployError ? new WorkflowStateDivergedError({
|
|
2154
|
+
instanceId: args.instanceId,
|
|
2155
|
+
guardError,
|
|
2156
|
+
reason: "a multi-guard deploy partially applied; the rolled-back move left orphaned guard locks"
|
|
2157
|
+
}) : guardError;
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
async function persistThenDeploy({
|
|
2161
|
+
ctx,
|
|
2162
|
+
mutation,
|
|
2163
|
+
deploy
|
|
2164
|
+
}) {
|
|
2165
|
+
const spawned = mutation.pendingCreates.length > 0, committed = await persist(ctx, mutation);
|
|
2166
|
+
await deployOrRollback({
|
|
2167
|
+
client: ctx.client,
|
|
2168
|
+
instanceId: ctx.instance._id,
|
|
2169
|
+
committedRev: committed._rev,
|
|
2170
|
+
restore: instanceStateFields(ctx.instance),
|
|
2171
|
+
unset: ctx.instance.completedAt === void 0 ? ["completedAt"] : [],
|
|
2172
|
+
reversible: !spawned,
|
|
2173
|
+
deploy
|
|
2174
|
+
});
|
|
2175
|
+
}
|
|
2176
|
+
async function persistThenMaybeRefresh({
|
|
2177
|
+
ctx,
|
|
2178
|
+
mutation,
|
|
2179
|
+
stageName,
|
|
2180
|
+
didChangeState
|
|
2181
|
+
}) {
|
|
2182
|
+
await persistThenDeploy({
|
|
2183
|
+
ctx,
|
|
2184
|
+
mutation,
|
|
2185
|
+
deploy: () => didChangeState ? refreshStageGuards({ ctx, mutation, stageName }) : Promise.resolve()
|
|
2186
|
+
});
|
|
2187
|
+
}
|
|
2188
|
+
async function refreshStageGuards({
|
|
2189
|
+
ctx,
|
|
2190
|
+
mutation,
|
|
2191
|
+
stageName
|
|
2192
|
+
}) {
|
|
2193
|
+
await deployStageGuards({
|
|
2194
|
+
client: ctx.client,
|
|
2195
|
+
clientForGdr: ctx.clientForGdr,
|
|
2196
|
+
instance: materializeInstance(ctx.instance, mutation),
|
|
2197
|
+
definition: ctx.definition,
|
|
2198
|
+
stageName,
|
|
2199
|
+
now: ctx.now
|
|
2200
|
+
});
|
|
2201
|
+
}
|
|
2202
|
+
async function completeEffect(args) {
|
|
2203
|
+
const { client, instanceId, effectKey, status, outputs, ops, detail, error, durationMs, options } = args, ctx = await loadCallContext({ client, instanceId, options });
|
|
2204
|
+
return commitCompleteEffect({
|
|
2205
|
+
ctx,
|
|
2206
|
+
effectKey,
|
|
2207
|
+
status,
|
|
2208
|
+
outputs,
|
|
2209
|
+
ops,
|
|
2210
|
+
detail,
|
|
2211
|
+
error,
|
|
2212
|
+
durationMs,
|
|
2213
|
+
actor: options?.actor
|
|
2214
|
+
});
|
|
2215
|
+
}
|
|
2216
|
+
function buildEffectHistoryEntry(pending, outcome) {
|
|
2217
|
+
const { status, ranAt, actor, detail, error, durationMs, outputs } = outcome, resolvedActor = actor ?? pending.actor;
|
|
2218
|
+
return {
|
|
2219
|
+
_key: pending._key,
|
|
2220
|
+
name: pending.name,
|
|
2221
|
+
...pending.title !== void 0 ? { title: pending.title } : {},
|
|
2222
|
+
...pending.description !== void 0 ? { description: pending.description } : {},
|
|
2223
|
+
params: pending.params,
|
|
2224
|
+
origin: pending.origin,
|
|
2225
|
+
...resolvedActor !== void 0 ? { actor: resolvedActor } : {},
|
|
2226
|
+
ranAt,
|
|
2227
|
+
...durationMs !== void 0 ? { durationMs } : {},
|
|
2228
|
+
status,
|
|
2229
|
+
...detail !== void 0 ? { detail } : {},
|
|
2230
|
+
...error !== void 0 ? { error } : {},
|
|
2231
|
+
...outputs !== void 0 ? { outputs } : {}
|
|
2232
|
+
};
|
|
2038
2233
|
}
|
|
2039
|
-
function
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2234
|
+
function upsertEffectsContext({
|
|
2235
|
+
mutation,
|
|
2236
|
+
effectName,
|
|
2237
|
+
outputs
|
|
2238
|
+
}) {
|
|
2239
|
+
const existingIndex = mutation.effectsContext.findIndex((e) => e.name === effectName), entry = effectsContextJsonEntry(effectName, outputs);
|
|
2240
|
+
existingIndex >= 0 ? mutation.effectsContext[existingIndex] = entry : mutation.effectsContext.push(entry);
|
|
2045
2241
|
}
|
|
2046
|
-
function
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2242
|
+
async function commitCompleteEffect({
|
|
2243
|
+
ctx,
|
|
2244
|
+
effectKey,
|
|
2245
|
+
status,
|
|
2246
|
+
outputs,
|
|
2247
|
+
ops,
|
|
2248
|
+
detail,
|
|
2249
|
+
error,
|
|
2250
|
+
durationMs,
|
|
2251
|
+
actor
|
|
2252
|
+
}) {
|
|
2253
|
+
const pending = ctx.instance.pendingEffects.find((e) => e._key === effectKey);
|
|
2254
|
+
if (pending === void 0)
|
|
2255
|
+
throw new Error(`Pending effect "${effectKey}" not found on instance ${ctx.instance._id}`);
|
|
2256
|
+
if (status === "failed" && ops !== void 0 && ops.length > 0)
|
|
2257
|
+
throw new EffectOpsInvalidError({
|
|
2258
|
+
effect: pending.name,
|
|
2259
|
+
issues: [
|
|
2260
|
+
"ops cannot accompany a failed completion \u2014 field.set the outcome on a done completion instead"
|
|
2261
|
+
]
|
|
2262
|
+
});
|
|
2263
|
+
const validatedOps = ops !== void 0 ? validateEffectOps(ops, pending.name) : [], mutation = startMutation(ctx.instance);
|
|
2264
|
+
mutation.pendingEffects = mutation.pendingEffects.filter((e) => e._key !== effectKey);
|
|
2265
|
+
const ranAt = ctx.now;
|
|
2266
|
+
mutation.effectHistory.push(
|
|
2267
|
+
buildEffectHistoryEntry(pending, { status, ranAt, actor, detail, error, durationMs, outputs })
|
|
2268
|
+
);
|
|
2269
|
+
const wroteEffectsContext = status === "done" && outputs !== void 0;
|
|
2270
|
+
wroteEffectsContext && upsertEffectsContext({ mutation, effectName: pending.name, outputs }), mutation.history.push({
|
|
2271
|
+
_key: randomKey(),
|
|
2272
|
+
_type: "effectCompleted",
|
|
2273
|
+
at: ranAt,
|
|
2274
|
+
effectKey,
|
|
2275
|
+
effect: pending.name,
|
|
2276
|
+
status,
|
|
2277
|
+
...outputs !== void 0 ? { outputs } : {},
|
|
2278
|
+
...detail !== void 0 ? { detail } : {},
|
|
2279
|
+
...actor !== void 0 ? { actor } : {}
|
|
2280
|
+
});
|
|
2281
|
+
const ranOps = runOps({
|
|
2282
|
+
ops: validatedOps,
|
|
2283
|
+
mutation,
|
|
2284
|
+
stage: ctx.instance.currentStage,
|
|
2285
|
+
origin: { effect: pending.name },
|
|
2286
|
+
params: pending.params,
|
|
2287
|
+
actor,
|
|
2288
|
+
self: selfGdr(ctx.instance),
|
|
2289
|
+
now: ranAt
|
|
2290
|
+
}), needsGuardRefresh = wroteEffectsContext || ranOps.some(isFieldOp);
|
|
2291
|
+
return await persistThenMaybeRefresh({
|
|
2292
|
+
ctx,
|
|
2293
|
+
mutation,
|
|
2294
|
+
stageName: ctx.instance.currentStage,
|
|
2295
|
+
didChangeState: needsGuardRefresh
|
|
2296
|
+
}), { effectKey, status };
|
|
2297
|
+
}
|
|
2298
|
+
function buildQueuedEffect({
|
|
2299
|
+
effect,
|
|
2300
|
+
origin,
|
|
2301
|
+
params,
|
|
2302
|
+
actor,
|
|
2303
|
+
now
|
|
2304
|
+
}) {
|
|
2305
|
+
const key = randomKey(), pending = {
|
|
2306
|
+
_key: key,
|
|
2307
|
+
_type: "pendingEffect",
|
|
2308
|
+
name: effect.name,
|
|
2309
|
+
...effect.title !== void 0 ? { title: effect.title } : {},
|
|
2310
|
+
...effect.description !== void 0 ? { description: effect.description } : {},
|
|
2311
|
+
...effect.bindings !== void 0 ? { bindings: effect.bindings } : {},
|
|
2312
|
+
params,
|
|
2313
|
+
origin,
|
|
2314
|
+
...actor !== void 0 ? { actor } : {},
|
|
2315
|
+
queuedAt: now
|
|
2316
|
+
}, history = {
|
|
2317
|
+
_key: randomKey(),
|
|
2318
|
+
_type: "effectQueued",
|
|
2319
|
+
at: now,
|
|
2320
|
+
effectKey: key,
|
|
2321
|
+
effect: effect.name,
|
|
2322
|
+
origin
|
|
2323
|
+
};
|
|
2324
|
+
return { pending, history };
|
|
2325
|
+
}
|
|
2326
|
+
async function queueEffects({
|
|
2327
|
+
ctx,
|
|
2328
|
+
mutation,
|
|
2329
|
+
effects,
|
|
2330
|
+
origin,
|
|
2331
|
+
actor,
|
|
2332
|
+
opts
|
|
2333
|
+
}) {
|
|
2334
|
+
if (!effects || effects.length === 0) return;
|
|
2335
|
+
const now = ctx.now, liveCtx = { ...ctx, instance: materializeInstance(ctx.instance, mutation) }, params = await ctxConditionParams(liveCtx, {
|
|
2336
|
+
...opts?.activityName !== void 0 ? { activityName: opts.activityName } : {},
|
|
2337
|
+
...actor !== void 0 ? { actor } : {},
|
|
2338
|
+
// Caller-supplied action params, readable in bindings as `$params.<name>`.
|
|
2339
|
+
vars: { params: opts?.callerParams ?? {} }
|
|
2340
|
+
});
|
|
2341
|
+
for (const effect of effects) {
|
|
2342
|
+
const resolved = await resolveBindings({
|
|
2343
|
+
bindings: effect.bindings,
|
|
2344
|
+
staticInput: effect.input,
|
|
2345
|
+
snapshot: ctx.snapshot,
|
|
2346
|
+
params
|
|
2347
|
+
}), { pending, history } = buildQueuedEffect({ effect, origin, params: resolved, actor, now });
|
|
2348
|
+
mutation.pendingEffects.push(pending), mutation.history.push(history);
|
|
2054
2349
|
}
|
|
2055
2350
|
}
|
|
2056
2351
|
const TAG_RE = /^[a-z0-9][a-z0-9-]*$/;
|
|
@@ -2071,13 +2366,14 @@ async function discoverItems(args) {
|
|
|
2071
2366
|
const { client, groq, params, perspective } = args, fetchOptions = perspective !== void 0 ? { perspective } : void 0;
|
|
2072
2367
|
return client.fetch(groq, paramsForLake(params), fetchOptions);
|
|
2073
2368
|
}
|
|
2074
|
-
function clientForDiscovery(
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2369
|
+
function clientForDiscovery({
|
|
2370
|
+
defaultClient,
|
|
2371
|
+
clientForGdr,
|
|
2372
|
+
subjectGdrUri
|
|
2373
|
+
}) {
|
|
2374
|
+
if (subjectGdrUri === void 0) return defaultClient;
|
|
2375
|
+
const parsed = tryParseGdr(subjectGdrUri);
|
|
2376
|
+
return parsed !== void 0 ? clientForGdr(parsed) : defaultClient;
|
|
2081
2377
|
}
|
|
2082
2378
|
const ENGINE_ACTOR_ID_PREFIX = "engine.";
|
|
2083
2379
|
function engineSystemActor(name) {
|
|
@@ -2090,25 +2386,49 @@ const MAX_SPAWN_DEPTH = 6, SUBWORKFLOWS_ALL_DONE = "count($subworkflows[status !
|
|
|
2090
2386
|
function gateActor(outcome) {
|
|
2091
2387
|
return engineSystemActor(outcome === "failed" ? "failWhen" : "completeWhen");
|
|
2092
2388
|
}
|
|
2389
|
+
function applyGateOutcome(args) {
|
|
2390
|
+
applyActivityStatusChange({
|
|
2391
|
+
entry: args.entry,
|
|
2392
|
+
history: args.history,
|
|
2393
|
+
stage: args.stage,
|
|
2394
|
+
to: args.outcome,
|
|
2395
|
+
at: args.at,
|
|
2396
|
+
actor: gateActor(args.outcome)
|
|
2397
|
+
});
|
|
2398
|
+
}
|
|
2093
2399
|
function effectiveCompleteWhen(activity) {
|
|
2094
2400
|
return activity.completeWhen ?? (activity.subworkflows !== void 0 ? SUBWORKFLOWS_ALL_DONE : void 0);
|
|
2095
2401
|
}
|
|
2096
|
-
async function evaluateResolutionGate(
|
|
2402
|
+
async function evaluateResolutionGate({
|
|
2403
|
+
ctx,
|
|
2404
|
+
activity,
|
|
2405
|
+
vars
|
|
2406
|
+
}) {
|
|
2097
2407
|
const scope = { activityName: activity.name, vars };
|
|
2098
|
-
if (activity.failWhen !== void 0 && await ctxEvaluateCondition(ctx, activity.failWhen, scope))
|
|
2408
|
+
if (activity.failWhen !== void 0 && await ctxEvaluateCondition({ ctx, condition: activity.failWhen, opts: scope }))
|
|
2099
2409
|
return "failed";
|
|
2100
2410
|
const completeWhen = effectiveCompleteWhen(activity);
|
|
2101
|
-
if (completeWhen !== void 0 && await ctxEvaluateCondition(ctx, completeWhen, scope))
|
|
2411
|
+
if (completeWhen !== void 0 && await ctxEvaluateCondition({ ctx, condition: completeWhen, opts: scope }))
|
|
2102
2412
|
return "done";
|
|
2103
2413
|
}
|
|
2104
|
-
async function spawnSubworkflows(
|
|
2414
|
+
async function spawnSubworkflows({
|
|
2415
|
+
ctx,
|
|
2416
|
+
mutation,
|
|
2417
|
+
activity,
|
|
2418
|
+
sub,
|
|
2419
|
+
actor
|
|
2420
|
+
}) {
|
|
2105
2421
|
if (ctx.instance.ancestors.length + 1 > MAX_SPAWN_DEPTH) {
|
|
2106
2422
|
const chain = [...ctx.instance.ancestors.map((a) => a.id), selfGdr(ctx.instance)].join(" \u2192 ");
|
|
2107
2423
|
throw new Error(
|
|
2108
2424
|
`Subworkflow depth limit (${MAX_SPAWN_DEPTH}) exceeded on activity "${activity.name}" of ${ctx.instance._id}. Ancestor chain: ${chain}`
|
|
2109
2425
|
);
|
|
2110
2426
|
}
|
|
2111
|
-
const now = ctx.now, definition = await resolveDefinitionRef(
|
|
2427
|
+
const now = ctx.now, definition = await resolveDefinitionRef({
|
|
2428
|
+
client: ctx.client,
|
|
2429
|
+
ref: sub.definition,
|
|
2430
|
+
tag: ctx.instance.tag
|
|
2431
|
+
});
|
|
2112
2432
|
if (definition === null) {
|
|
2113
2433
|
const versionLabel = sub.definition.version === void 0 || sub.definition.version === "latest" ? "latest" : `v${sub.definition.version}`;
|
|
2114
2434
|
throw new Error(
|
|
@@ -2118,16 +2438,16 @@ async function spawnSubworkflows(ctx, mutation, activity, sub, actor) {
|
|
|
2118
2438
|
const parentScope = await ctxConditionParams(ctx, {
|
|
2119
2439
|
activityName: activity.name,
|
|
2120
2440
|
...actor ? { actor } : {}
|
|
2121
|
-
}), { rows, discoveryResource } = await resolveSpawnRows(ctx, sub), effectsContext = await resolveContextHandoff(ctx, sub, parentScope), refs = [];
|
|
2441
|
+
}), { rows, discoveryResource } = await resolveSpawnRows(ctx, sub), effectsContext = await resolveContextHandoff({ ctx, sub, parentScope }), refs = [];
|
|
2122
2442
|
for (const row of rows) {
|
|
2123
|
-
const initialFields = await projectRowFields(
|
|
2443
|
+
const initialFields = await projectRowFields({
|
|
2124
2444
|
ctx,
|
|
2125
2445
|
sub,
|
|
2126
|
-
definition,
|
|
2446
|
+
childDefinition: definition,
|
|
2127
2447
|
parentScope,
|
|
2128
2448
|
row,
|
|
2129
2449
|
discoveryResource
|
|
2130
|
-
), { ref, body } = await prepareChildInstance({
|
|
2450
|
+
}), { ref, body } = await prepareChildInstance({
|
|
2131
2451
|
client: ctx.client,
|
|
2132
2452
|
parent: ctx.instance,
|
|
2133
2453
|
definition,
|
|
@@ -2144,7 +2464,11 @@ async function resolveSpawnRows(ctx, sub) {
|
|
|
2144
2464
|
const params = buildParams({ instance: ctx.instance, now: ctx.now, snapshot: ctx.snapshot }), groq = sub.forEach;
|
|
2145
2465
|
let value, discoveryResource;
|
|
2146
2466
|
if (groq.includes("*")) {
|
|
2147
|
-
const routingUri = entrySubjectRefs(ctx.instance.fields)[0]?.id, client = clientForDiscovery(
|
|
2467
|
+
const routingUri = entrySubjectRefs(ctx.instance.fields)[0]?.id, client = clientForDiscovery({
|
|
2468
|
+
defaultClient: ctx.client,
|
|
2469
|
+
clientForGdr: ctx.clientForGdr,
|
|
2470
|
+
subjectGdrUri: routingUri
|
|
2471
|
+
});
|
|
2148
2472
|
client !== ctx.client && routingUri !== void 0 && (discoveryResource = resourceFromGdrUri(routingUri)), value = await discoverItems({
|
|
2149
2473
|
client,
|
|
2150
2474
|
groq,
|
|
@@ -2160,7 +2484,14 @@ async function resolveSpawnRows(ctx, sub) {
|
|
|
2160
2484
|
}
|
|
2161
2485
|
return Array.isArray(value) ? { rows: value, discoveryResource } : value == null ? { rows: [], discoveryResource } : { rows: [value], discoveryResource };
|
|
2162
2486
|
}
|
|
2163
|
-
async function projectRowFields(
|
|
2487
|
+
async function projectRowFields({
|
|
2488
|
+
ctx,
|
|
2489
|
+
sub,
|
|
2490
|
+
childDefinition,
|
|
2491
|
+
parentScope,
|
|
2492
|
+
row,
|
|
2493
|
+
discoveryResource
|
|
2494
|
+
}) {
|
|
2164
2495
|
const out = [];
|
|
2165
2496
|
for (const [name, groq] of Object.entries(sub.with ?? {})) {
|
|
2166
2497
|
const declared = (childDefinition.fields ?? []).find((e) => e.name === name);
|
|
@@ -2168,11 +2499,15 @@ async function projectRowFields(ctx, sub, childDefinition, parentScope, row, dis
|
|
|
2168
2499
|
throw new Error(
|
|
2169
2500
|
`subworkflows.with["${name}"]: subworkflow "${childDefinition.name}" declares no field entry "${name}"`
|
|
2170
2501
|
);
|
|
2171
|
-
const raw = await runGroq(groq, { ...parentScope, row }, ctx.snapshot), value = canonicaliseSpawnValue(
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2502
|
+
const raw = await runGroq({ groq, params: { ...parentScope, row }, snapshot: ctx.snapshot }), value = canonicaliseSpawnValue({
|
|
2503
|
+
kind: declared.type,
|
|
2504
|
+
value: raw,
|
|
2505
|
+
cx: {
|
|
2506
|
+
parentResource: ctx.instance.workflowResource,
|
|
2507
|
+
discoveryResource,
|
|
2508
|
+
entryName: name,
|
|
2509
|
+
childName: childDefinition.name
|
|
2510
|
+
}
|
|
2176
2511
|
});
|
|
2177
2512
|
value != null && out.push({
|
|
2178
2513
|
type: declared.type,
|
|
@@ -2182,15 +2517,23 @@ async function projectRowFields(ctx, sub, childDefinition, parentScope, row, dis
|
|
|
2182
2517
|
}
|
|
2183
2518
|
return out;
|
|
2184
2519
|
}
|
|
2185
|
-
async function resolveContextHandoff(
|
|
2520
|
+
async function resolveContextHandoff({
|
|
2521
|
+
ctx,
|
|
2522
|
+
sub,
|
|
2523
|
+
parentScope
|
|
2524
|
+
}) {
|
|
2186
2525
|
const out = {};
|
|
2187
2526
|
for (const [name, groq] of Object.entries(sub.context ?? {})) {
|
|
2188
|
-
const v2 = await runGroq(groq, parentScope, ctx.snapshot);
|
|
2527
|
+
const v2 = await runGroq({ groq, params: parentScope, snapshot: ctx.snapshot });
|
|
2189
2528
|
v2 != null && (typeof v2 == "string" || typeof v2 == "number" || typeof v2 == "boolean" || typeof v2 == "object" && "id" in v2 && "type" in v2) && (out[name] = v2);
|
|
2190
2529
|
}
|
|
2191
2530
|
return out;
|
|
2192
2531
|
}
|
|
2193
|
-
function canonicaliseSpawnValue(
|
|
2532
|
+
function canonicaliseSpawnValue({
|
|
2533
|
+
kind,
|
|
2534
|
+
value,
|
|
2535
|
+
cx
|
|
2536
|
+
}) {
|
|
2194
2537
|
return kind === "doc.ref" ? coerceSpawnGdr(value, cx) : kind === "doc.refs" && Array.isArray(value) ? value.map((v2) => coerceSpawnGdr(v2, cx)).filter((v2) => v2 !== null) : value;
|
|
2195
2538
|
}
|
|
2196
2539
|
function assertBareIdRootsCorrectly(id, cx) {
|
|
@@ -2213,7 +2556,11 @@ function coerceSpawnGdr(raw, cx) {
|
|
|
2213
2556
|
}
|
|
2214
2557
|
return typeof raw == "string" && raw.length > 0 ? { id: toUri(raw), type: "document" } : null;
|
|
2215
2558
|
}
|
|
2216
|
-
async function resolveDefinitionRef(
|
|
2559
|
+
async function resolveDefinitionRef({
|
|
2560
|
+
client,
|
|
2561
|
+
ref,
|
|
2562
|
+
tag
|
|
2563
|
+
}) {
|
|
2217
2564
|
const wantsExplicit = typeof ref.version == "number", params = { definition: ref.name, tag };
|
|
2218
2565
|
return wantsExplicit && (params.version = ref.version), await client.fetch(
|
|
2219
2566
|
definitionLookupGroq(wantsExplicit),
|
|
@@ -2221,7 +2568,7 @@ async function resolveDefinitionRef(client, ref, tag) {
|
|
|
2221
2568
|
) ?? null;
|
|
2222
2569
|
}
|
|
2223
2570
|
async function prepareChildInstance(args) {
|
|
2224
|
-
const { client, parent, definition, initialFields, effectsContext, actor, now } = args, childTag = parent.tag, workflowResource = parent.workflowResource, childDocId =
|
|
2571
|
+
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 = [
|
|
2225
2572
|
...parent.ancestors,
|
|
2226
2573
|
{
|
|
2227
2574
|
id: selfGdr(parent),
|
|
@@ -2238,7 +2585,7 @@ async function prepareChildInstance(args) {
|
|
|
2238
2585
|
workflowResource,
|
|
2239
2586
|
definitionName: definition.name,
|
|
2240
2587
|
...inheritedPerspective !== void 0 ? { perspective: inheritedPerspective } : {},
|
|
2241
|
-
recordDiscard: recordFieldDiscards(fieldDiscards, "workflow", now)
|
|
2588
|
+
recordDiscard: recordFieldDiscards({ target: fieldDiscards, scope: "workflow", at: now })
|
|
2242
2589
|
},
|
|
2243
2590
|
randomKey
|
|
2244
2591
|
}), childPerspective = derivePerspectiveFromFields(childFields) ?? inheritedPerspective, effectsContextEntries = Object.entries(effectsContext).map(
|
|
@@ -2273,7 +2620,7 @@ async function prepareChildInstance(args) {
|
|
|
2273
2620
|
}
|
|
2274
2621
|
async function subworkflowRows(ctx, refs) {
|
|
2275
2622
|
if (refs.length === 0) return [];
|
|
2276
|
-
const ids = refs.map((r) =>
|
|
2623
|
+
const ids = refs.map((r) => toBareId(r.id));
|
|
2277
2624
|
return (await ctx.client.fetch("*[_id in $ids]{_id, currentStage, completedAt}", { ids })).map((c) => ({
|
|
2278
2625
|
_id: c._id,
|
|
2279
2626
|
stage: c.currentStage,
|
|
@@ -2281,13 +2628,14 @@ async function subworkflowRows(ctx, refs) {
|
|
|
2281
2628
|
}));
|
|
2282
2629
|
}
|
|
2283
2630
|
async function invokeActivity(args) {
|
|
2284
|
-
const { client, instanceId, activity: activityName, options } = args, ctx = await
|
|
2285
|
-
|
|
2286
|
-
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2631
|
+
const { client, instanceId, activity: activityName, options } = args, ctx = await loadCallContext({ client, instanceId, options });
|
|
2632
|
+
return commitInvoke({ ctx, activityName, actor: options?.actor });
|
|
2633
|
+
}
|
|
2634
|
+
async function commitInvoke({
|
|
2635
|
+
ctx,
|
|
2636
|
+
activityName,
|
|
2637
|
+
actor
|
|
2638
|
+
}) {
|
|
2291
2639
|
const { activity } = findActivityInCurrentStage(ctx, activityName), entry = findCurrentActivities(ctx.instance).find((t) => t.name === activityName);
|
|
2292
2640
|
if (entry === void 0)
|
|
2293
2641
|
throw new Error(
|
|
@@ -2296,9 +2644,15 @@ async function commitInvoke(ctx, activityName, actor) {
|
|
|
2296
2644
|
if (entry.status !== "pending")
|
|
2297
2645
|
return { invoked: !1, activity: activityName };
|
|
2298
2646
|
const mutation = startMutation(ctx.instance), mutEntry = requireMutationActivityEntry(mutation, activityName);
|
|
2299
|
-
return await activateActivity(ctx, mutation, activity, mutEntry, actor), await persist(ctx, mutation), { invoked: !0, activity: activityName };
|
|
2300
|
-
}
|
|
2301
|
-
async function activateActivity(
|
|
2647
|
+
return await activateActivity({ ctx, mutation, activity, entry: mutEntry, actor }), await persist(ctx, mutation), { invoked: !0, activity: activityName };
|
|
2648
|
+
}
|
|
2649
|
+
async function activateActivity({
|
|
2650
|
+
ctx,
|
|
2651
|
+
mutation,
|
|
2652
|
+
activity,
|
|
2653
|
+
entry,
|
|
2654
|
+
actor
|
|
2655
|
+
}) {
|
|
2302
2656
|
const now = ctx.now;
|
|
2303
2657
|
if (entry.status = "active", entry.startedAt = now, activity.fields !== void 0 && activity.fields.length > 0) {
|
|
2304
2658
|
const stage = findStage(ctx.definition, mutation.currentStage);
|
|
@@ -2308,7 +2662,7 @@ async function activateActivity(ctx, mutation, activity, entry, actor) {
|
|
|
2308
2662
|
stage,
|
|
2309
2663
|
activity,
|
|
2310
2664
|
now,
|
|
2311
|
-
recordDiscard: recordFieldDiscards(mutation.history, "activity", now)
|
|
2665
|
+
recordDiscard: recordFieldDiscards({ target: mutation.history, scope: "activity", at: now })
|
|
2312
2666
|
});
|
|
2313
2667
|
}
|
|
2314
2668
|
runOps({
|
|
@@ -2327,19 +2681,25 @@ async function activateActivity(ctx, mutation, activity, entry, actor) {
|
|
|
2327
2681
|
stage: mutation.currentStage,
|
|
2328
2682
|
activity: activity.name,
|
|
2329
2683
|
...actor !== void 0 ? { actor } : {}
|
|
2330
|
-
}), await queueEffects(
|
|
2684
|
+
}), await queueEffects({
|
|
2331
2685
|
ctx,
|
|
2332
2686
|
mutation,
|
|
2333
|
-
activity.effects,
|
|
2334
|
-
{ kind: "activity", name: activity.name },
|
|
2687
|
+
effects: activity.effects,
|
|
2688
|
+
origin: { kind: "activity", name: activity.name },
|
|
2335
2689
|
actor,
|
|
2336
|
-
{
|
|
2690
|
+
opts: {
|
|
2337
2691
|
activityName: activity.name
|
|
2338
2692
|
}
|
|
2339
|
-
);
|
|
2693
|
+
});
|
|
2340
2694
|
let pendingChildren;
|
|
2341
2695
|
if (activity.subworkflows !== void 0) {
|
|
2342
|
-
const createsBefore = mutation.pendingCreates.length, refs = await spawnSubworkflows(
|
|
2696
|
+
const createsBefore = mutation.pendingCreates.length, refs = await spawnSubworkflows({
|
|
2697
|
+
ctx,
|
|
2698
|
+
mutation,
|
|
2699
|
+
activity,
|
|
2700
|
+
sub: activity.subworkflows,
|
|
2701
|
+
actor
|
|
2702
|
+
});
|
|
2343
2703
|
entry.spawnedInstances = refs, pendingChildren = mutation.pendingCreates.slice(createsBefore).map((c) => ({ _id: c._id, stage: c.currentStage, status: "active" }));
|
|
2344
2704
|
for (const ref of refs)
|
|
2345
2705
|
mutation.history.push({
|
|
@@ -2350,24 +2710,42 @@ async function activateActivity(ctx, mutation, activity, entry, actor) {
|
|
|
2350
2710
|
instanceRef: ref
|
|
2351
2711
|
});
|
|
2352
2712
|
}
|
|
2353
|
-
await autoResolveOnActivate(
|
|
2354
|
-
|
|
2355
|
-
|
|
2713
|
+
await autoResolveOnActivate({
|
|
2714
|
+
ctx,
|
|
2715
|
+
mutation,
|
|
2716
|
+
activity,
|
|
2717
|
+
entry,
|
|
2718
|
+
now,
|
|
2719
|
+
...pendingChildren !== void 0 ? { pendingChildren } : {}
|
|
2720
|
+
}) || resolveMachineStep({ mutation, activity, entry, now });
|
|
2721
|
+
}
|
|
2722
|
+
async function autoResolveOnActivate({
|
|
2723
|
+
ctx,
|
|
2724
|
+
mutation,
|
|
2725
|
+
activity,
|
|
2726
|
+
entry,
|
|
2727
|
+
now,
|
|
2728
|
+
pendingChildren
|
|
2729
|
+
}) {
|
|
2356
2730
|
if (activity.failWhen === void 0 && effectiveCompleteWhen(activity) === void 0) return !1;
|
|
2357
|
-
const vars = pendingChildren !== void 0 ? { subworkflows: pendingChildren } : await activityConditionVars(ctx, entry), outcome = await evaluateResolutionGate(ctx, activity, vars);
|
|
2358
|
-
return outcome === void 0 ? !1 : (
|
|
2731
|
+
const vars = pendingChildren !== void 0 ? { subworkflows: pendingChildren } : await activityConditionVars(ctx, entry), outcome = await evaluateResolutionGate({ ctx, activity, vars });
|
|
2732
|
+
return outcome === void 0 ? !1 : (applyGateOutcome({
|
|
2359
2733
|
entry,
|
|
2360
2734
|
history: mutation.history,
|
|
2361
2735
|
stage: mutation.currentStage,
|
|
2362
|
-
to: outcome,
|
|
2363
2736
|
at: now,
|
|
2364
|
-
|
|
2737
|
+
outcome
|
|
2365
2738
|
}), !0);
|
|
2366
2739
|
}
|
|
2367
2740
|
async function activityConditionVars(ctx, entry) {
|
|
2368
2741
|
return entry.spawnedInstances === void 0 || entry.spawnedInstances.length === 0 ? { subworkflows: [] } : { subworkflows: await subworkflowRows(ctx, entry.spawnedInstances) };
|
|
2369
2742
|
}
|
|
2370
|
-
function resolveMachineStep(
|
|
2743
|
+
function resolveMachineStep({
|
|
2744
|
+
mutation,
|
|
2745
|
+
activity,
|
|
2746
|
+
entry,
|
|
2747
|
+
now
|
|
2748
|
+
}) {
|
|
2371
2749
|
const waitsForActor = activity.actions !== void 0 && activity.actions.length > 0, waitsForCondition = activity.completeWhen !== void 0 || activity.subworkflows !== void 0;
|
|
2372
2750
|
waitsForActor || waitsForCondition || applyActivityStatusChange({
|
|
2373
2751
|
entry,
|
|
@@ -2381,19 +2759,25 @@ function resolveMachineStep(mutation, activity, entry, now) {
|
|
|
2381
2759
|
async function buildStageActivities(ctx, stage) {
|
|
2382
2760
|
const entries = [];
|
|
2383
2761
|
for (const activity of stage.activities ?? []) {
|
|
2384
|
-
const outcome = await ctxEvaluateConditionOutcome(
|
|
2385
|
-
|
|
2762
|
+
const outcome = await ctxEvaluateConditionOutcome({
|
|
2763
|
+
ctx,
|
|
2764
|
+
condition: activity.filter,
|
|
2765
|
+
opts: { activityName: activity.name }
|
|
2386
2766
|
}), inScope = outcome !== "unsatisfied";
|
|
2387
2767
|
entries.push({
|
|
2388
2768
|
_key: randomKey(),
|
|
2389
2769
|
name: activity.name,
|
|
2390
2770
|
status: inScope ? "pending" : "skipped",
|
|
2391
|
-
...activity.filter !== void 0 ? { filterEvaluation: buildFilterEvaluation(ctx.now, activity.filter, outcome) } : {}
|
|
2771
|
+
...activity.filter !== void 0 ? { filterEvaluation: buildFilterEvaluation({ at: ctx.now, filter: activity.filter, outcome }) } : {}
|
|
2392
2772
|
});
|
|
2393
2773
|
}
|
|
2394
2774
|
return entries;
|
|
2395
2775
|
}
|
|
2396
|
-
function buildFilterEvaluation(
|
|
2776
|
+
function buildFilterEvaluation({
|
|
2777
|
+
at,
|
|
2778
|
+
filter,
|
|
2779
|
+
outcome
|
|
2780
|
+
}) {
|
|
2397
2781
|
return outcome === "unevaluable" ? {
|
|
2398
2782
|
at,
|
|
2399
2783
|
truthy: !1,
|
|
@@ -2405,13 +2789,16 @@ function isTerminalStage(stage) {
|
|
|
2405
2789
|
return (stage.transitions ?? []).length === 0;
|
|
2406
2790
|
}
|
|
2407
2791
|
async function setStage(args) {
|
|
2408
|
-
const { client, instanceId, targetStage, reason, options } = args, ctx = await
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2792
|
+
const { client, instanceId, targetStage, reason, options } = args, ctx = await loadCallContext({ client, instanceId, options });
|
|
2793
|
+
return commitSetStage({ ctx, targetStage, reason, actor: options?.actor });
|
|
2794
|
+
}
|
|
2795
|
+
async function enterStage({
|
|
2796
|
+
ctx,
|
|
2797
|
+
mutation,
|
|
2798
|
+
nextStage,
|
|
2799
|
+
actor,
|
|
2800
|
+
at
|
|
2801
|
+
}) {
|
|
2415
2802
|
mutation.currentStage = nextStage.name;
|
|
2416
2803
|
const nextStageEntry = {
|
|
2417
2804
|
_key: randomKey(),
|
|
@@ -2422,7 +2809,7 @@ async function enterStage(ctx, mutation, nextStage, actor, at) {
|
|
|
2422
2809
|
instance: ctx.instance,
|
|
2423
2810
|
stage: nextStage,
|
|
2424
2811
|
now: at,
|
|
2425
|
-
recordDiscard: recordFieldDiscards(mutation.history, "stage", at)
|
|
2812
|
+
recordDiscard: recordFieldDiscards({ target: mutation.history, scope: "stage", at })
|
|
2426
2813
|
}),
|
|
2427
2814
|
activities: await buildStageActivities(ctx, nextStage)
|
|
2428
2815
|
};
|
|
@@ -2430,14 +2817,19 @@ async function enterStage(ctx, mutation, nextStage, actor, at) {
|
|
|
2430
2817
|
for (const activity of nextStage.activities ?? []) {
|
|
2431
2818
|
if (activity.activation !== "auto") continue;
|
|
2432
2819
|
const entry = nextStageEntry.activities.find((t) => t.name === activity.name);
|
|
2433
|
-
entry && entry.status === "pending" && await activateActivity(ctx, mutation, activity, entry, actor);
|
|
2820
|
+
entry && entry.status === "pending" && await activateActivity({ ctx, mutation, activity, entry, actor });
|
|
2434
2821
|
}
|
|
2435
2822
|
isTerminalStage(nextStage) && (mutation.completedAt = at);
|
|
2436
2823
|
}
|
|
2437
2824
|
function isTerminal(ctx) {
|
|
2438
2825
|
return ctx.instance.completedAt !== void 0;
|
|
2439
2826
|
}
|
|
2440
|
-
async function commitSetStage(
|
|
2827
|
+
async function commitSetStage({
|
|
2828
|
+
ctx,
|
|
2829
|
+
targetStage,
|
|
2830
|
+
reason,
|
|
2831
|
+
actor
|
|
2832
|
+
}) {
|
|
2441
2833
|
if (isTerminal(ctx))
|
|
2442
2834
|
return { fired: !1 };
|
|
2443
2835
|
const currentStage = findStage(ctx.definition, ctx.instance.currentStage), nextStage = findStage(ctx.definition, targetStage);
|
|
@@ -2456,18 +2848,28 @@ async function commitSetStage(ctx, targetStage, reason, actor) {
|
|
|
2456
2848
|
})
|
|
2457
2849
|
);
|
|
2458
2850
|
const priorEntry = findOpenStageEntry(mutation);
|
|
2459
|
-
return priorEntry !== void 0 && (priorEntry.exitedAt = at), await enterStage(ctx, mutation, nextStage, actor, at), await persistStageMove(
|
|
2851
|
+
return priorEntry !== void 0 && (priorEntry.exitedAt = at), await enterStage({ ctx, mutation, nextStage, actor, at }), await persistStageMove({
|
|
2852
|
+
ctx,
|
|
2853
|
+
mutation,
|
|
2854
|
+
exitedStage: currentStage.name,
|
|
2855
|
+
enteredStage: nextStage.name
|
|
2856
|
+
}), {
|
|
2460
2857
|
fired: !0,
|
|
2461
2858
|
fromStage: currentStage.name,
|
|
2462
2859
|
toStage: nextStage.name,
|
|
2463
2860
|
transition: transitionName
|
|
2464
2861
|
};
|
|
2465
2862
|
}
|
|
2466
|
-
async function persistStageMove(
|
|
2467
|
-
|
|
2863
|
+
async function persistStageMove({
|
|
2864
|
+
ctx,
|
|
2865
|
+
mutation,
|
|
2866
|
+
exitedStage,
|
|
2867
|
+
enteredStage
|
|
2868
|
+
}) {
|
|
2869
|
+
await persistThenDeploy({
|
|
2468
2870
|
ctx,
|
|
2469
2871
|
mutation,
|
|
2470
|
-
() => deployStageGuards({
|
|
2872
|
+
deploy: () => deployStageGuards({
|
|
2471
2873
|
client: ctx.client,
|
|
2472
2874
|
clientForGdr: ctx.clientForGdr,
|
|
2473
2875
|
instance: materializeInstance(ctx.instance, mutation),
|
|
@@ -2475,7 +2877,7 @@ async function persistStageMove(ctx, mutation, exitedStage, enteredStage) {
|
|
|
2475
2877
|
stageName: enteredStage,
|
|
2476
2878
|
now: ctx.now
|
|
2477
2879
|
})
|
|
2478
|
-
), await retractStageGuards({
|
|
2880
|
+
}), await retractStageGuards({
|
|
2479
2881
|
client: ctx.client,
|
|
2480
2882
|
clientForGdr: ctx.clientForGdr,
|
|
2481
2883
|
instance: ctx.instance,
|
|
@@ -2500,16 +2902,16 @@ async function commitTransition(ctx, actor) {
|
|
|
2500
2902
|
actor,
|
|
2501
2903
|
self: selfGdr(ctx.instance),
|
|
2502
2904
|
now: at
|
|
2503
|
-
}), await queueEffects(
|
|
2905
|
+
}), await queueEffects({
|
|
2504
2906
|
ctx,
|
|
2505
2907
|
mutation,
|
|
2506
|
-
transition.effects,
|
|
2507
|
-
{
|
|
2908
|
+
effects: transition.effects,
|
|
2909
|
+
origin: {
|
|
2508
2910
|
kind: "transition",
|
|
2509
2911
|
name: transition.name
|
|
2510
2912
|
},
|
|
2511
2913
|
actor
|
|
2512
|
-
), mutation.history.push(
|
|
2914
|
+
}), mutation.history.push(
|
|
2513
2915
|
...stageTransitionHistory({
|
|
2514
2916
|
fromStage: currentStage.name,
|
|
2515
2917
|
toStage: transition.to,
|
|
@@ -2521,7 +2923,12 @@ async function commitTransition(ctx, actor) {
|
|
|
2521
2923
|
const priorEntry = findOpenStageEntry(mutation);
|
|
2522
2924
|
priorEntry !== void 0 && (priorEntry.exitedAt = at);
|
|
2523
2925
|
const nextStage = findStage(ctx.definition, transition.to);
|
|
2524
|
-
return await enterStage(ctx, mutation, nextStage, actor, at), await persistStageMove(
|
|
2926
|
+
return await enterStage({ ctx, mutation, nextStage, actor, at }), await persistStageMove({
|
|
2927
|
+
ctx,
|
|
2928
|
+
mutation,
|
|
2929
|
+
exitedStage: currentStage.name,
|
|
2930
|
+
enteredStage: nextStage.name
|
|
2931
|
+
}), {
|
|
2525
2932
|
fired: !0,
|
|
2526
2933
|
fromStage: currentStage.name,
|
|
2527
2934
|
toStage: transition.to,
|
|
@@ -2530,20 +2937,30 @@ async function commitTransition(ctx, actor) {
|
|
|
2530
2937
|
}
|
|
2531
2938
|
async function pickTransition(ctx, stage) {
|
|
2532
2939
|
for (const candidate of stage.transitions ?? []) {
|
|
2533
|
-
const outcome = await ctxEvaluateConditionOutcome(ctx, candidate.filter);
|
|
2940
|
+
const outcome = await ctxEvaluateConditionOutcome({ ctx, condition: candidate.filter });
|
|
2534
2941
|
if (outcome === "satisfied") return candidate;
|
|
2535
2942
|
if (outcome === "unevaluable") return;
|
|
2536
2943
|
}
|
|
2537
2944
|
}
|
|
2538
2945
|
async function evaluateAutoTransitions(args) {
|
|
2539
|
-
const { client, instanceId, options, clientForGdr, clock, overlay } = args, ctx = await loadContext(
|
|
2540
|
-
|
|
2541
|
-
|
|
2542
|
-
|
|
2946
|
+
const { client, instanceId, options, clientForGdr, clock, overlay } = args, ctx = await loadContext({
|
|
2947
|
+
client,
|
|
2948
|
+
instanceId,
|
|
2949
|
+
options: {
|
|
2950
|
+
...clientForGdr ? { clientForGdr } : {},
|
|
2951
|
+
...clock ? { clock } : {},
|
|
2952
|
+
...overlay ? { overlay } : {}
|
|
2953
|
+
}
|
|
2543
2954
|
});
|
|
2544
2955
|
return commitTransition(ctx, options?.actor);
|
|
2545
2956
|
}
|
|
2546
|
-
async function primeInitialStage(
|
|
2957
|
+
async function primeInitialStage({
|
|
2958
|
+
client,
|
|
2959
|
+
instanceId,
|
|
2960
|
+
actor,
|
|
2961
|
+
clientForGdr,
|
|
2962
|
+
clock
|
|
2963
|
+
}) {
|
|
2547
2964
|
const instance = await client.getDocument(instanceId);
|
|
2548
2965
|
if (!instance || instance.stages.length > 0) return;
|
|
2549
2966
|
const definition = parseDefinitionSnapshot(instance), stage = definition.stages.find((s) => s.name === instance.currentStage);
|
|
@@ -2563,7 +2980,7 @@ async function primeInitialStage(client, instanceId, actor, clientForGdr, clock)
|
|
|
2563
2980
|
instance,
|
|
2564
2981
|
stage,
|
|
2565
2982
|
now,
|
|
2566
|
-
recordDiscard: recordFieldDiscards(discards, "stage", now)
|
|
2983
|
+
recordDiscard: recordFieldDiscards({ target: discards, scope: "stage", at: now })
|
|
2567
2984
|
}),
|
|
2568
2985
|
activities: await buildStageActivities(ctx, stage)
|
|
2569
2986
|
}, committed = await client.patch(instance._id).set({
|
|
@@ -2588,7 +3005,7 @@ async function primeInitialStage(client, instanceId, actor, clientForGdr, clock)
|
|
|
2588
3005
|
stageName: stage.name,
|
|
2589
3006
|
now
|
|
2590
3007
|
})
|
|
2591
|
-
}), await autoActivatePrimedActivities(
|
|
3008
|
+
}), await autoActivatePrimedActivities({
|
|
2592
3009
|
client,
|
|
2593
3010
|
instanceId,
|
|
2594
3011
|
definition,
|
|
@@ -2596,9 +3013,17 @@ async function primeInitialStage(client, instanceId, actor, clientForGdr, clock)
|
|
|
2596
3013
|
actor,
|
|
2597
3014
|
clientForGdr,
|
|
2598
3015
|
clock
|
|
2599
|
-
);
|
|
3016
|
+
});
|
|
2600
3017
|
}
|
|
2601
|
-
async function autoActivatePrimedActivities(
|
|
3018
|
+
async function autoActivatePrimedActivities({
|
|
3019
|
+
client,
|
|
3020
|
+
instanceId,
|
|
3021
|
+
definition,
|
|
3022
|
+
stage,
|
|
3023
|
+
actor,
|
|
3024
|
+
clientForGdr,
|
|
3025
|
+
clock
|
|
3026
|
+
}) {
|
|
2602
3027
|
const resolvedClientForGdr = clientForGdr ?? (() => client);
|
|
2603
3028
|
for (const activity of stage.activities ?? []) {
|
|
2604
3029
|
if (activity.activation !== "auto") continue;
|
|
@@ -2611,7 +3036,7 @@ async function autoActivatePrimedActivities(client, instanceId, definition, stag
|
|
|
2611
3036
|
definition,
|
|
2612
3037
|
...clock ? { clock } : {}
|
|
2613
3038
|
}), mutation = startMutation(updated), mutEntry = currentActivities(mutation).find((t) => t.name === activity.name);
|
|
2614
|
-
mutEntry && mutEntry.status === "pending" && (await activateActivity(updatedCtx, mutation, activity, mutEntry, actor), await persist(updatedCtx, mutation));
|
|
3039
|
+
mutEntry && mutEntry.status === "pending" && (await activateActivity({ ctx: updatedCtx, mutation, activity, entry: mutEntry, actor }), await persist(updatedCtx, mutation));
|
|
2615
3040
|
}
|
|
2616
3041
|
}
|
|
2617
3042
|
const CASCADE_LIMIT = 100;
|
|
@@ -2620,20 +3045,30 @@ async function gatherAutoResolveCandidates(ctx, activities) {
|
|
|
2620
3045
|
for (const activity of activities) {
|
|
2621
3046
|
const entry = currentActivitiesList.find((e) => e.name === activity.name);
|
|
2622
3047
|
if (entry?.status !== "active") continue;
|
|
2623
|
-
const outcome = await evaluateResolutionGate(
|
|
3048
|
+
const outcome = await evaluateResolutionGate({
|
|
2624
3049
|
ctx,
|
|
2625
3050
|
activity,
|
|
2626
|
-
await activityConditionVars(ctx, entry)
|
|
2627
|
-
);
|
|
3051
|
+
vars: await activityConditionVars(ctx, entry)
|
|
3052
|
+
});
|
|
2628
3053
|
outcome !== void 0 && candidates.push({ activity, outcome });
|
|
2629
3054
|
}
|
|
2630
3055
|
return candidates;
|
|
2631
3056
|
}
|
|
2632
|
-
async function resolveCompleteWhen(
|
|
2633
|
-
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
3057
|
+
async function resolveCompleteWhen({
|
|
3058
|
+
client,
|
|
3059
|
+
instanceId,
|
|
3060
|
+
clientForGdr,
|
|
3061
|
+
clock,
|
|
3062
|
+
overlay
|
|
3063
|
+
}) {
|
|
3064
|
+
const ctx = await loadContext({
|
|
3065
|
+
client,
|
|
3066
|
+
instanceId,
|
|
3067
|
+
options: {
|
|
3068
|
+
...clientForGdr ? { clientForGdr } : {},
|
|
3069
|
+
...clock ? { clock } : {},
|
|
3070
|
+
...overlay ? { overlay } : {}
|
|
3071
|
+
}
|
|
2637
3072
|
}), stage = findStage(ctx.definition, ctx.instance.currentStage), gatedActivities = (stage.activities ?? []).filter(
|
|
2638
3073
|
(t) => effectiveCompleteWhen(t) !== void 0 || t.failWhen !== void 0
|
|
2639
3074
|
);
|
|
@@ -2644,21 +3079,27 @@ async function resolveCompleteWhen(client, instanceId, clientForGdr, clock, over
|
|
|
2644
3079
|
let count = 0;
|
|
2645
3080
|
for (const { activity, outcome } of candidates) {
|
|
2646
3081
|
const mutEntry = currentActivities(mutation).find((t) => t.name === activity.name);
|
|
2647
|
-
mutEntry === void 0 || mutEntry.status !== "active" || (
|
|
3082
|
+
mutEntry === void 0 || mutEntry.status !== "active" || (applyGateOutcome({
|
|
2648
3083
|
entry: mutEntry,
|
|
2649
3084
|
history: mutation.history,
|
|
2650
3085
|
stage: stage.name,
|
|
2651
|
-
to: outcome,
|
|
2652
3086
|
at: ctx.now,
|
|
2653
|
-
|
|
3087
|
+
outcome
|
|
2654
3088
|
}), count++);
|
|
2655
3089
|
}
|
|
2656
3090
|
return count > 0 && await persist(ctx, mutation), count;
|
|
2657
3091
|
}
|
|
2658
|
-
async function cascadeAutoTransitions(
|
|
3092
|
+
async function cascadeAutoTransitions({
|
|
3093
|
+
client,
|
|
3094
|
+
instanceId,
|
|
3095
|
+
actor,
|
|
3096
|
+
clientForGdr,
|
|
3097
|
+
clock,
|
|
3098
|
+
overlay
|
|
3099
|
+
}) {
|
|
2659
3100
|
let count = 0;
|
|
2660
3101
|
for (; ; ) {
|
|
2661
|
-
if (await resolveCompleteWhen(client, instanceId, clientForGdr, clock, overlay), !(await evaluateAutoTransitions({
|
|
3102
|
+
if (await resolveCompleteWhen({ client, instanceId, clientForGdr, clock, overlay }), !(await evaluateAutoTransitions({
|
|
2662
3103
|
client,
|
|
2663
3104
|
instanceId,
|
|
2664
3105
|
...actor !== void 0 ? { options: { actor } } : {},
|
|
@@ -2670,14 +3111,19 @@ async function cascadeAutoTransitions(client, instanceId, actor, clientForGdr, c
|
|
|
2670
3111
|
throw new CascadeLimitError({ instanceId, limit: CASCADE_LIMIT });
|
|
2671
3112
|
}
|
|
2672
3113
|
}
|
|
2673
|
-
async function findResolvedParentSpawn(
|
|
3114
|
+
async function findResolvedParentSpawn({
|
|
3115
|
+
client,
|
|
3116
|
+
child,
|
|
3117
|
+
clientForGdr,
|
|
3118
|
+
clock
|
|
3119
|
+
}) {
|
|
2674
3120
|
const parentRef = child.ancestors.at(-1);
|
|
2675
3121
|
if (parentRef === void 0) return;
|
|
2676
|
-
const parent = await client.getDocument(
|
|
3122
|
+
const parent = await client.getDocument(toBareId(parentRef.id));
|
|
2677
3123
|
if (!parent || parent._type !== WORKFLOW_INSTANCE_TYPE || typeof parent.definitionSnapshot != "string") return;
|
|
2678
3124
|
const definition = parseDefinitionSnapshot(parent), stage = definition.stages.find((s) => s.name === parent.currentStage);
|
|
2679
3125
|
if (stage === void 0) return;
|
|
2680
|
-
const parentCurrentActivities = findCurrentActivities(parent), activity = (stage.activities ?? []).find((t) => t.subworkflows === void 0 ? !1 : parentCurrentActivities.find((e) => e.name === t.name)?.spawnedInstances?.some((r) =>
|
|
3126
|
+
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);
|
|
2681
3127
|
if (activity?.subworkflows === void 0) return;
|
|
2682
3128
|
const entry = parentCurrentActivities.find((e) => e.name === activity.name);
|
|
2683
3129
|
if (entry === void 0 || entry.status !== "active") return;
|
|
@@ -2687,18 +3133,29 @@ async function findResolvedParentSpawn(client, child, clientForGdr, clock) {
|
|
|
2687
3133
|
instance: parent,
|
|
2688
3134
|
definition,
|
|
2689
3135
|
...clock ? { clock } : {}
|
|
2690
|
-
}), outcome = await evaluateResolutionGate(
|
|
3136
|
+
}), outcome = await evaluateResolutionGate({
|
|
2691
3137
|
ctx,
|
|
2692
3138
|
activity,
|
|
2693
|
-
await activityConditionVars(ctx, entry)
|
|
2694
|
-
);
|
|
3139
|
+
vars: await activityConditionVars(ctx, entry)
|
|
3140
|
+
});
|
|
2695
3141
|
if (outcome !== void 0)
|
|
2696
3142
|
return { parent, definition, stage, activity, outcome };
|
|
2697
3143
|
}
|
|
2698
|
-
async function propagateToAncestors(
|
|
3144
|
+
async function propagateToAncestors({
|
|
3145
|
+
client,
|
|
3146
|
+
instanceId,
|
|
3147
|
+
actor,
|
|
3148
|
+
clientForGdr,
|
|
3149
|
+
clock
|
|
3150
|
+
}) {
|
|
2699
3151
|
const instance = await client.getDocument(instanceId);
|
|
2700
3152
|
if (!instance) return;
|
|
2701
|
-
const resolvedClientForGdr = clientForGdr ?? (() => client), found = await findResolvedParentSpawn(
|
|
3153
|
+
const resolvedClientForGdr = clientForGdr ?? (() => client), found = await findResolvedParentSpawn({
|
|
3154
|
+
client,
|
|
3155
|
+
child: instance,
|
|
3156
|
+
clientForGdr: resolvedClientForGdr,
|
|
3157
|
+
clock
|
|
3158
|
+
});
|
|
2702
3159
|
if (found === void 0) return;
|
|
2703
3160
|
const { parent, definition, stage, activity, outcome } = found, ctx = await buildEngineContext({
|
|
2704
3161
|
client,
|
|
@@ -2707,14 +3164,13 @@ async function propagateToAncestors(client, instanceId, actor, clientForGdr, clo
|
|
|
2707
3164
|
definition,
|
|
2708
3165
|
...clock ? { clock } : {}
|
|
2709
3166
|
}), mutation = startMutation(parent), mutEntry = currentActivities(mutation).find((e) => e.name === activity.name);
|
|
2710
|
-
mutEntry !== void 0 && (
|
|
3167
|
+
mutEntry !== void 0 && (applyGateOutcome({
|
|
2711
3168
|
entry: mutEntry,
|
|
2712
3169
|
history: mutation.history,
|
|
2713
3170
|
stage: stage.name,
|
|
2714
|
-
to: outcome,
|
|
2715
3171
|
at: ctx.now,
|
|
2716
|
-
|
|
2717
|
-
}), await persist(ctx, mutation), await cascadeAutoTransitions(client, parent._id, actor, clientForGdr, clock), await propagateToAncestors(client, parent._id, actor, clientForGdr, clock));
|
|
3172
|
+
outcome
|
|
3173
|
+
}), await persist(ctx, mutation), await cascadeAutoTransitions({ client, instanceId: parent._id, actor, clientForGdr, clock }), await propagateToAncestors({ client, instanceId: parent._id, actor, clientForGdr, clock }));
|
|
2718
3174
|
}
|
|
2719
3175
|
const parsedFilters = /* @__PURE__ */ new Map();
|
|
2720
3176
|
async function matchesFilter(args) {
|
|
@@ -2755,7 +3211,12 @@ async function resolveAccess(client, args = {}) {
|
|
|
2755
3211
|
);
|
|
2756
3212
|
return { actor: overrideActor };
|
|
2757
3213
|
}
|
|
2758
|
-
const actorPromise = overrideActor !== void 0 ? Promise.resolve(overrideActor) : cachedActor(client, requestFn), grantsPromise = resolveGrants(
|
|
3214
|
+
const actorPromise = overrideActor !== void 0 ? Promise.resolve(overrideActor) : cachedActor(client, requestFn), grantsPromise = resolveGrants({
|
|
3215
|
+
overrideGrants,
|
|
3216
|
+
grantsFromPath: args.grantsFromPath,
|
|
3217
|
+
client,
|
|
3218
|
+
requestFn
|
|
3219
|
+
}), [actor, grants] = await Promise.all([actorPromise, grantsPromise]);
|
|
2759
3220
|
if (actor === void 0)
|
|
2760
3221
|
throw new Error(
|
|
2761
3222
|
"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."
|
|
@@ -2770,10 +3231,19 @@ function cachedActor(client, requestFn) {
|
|
|
2770
3231
|
});
|
|
2771
3232
|
return actorCache.set(client, pending), pending;
|
|
2772
3233
|
}
|
|
2773
|
-
function resolveGrants(
|
|
2774
|
-
|
|
2775
|
-
|
|
2776
|
-
|
|
3234
|
+
function resolveGrants({
|
|
3235
|
+
overrideGrants,
|
|
3236
|
+
grantsFromPath,
|
|
3237
|
+
client,
|
|
3238
|
+
requestFn
|
|
3239
|
+
}) {
|
|
3240
|
+
return overrideGrants !== void 0 ? Promise.resolve(overrideGrants) : grantsFromPath !== void 0 ? cachedGrants({ client, requestFn, resourcePath: grantsFromPath }) : Promise.resolve(void 0);
|
|
3241
|
+
}
|
|
3242
|
+
function cachedGrants({
|
|
3243
|
+
client,
|
|
3244
|
+
requestFn,
|
|
3245
|
+
resourcePath
|
|
3246
|
+
}) {
|
|
2777
3247
|
let byPath = grantsCache.get(client);
|
|
2778
3248
|
byPath === void 0 && (byPath = /* @__PURE__ */ new Map(), grantsCache.set(client, byPath));
|
|
2779
3249
|
let cached = byPath.get(resourcePath);
|
|
@@ -2873,9 +3343,7 @@ function slotWindowOpen(instance, slot) {
|
|
|
2873
3343
|
if (instance.completedAt !== void 0) return { open: !1, detail: "instance completed" };
|
|
2874
3344
|
if (instance.abortedAt !== void 0) return { open: !1, detail: "instance aborted" };
|
|
2875
3345
|
if (slot.scope !== "activity") return { open: !0 };
|
|
2876
|
-
const status =
|
|
2877
|
-
(t) => t.name === slot.activity
|
|
2878
|
-
)?.status;
|
|
3346
|
+
const status = findCurrentActivityEntry(instance, slot.activity)?.status;
|
|
2879
3347
|
return status === "active" ? { open: !0 } : { open: !1, detail: `activity "${slot.activity}" is ${status ?? "not active"}` };
|
|
2880
3348
|
}
|
|
2881
3349
|
function readSlotValue(instance, slot) {
|
|
@@ -2910,7 +3378,7 @@ async function evaluateInstance(args) {
|
|
|
2910
3378
|
const { actor, grants } = await resolveAccess(client, {
|
|
2911
3379
|
...args.access !== void 0 ? { override: args.access } : {},
|
|
2912
3380
|
...args.grantsFromPath !== void 0 ? { grantsFromPath: args.grantsFromPath } : {}
|
|
2913
|
-
}), 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);
|
|
3381
|
+
}), 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);
|
|
2914
3382
|
return evaluateFromSnapshot({
|
|
2915
3383
|
instance,
|
|
2916
3384
|
definition,
|
|
@@ -2927,7 +3395,7 @@ async function evaluateFromSnapshot(args) {
|
|
|
2927
3395
|
throw new Error(
|
|
2928
3396
|
`Instance "${instance._id}" currentStage "${instance.currentStage}" not in definition`
|
|
2929
3397
|
);
|
|
2930
|
-
const scope = await renderScope({ instance, definition, actor, grants, snapshot, now }), currentActivityEntries = findOpenStageEntry(instance)?.activities ?? [], guardDenial = await instanceGuardReason(instance, actor, args.guards), activityEvaluations = [];
|
|
3398
|
+
const scope = await renderScope({ instance, definition, actor, grants, snapshot, now }), currentActivityEntries = findOpenStageEntry(instance)?.activities ?? [], guardDenial = await instanceGuardReason({ instance, actor, guards: args.guards }), activityEvaluations = [];
|
|
2931
3399
|
for (const activity of stage.activities ?? [])
|
|
2932
3400
|
activityEvaluations.push(
|
|
2933
3401
|
await evaluateActivity({
|
|
@@ -3030,7 +3498,7 @@ async function renderScope(args) {
|
|
|
3030
3498
|
...buildParams({ instance, now, snapshot }),
|
|
3031
3499
|
actor,
|
|
3032
3500
|
assigned: !1,
|
|
3033
|
-
can: await advisoryCan(instance, actor, grants)
|
|
3501
|
+
can: await advisoryCan({ instance, actor, grants })
|
|
3034
3502
|
};
|
|
3035
3503
|
return { ...await evaluatePredicates({
|
|
3036
3504
|
predicates: definition.predicates,
|
|
@@ -3038,7 +3506,11 @@ async function renderScope(args) {
|
|
|
3038
3506
|
params
|
|
3039
3507
|
}), ...params };
|
|
3040
3508
|
}
|
|
3041
|
-
async function advisoryCan(
|
|
3509
|
+
async function advisoryCan({
|
|
3510
|
+
instance,
|
|
3511
|
+
actor,
|
|
3512
|
+
grants
|
|
3513
|
+
}) {
|
|
3042
3514
|
if (grants === void 0) return;
|
|
3043
3515
|
const can = {};
|
|
3044
3516
|
for (const permission of schema.DOCUMENT_VALUE_PERMISSIONS)
|
|
@@ -3056,9 +3528,9 @@ function activityScopeFor(args) {
|
|
|
3056
3528
|
...scope,
|
|
3057
3529
|
fields: {
|
|
3058
3530
|
...scope.fields,
|
|
3059
|
-
...scopedFieldOverlay(instance, snapshot, activityName)
|
|
3531
|
+
...scopedFieldOverlay({ instance, snapshot, activityName })
|
|
3060
3532
|
},
|
|
3061
|
-
assigned: assignedFor(instance, activityName, actor, roleAliases)
|
|
3533
|
+
assigned: assignedFor({ instance, activityName, actor, roleAliases })
|
|
3062
3534
|
};
|
|
3063
3535
|
}
|
|
3064
3536
|
async function evaluateActivity(args) {
|
|
@@ -3120,14 +3592,18 @@ async function evaluateAction(args) {
|
|
|
3120
3592
|
stageHasExits,
|
|
3121
3593
|
guardDenial,
|
|
3122
3594
|
requirementsReason
|
|
3123
|
-
} = args, lifecycle = lifecycleReason(instance, status, stageHasExits);
|
|
3595
|
+
} = args, lifecycle = lifecycleReason({ instance, status, stageHasExits });
|
|
3124
3596
|
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({
|
|
3125
3597
|
condition: action.filter,
|
|
3126
3598
|
snapshot,
|
|
3127
3599
|
params: activityScope
|
|
3128
3600
|
}) ? disabled(action, { kind: "filter-failed", filter: action.filter }) : { action, allowed: !0 };
|
|
3129
3601
|
}
|
|
3130
|
-
async function instanceGuardReason(
|
|
3602
|
+
async function instanceGuardReason({
|
|
3603
|
+
instance,
|
|
3604
|
+
actor,
|
|
3605
|
+
guards
|
|
3606
|
+
}) {
|
|
3131
3607
|
if (guards === void 0 || guards.length === 0) return;
|
|
3132
3608
|
const denied = await instanceWriteDenials({ instance, guards, identity: actor.id });
|
|
3133
3609
|
if (denied.length !== 0)
|
|
@@ -3137,7 +3613,11 @@ async function instanceGuardReason(instance, actor, guards) {
|
|
|
3137
3613
|
detail: denied.map((g) => g.name ?? g._id).join(", ")
|
|
3138
3614
|
};
|
|
3139
3615
|
}
|
|
3140
|
-
function lifecycleReason(
|
|
3616
|
+
function lifecycleReason({
|
|
3617
|
+
instance,
|
|
3618
|
+
status,
|
|
3619
|
+
stageHasExits
|
|
3620
|
+
}) {
|
|
3141
3621
|
if (instance.completedAt !== void 0)
|
|
3142
3622
|
return { kind: "instance-completed", completedAt: instance.completedAt };
|
|
3143
3623
|
if (!stageHasExits)
|
|
@@ -3148,10 +3628,11 @@ function lifecycleReason(instance, status, stageHasExits) {
|
|
|
3148
3628
|
function disabled(action, reason) {
|
|
3149
3629
|
return { action, allowed: !1, disabledReason: reason };
|
|
3150
3630
|
}
|
|
3151
|
-
function
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3631
|
+
async function sortByDependencies({
|
|
3632
|
+
client,
|
|
3633
|
+
definitions,
|
|
3634
|
+
tag
|
|
3635
|
+
}) {
|
|
3155
3636
|
const byName = /* @__PURE__ */ new Map();
|
|
3156
3637
|
for (const def of definitions) {
|
|
3157
3638
|
if (byName.has(def.name))
|
|
@@ -3161,18 +3642,22 @@ async function sortByDependencies(client, definitions, tag) {
|
|
|
3161
3642
|
byName.set(def.name, def);
|
|
3162
3643
|
}
|
|
3163
3644
|
const findInBatch = (ref) => findRefInBatch(byName, ref);
|
|
3164
|
-
return await assertCrossBatchRefsDeployed(client, definitions, { tag, findInBatch }), topoSortDefinitions(definitions, { findInBatch });
|
|
3645
|
+
return await assertCrossBatchRefsDeployed({ client, definitions, ctx: { tag, findInBatch } }), topoSortDefinitions(definitions, { findInBatch });
|
|
3165
3646
|
}
|
|
3166
3647
|
function findRefInBatch(byName, ref) {
|
|
3167
3648
|
if (typeof ref.version != "number")
|
|
3168
3649
|
return byName.get(ref.name);
|
|
3169
3650
|
}
|
|
3170
|
-
async function assertCrossBatchRefsDeployed(
|
|
3651
|
+
async function assertCrossBatchRefsDeployed({
|
|
3652
|
+
client,
|
|
3653
|
+
definitions,
|
|
3654
|
+
ctx
|
|
3655
|
+
}) {
|
|
3171
3656
|
const missing = [];
|
|
3172
3657
|
for (const def of definitions)
|
|
3173
3658
|
for (const ref of refsOf(def)) {
|
|
3174
3659
|
if (ctx.findInBatch(ref) !== void 0) continue;
|
|
3175
|
-
const label = await resolveDeployedRefLabel(client, ref, ctx.tag);
|
|
3660
|
+
const label = await resolveDeployedRefLabel({ client, ref, tag: ctx.tag });
|
|
3176
3661
|
label !== void 0 && missing.push({ from: def.name, ref: label });
|
|
3177
3662
|
}
|
|
3178
3663
|
if (missing.length === 0) return;
|
|
@@ -3183,7 +3668,11 @@ async function assertCrossBatchRefsDeployed(client, definitions, ctx) {
|
|
|
3183
3668
|
`)
|
|
3184
3669
|
);
|
|
3185
3670
|
}
|
|
3186
|
-
async function resolveDeployedRefLabel(
|
|
3671
|
+
async function resolveDeployedRefLabel({
|
|
3672
|
+
client,
|
|
3673
|
+
ref,
|
|
3674
|
+
tag
|
|
3675
|
+
}) {
|
|
3187
3676
|
const wantsExplicit = typeof ref.version == "number", params = { definition: ref.name, tag };
|
|
3188
3677
|
if (wantsExplicit && (params.version = ref.version), !await client.fetch(
|
|
3189
3678
|
definitionLookupGroq(wantsExplicit),
|
|
@@ -3226,9 +3715,17 @@ function hashDefinitionContent(def) {
|
|
|
3226
3715
|
hash ^= BigInt(canonical.charCodeAt(i)), hash = hash * 0x100000001b3n & 0xffffffffffffffffn;
|
|
3227
3716
|
return hash.toString(16).padStart(16, "0");
|
|
3228
3717
|
}
|
|
3229
|
-
function planDefinitionDeploy(
|
|
3230
|
-
|
|
3231
|
-
|
|
3718
|
+
function planDefinitionDeploy({
|
|
3719
|
+
def,
|
|
3720
|
+
latest,
|
|
3721
|
+
target
|
|
3722
|
+
}) {
|
|
3723
|
+
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({
|
|
3724
|
+
tag: target.tag,
|
|
3725
|
+
definition: expanded.name,
|
|
3726
|
+
version
|
|
3727
|
+
}), document = {
|
|
3728
|
+
...expanded,
|
|
3232
3729
|
_id: docId,
|
|
3233
3730
|
_type: schema.WORKFLOW_DEFINITION_TYPE,
|
|
3234
3731
|
tag: target.tag,
|
|
@@ -3237,6 +3734,38 @@ function planDefinitionDeploy(def, latest, target) {
|
|
|
3237
3734
|
};
|
|
3238
3735
|
return { status: unchanged ? "unchanged" : "create", version, contentHash, docId, document };
|
|
3239
3736
|
}
|
|
3737
|
+
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"]);
|
|
3738
|
+
function expandResourceAliases(definition, resourceAliases) {
|
|
3739
|
+
assertResourceAliasNamesValid(resourceAliases);
|
|
3740
|
+
const map = resourceAliases ?? {};
|
|
3741
|
+
return mapJsonStrings(
|
|
3742
|
+
definition,
|
|
3743
|
+
(value, key) => key !== void 0 && PROSE_KEYS.has(key) ? value : expandAliasString({ value, map, definitionName: definition.name })
|
|
3744
|
+
);
|
|
3745
|
+
}
|
|
3746
|
+
function expandAliasString({
|
|
3747
|
+
value,
|
|
3748
|
+
map,
|
|
3749
|
+
definitionName
|
|
3750
|
+
}) {
|
|
3751
|
+
const expanded = value.replace(RESOURCE_ALIAS_REF_RE, (_match, alias) => {
|
|
3752
|
+
if (!Object.hasOwn(map, alias))
|
|
3753
|
+
throw new Error(
|
|
3754
|
+
`workflow.deployDefinitions: definition "${definitionName}" references unbound resource alias "@${alias}". Bind it in the deploy target's resource map, or remove the reference.`
|
|
3755
|
+
);
|
|
3756
|
+
return gdrResourcePrefix(map[alias]);
|
|
3757
|
+
});
|
|
3758
|
+
if (expanded === value) return value;
|
|
3759
|
+
if (STANDALONE_RESOURCE_ALIAS_RE.test(value) && !isGdrUri(expanded))
|
|
3760
|
+
throw new Error(
|
|
3761
|
+
`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.`
|
|
3762
|
+
);
|
|
3763
|
+
return expanded;
|
|
3764
|
+
}
|
|
3765
|
+
function assertResourceAliasNamesValid(resourceAliases) {
|
|
3766
|
+
for (const name of Object.keys(resourceAliases ?? {}))
|
|
3767
|
+
validateResourceAliasName(name);
|
|
3768
|
+
}
|
|
3240
3769
|
function stripSystemFields(doc) {
|
|
3241
3770
|
const out = {};
|
|
3242
3771
|
for (const [k, v2] of Object.entries(doc))
|
|
@@ -3248,7 +3777,13 @@ function stableStringify(value) {
|
|
|
3248
3777
|
([a], [b]) => a.localeCompare(b)
|
|
3249
3778
|
).map(([k, v2]) => `${JSON.stringify(k)}:${stableStringify(v2)}`).join(",")}}`;
|
|
3250
3779
|
}
|
|
3251
|
-
|
|
3780
|
+
const noDeployedDefinitionMessage = (definition) => `No deployed definition for workflow ${definition}`;
|
|
3781
|
+
async function loadDefinition({
|
|
3782
|
+
client,
|
|
3783
|
+
definition,
|
|
3784
|
+
version,
|
|
3785
|
+
tag
|
|
3786
|
+
}) {
|
|
3252
3787
|
if (version !== void 0) {
|
|
3253
3788
|
const doc = await client.fetch(definitionLookupGroq(!0), {
|
|
3254
3789
|
definition,
|
|
@@ -3259,31 +3794,50 @@ async function loadDefinition(client, definition, version, tag) {
|
|
|
3259
3794
|
throw new Error(`Workflow definition ${definition} v${version} not deployed`);
|
|
3260
3795
|
return doc;
|
|
3261
3796
|
}
|
|
3262
|
-
const latest = await loadLatestDeployed(client, definition, tag);
|
|
3797
|
+
const latest = await loadLatestDeployed({ client, definition, tag });
|
|
3263
3798
|
if (!latest)
|
|
3264
|
-
throw new Error(
|
|
3799
|
+
throw new Error(noDeployedDefinitionMessage(definition));
|
|
3265
3800
|
return latest;
|
|
3266
3801
|
}
|
|
3267
|
-
async function loadLatestDeployed(
|
|
3802
|
+
async function loadLatestDeployed({
|
|
3803
|
+
client,
|
|
3804
|
+
definition,
|
|
3805
|
+
tag
|
|
3806
|
+
}) {
|
|
3268
3807
|
return await client.fetch(definitionLookupGroq(!1), {
|
|
3269
3808
|
definition,
|
|
3270
3809
|
tag
|
|
3271
3810
|
}) ?? void 0;
|
|
3272
3811
|
}
|
|
3273
|
-
async function loadDefinitionVersions(
|
|
3812
|
+
async function loadDefinitionVersions({
|
|
3813
|
+
client,
|
|
3814
|
+
definition,
|
|
3815
|
+
tag
|
|
3816
|
+
}) {
|
|
3274
3817
|
return client.fetch(
|
|
3275
3818
|
`*[_type == "${schema.WORKFLOW_DEFINITION_TYPE}" && name == $definition && ${tagScopeFilter()}] | order(version desc)`,
|
|
3276
3819
|
{ definition, tag }
|
|
3277
3820
|
);
|
|
3278
3821
|
}
|
|
3279
|
-
async function
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3822
|
+
async function loadDefinitionVersionsOrThrow({
|
|
3823
|
+
client,
|
|
3824
|
+
definition,
|
|
3825
|
+
tag
|
|
3826
|
+
}) {
|
|
3827
|
+
const versions = await loadDefinitionVersions({ client, definition, tag });
|
|
3828
|
+
if (versions.length === 0)
|
|
3829
|
+
throw new Error(noDeployedDefinitionMessage(definition));
|
|
3830
|
+
return versions;
|
|
3285
3831
|
}
|
|
3286
|
-
async function
|
|
3832
|
+
async function abortInstance(args) {
|
|
3833
|
+
const { client, instanceId, reason, options } = args, ctx = await loadCallContext({ client, instanceId, options });
|
|
3834
|
+
return commitAbort({ ctx, reason, actor: options?.actor });
|
|
3835
|
+
}
|
|
3836
|
+
async function commitAbort({
|
|
3837
|
+
ctx,
|
|
3838
|
+
reason,
|
|
3839
|
+
actor
|
|
3840
|
+
}) {
|
|
3287
3841
|
if (ctx.instance.completedAt !== void 0)
|
|
3288
3842
|
return { fired: !1 };
|
|
3289
3843
|
const stage = findStage(ctx.definition, ctx.instance.currentStage), mutation = startMutation(ctx.instance), at = ctx.now, openEntry = findOpenStageEntry(mutation);
|
|
@@ -3317,30 +3871,44 @@ async function commitAbort(ctx, reason, actor) {
|
|
|
3317
3871
|
}
|
|
3318
3872
|
async function fireAction(args) {
|
|
3319
3873
|
const { client, instanceId, activity, action, params, options } = args;
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
try {
|
|
3323
|
-
return await commitAction(ctx, activity, action, params, options);
|
|
3324
|
-
} catch (error) {
|
|
3325
|
-
if (!isRevisionConflict(error)) throw error;
|
|
3326
|
-
}
|
|
3327
|
-
}
|
|
3328
|
-
throw new ConcurrentFireActionError({
|
|
3874
|
+
return retryOnRevisionConflict({
|
|
3875
|
+
client,
|
|
3329
3876
|
instanceId,
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3877
|
+
options,
|
|
3878
|
+
commit: (ctx) => commitAction({
|
|
3879
|
+
ctx,
|
|
3880
|
+
activityName: activity,
|
|
3881
|
+
actionName: action,
|
|
3882
|
+
callerParams: params,
|
|
3883
|
+
options
|
|
3884
|
+
}),
|
|
3885
|
+
onExhausted: () => new ConcurrentFireActionError({
|
|
3886
|
+
instanceId,
|
|
3887
|
+
activity,
|
|
3888
|
+
action,
|
|
3889
|
+
attempts: CONCURRENT_FIRE_ACTION_MAX_ATTEMPTS
|
|
3890
|
+
})
|
|
3333
3891
|
});
|
|
3334
3892
|
}
|
|
3335
|
-
async function resolveActionCommit(
|
|
3893
|
+
async function resolveActionCommit({
|
|
3894
|
+
ctx,
|
|
3895
|
+
activityName,
|
|
3896
|
+
actionName,
|
|
3897
|
+
callerParams,
|
|
3898
|
+
options
|
|
3899
|
+
}) {
|
|
3336
3900
|
const actor = options?.actor, { stage, activity } = findActivityInCurrentStage(ctx, activityName), action = (activity.actions ?? []).find((a) => a.name === actionName);
|
|
3337
3901
|
if (action === void 0)
|
|
3338
3902
|
throw new Error(`Action "${actionName}" not declared on activity "${activityName}"`);
|
|
3339
|
-
const can = options?.grants !== void 0 && actor !== void 0 ? await advisoryCan(ctx.instance, actor, options.grants) : void 0;
|
|
3340
|
-
if (!await ctxEvaluateCondition(
|
|
3341
|
-
|
|
3342
|
-
|
|
3343
|
-
|
|
3903
|
+
const can = options?.grants !== void 0 && actor !== void 0 ? await advisoryCan({ instance: ctx.instance, actor, grants: options.grants }) : void 0;
|
|
3904
|
+
if (!await ctxEvaluateCondition({
|
|
3905
|
+
ctx,
|
|
3906
|
+
condition: action.filter,
|
|
3907
|
+
opts: {
|
|
3908
|
+
activityName,
|
|
3909
|
+
...actor !== void 0 ? { actor } : {},
|
|
3910
|
+
...can !== void 0 ? { vars: { can } } : {}
|
|
3911
|
+
}
|
|
3344
3912
|
}))
|
|
3345
3913
|
throw new Error(`Action "${actionName}" filter rejected on activity "${activityName}"`);
|
|
3346
3914
|
const entry = findCurrentActivities(ctx.instance).find((t) => t.name === activityName);
|
|
@@ -3348,17 +3916,23 @@ async function resolveActionCommit(ctx, activityName, actionName, callerParams,
|
|
|
3348
3916
|
throw new Error(
|
|
3349
3917
|
`Activity "${activityName}" must be active to fire action "${actionName}"; status is ${entry?.status ?? "missing"}`
|
|
3350
3918
|
);
|
|
3351
|
-
const params = validateActionParams(action, activityName, callerParams);
|
|
3919
|
+
const params = validateActionParams({ action, activityName, callerParams });
|
|
3352
3920
|
return { stage, activity, action, params };
|
|
3353
3921
|
}
|
|
3354
|
-
async function commitAction(
|
|
3355
|
-
|
|
3922
|
+
async function commitAction({
|
|
3923
|
+
ctx,
|
|
3924
|
+
activityName,
|
|
3925
|
+
actionName,
|
|
3926
|
+
callerParams,
|
|
3927
|
+
options
|
|
3928
|
+
}) {
|
|
3929
|
+
const actor = options?.actor, { stage, action, params } = await resolveActionCommit({
|
|
3356
3930
|
ctx,
|
|
3357
3931
|
activityName,
|
|
3358
3932
|
actionName,
|
|
3359
3933
|
callerParams,
|
|
3360
3934
|
options
|
|
3361
|
-
), mutation = startMutation(ctx.instance), mutEntry = requireMutationActivityEntry(mutation, activityName), now = ctx.now;
|
|
3935
|
+
}), mutation = startMutation(ctx.instance), mutEntry = requireMutationActivityEntry(mutation, activityName), now = ctx.now;
|
|
3362
3936
|
mutation.history.push({
|
|
3363
3937
|
_key: randomKey(),
|
|
3364
3938
|
_type: "actionFired",
|
|
@@ -3378,14 +3952,19 @@ async function commitAction(ctx, activityName, actionName, callerParams, options
|
|
|
3378
3952
|
self: selfGdr(ctx.instance),
|
|
3379
3953
|
now
|
|
3380
3954
|
}), newStatus = mutEntry.status !== statusBefore ? mutEntry.status : void 0;
|
|
3381
|
-
return await queueEffects(
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3955
|
+
return await queueEffects({
|
|
3956
|
+
ctx,
|
|
3957
|
+
mutation,
|
|
3958
|
+
effects: action.effects,
|
|
3959
|
+
origin: { kind: "action", name: actionName },
|
|
3960
|
+
actor,
|
|
3961
|
+
opts: { callerParams: params, activityName }
|
|
3962
|
+
}), await persistThenMaybeRefresh({
|
|
3385
3963
|
ctx,
|
|
3386
3964
|
mutation,
|
|
3387
|
-
|
|
3388
|
-
|
|
3965
|
+
stageName: stage.name,
|
|
3966
|
+
didChangeState: ranOps.some(isFieldOp)
|
|
3967
|
+
}), {
|
|
3389
3968
|
fired: !0,
|
|
3390
3969
|
activity: activityName,
|
|
3391
3970
|
action: actionName,
|
|
@@ -3398,7 +3977,7 @@ class ActionDisabledError extends Error {
|
|
|
3398
3977
|
activity;
|
|
3399
3978
|
action;
|
|
3400
3979
|
constructor(args) {
|
|
3401
|
-
super(formatDisabledReason(args.activity, args.action, args.reason)), this.name = "ActionDisabledError", this.reason = args.reason, this.activity = args.activity, this.action = args.action;
|
|
3980
|
+
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;
|
|
3402
3981
|
}
|
|
3403
3982
|
}
|
|
3404
3983
|
const disabledReasonDetail = {
|
|
@@ -3409,7 +3988,11 @@ const disabledReasonDetail = {
|
|
|
3409
3988
|
"mutation-guard-denied": (r) => `blocked by mutation guard(s) [${r.guardIds.join(", ")}]${r.detail ? ` (${r.detail})` : ""}`,
|
|
3410
3989
|
"requirements-unmet": (r) => `unmet requirement(s): ${r.unmetRequirements.join(", ")}`
|
|
3411
3990
|
};
|
|
3412
|
-
function formatDisabledReason(
|
|
3991
|
+
function formatDisabledReason({
|
|
3992
|
+
activity,
|
|
3993
|
+
action,
|
|
3994
|
+
reason
|
|
3995
|
+
}) {
|
|
3413
3996
|
const detail = disabledReasonDetail[reason.kind](reason);
|
|
3414
3997
|
return `Action "${activity}:${action}" is not allowed: ${detail}`;
|
|
3415
3998
|
}
|
|
@@ -3435,28 +4018,32 @@ function formatEditDisabledReason(target, reason) {
|
|
|
3435
4018
|
}
|
|
3436
4019
|
async function editField(args) {
|
|
3437
4020
|
const { client, instanceId, target, mode = "set", value, options } = args;
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
try {
|
|
3441
|
-
return await commitEdit(ctx, target, mode, value, options);
|
|
3442
|
-
} catch (error) {
|
|
3443
|
-
if (!isRevisionConflict(error)) throw error;
|
|
3444
|
-
}
|
|
3445
|
-
}
|
|
3446
|
-
throw new ConcurrentEditFieldError({
|
|
4021
|
+
return retryOnRevisionConflict({
|
|
4022
|
+
client,
|
|
3447
4023
|
instanceId,
|
|
3448
|
-
|
|
3449
|
-
|
|
4024
|
+
options,
|
|
4025
|
+
commit: (ctx) => commitEdit({ ctx, target, mode, value, options }),
|
|
4026
|
+
onExhausted: () => new ConcurrentEditFieldError({
|
|
4027
|
+
instanceId,
|
|
4028
|
+
target: labelTarget(target),
|
|
4029
|
+
attempts: CONCURRENT_FIRE_ACTION_MAX_ATTEMPTS
|
|
4030
|
+
})
|
|
3450
4031
|
});
|
|
3451
4032
|
}
|
|
3452
|
-
async function commitEdit(
|
|
4033
|
+
async function commitEdit({
|
|
4034
|
+
ctx,
|
|
4035
|
+
target,
|
|
4036
|
+
mode,
|
|
4037
|
+
value,
|
|
4038
|
+
options
|
|
4039
|
+
}) {
|
|
3453
4040
|
const actor = options?.actor, stage = findStage(ctx.definition, ctx.instance.currentStage), slot = resolveEditTarget({ definition: ctx.definition, stage, target });
|
|
3454
4041
|
if (slot === void 0)
|
|
3455
4042
|
throw new Error(
|
|
3456
4043
|
`No field slot "${describeTarget(target)}" resolves in current stage "${stage.name}" of ${ctx.definition.name}`
|
|
3457
4044
|
);
|
|
3458
|
-
await assertSlotEditable(ctx, slot, options);
|
|
3459
|
-
const op = buildEditOp(slot, mode, value), mutation = startMutation(ctx.instance), ranOps = runOps({
|
|
4045
|
+
await assertSlotEditable({ ctx, slot, options });
|
|
4046
|
+
const op = buildEditOp({ slot, mode, value }), mutation = startMutation(ctx.instance), ranOps = runOps({
|
|
3460
4047
|
ops: [op],
|
|
3461
4048
|
mutation,
|
|
3462
4049
|
stage: stage.name,
|
|
@@ -3469,21 +4056,30 @@ async function commitEdit(ctx, target, mode, value, options) {
|
|
|
3469
4056
|
self: selfGdr(ctx.instance),
|
|
3470
4057
|
now: ctx.now
|
|
3471
4058
|
});
|
|
3472
|
-
return await
|
|
4059
|
+
return await persistThenMaybeRefresh({
|
|
3473
4060
|
ctx,
|
|
3474
4061
|
mutation,
|
|
3475
|
-
|
|
3476
|
-
|
|
4062
|
+
stageName: stage.name,
|
|
4063
|
+
didChangeState: ranOps.some(isFieldOp)
|
|
4064
|
+
}), {
|
|
3477
4065
|
edited: !0,
|
|
3478
4066
|
target: slotTarget(slot),
|
|
3479
4067
|
...ranOps.length > 0 ? { ranOps } : {}
|
|
3480
4068
|
};
|
|
3481
4069
|
}
|
|
3482
|
-
async function assertSlotEditable(
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
4070
|
+
async function assertSlotEditable({
|
|
4071
|
+
ctx,
|
|
4072
|
+
slot,
|
|
4073
|
+
options
|
|
4074
|
+
}) {
|
|
4075
|
+
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({
|
|
4076
|
+
ctx,
|
|
4077
|
+
condition: slot.effective,
|
|
4078
|
+
opts: {
|
|
4079
|
+
...slot.activity !== void 0 ? { activityName: slot.activity } : {},
|
|
4080
|
+
...actor !== void 0 ? { actor } : {},
|
|
4081
|
+
...can !== void 0 ? { vars: { can } } : {}
|
|
4082
|
+
}
|
|
3487
4083
|
}) : slot.effective === !0, reason = editDisabledReason({
|
|
3488
4084
|
effective: slot.effective,
|
|
3489
4085
|
instance: ctx.instance,
|
|
@@ -3493,7 +4089,11 @@ async function assertSlotEditable(ctx, slot, options) {
|
|
|
3493
4089
|
});
|
|
3494
4090
|
if (reason !== void 0) throw new EditFieldDeniedError({ target: slotTarget(slot), reason });
|
|
3495
4091
|
}
|
|
3496
|
-
function buildEditOp(
|
|
4092
|
+
function buildEditOp({
|
|
4093
|
+
slot,
|
|
4094
|
+
mode,
|
|
4095
|
+
value
|
|
4096
|
+
}) {
|
|
3497
4097
|
if (mode === "unset") return { type: "field.unset", target: slot.ref };
|
|
3498
4098
|
if (value === void 0)
|
|
3499
4099
|
throw new Error(`editField mode "${mode}" requires a value for slot "${slot.name}"`);
|
|
@@ -3541,25 +4141,45 @@ async function resolveOperationContext(args) {
|
|
|
3541
4141
|
clientForGdr: buildClientForGdr(args.client, args.resourceClients)
|
|
3542
4142
|
};
|
|
3543
4143
|
}
|
|
3544
|
-
async function cascade(
|
|
3545
|
-
|
|
4144
|
+
async function cascade({
|
|
4145
|
+
client,
|
|
4146
|
+
instanceId,
|
|
4147
|
+
actor,
|
|
4148
|
+
clientForGdr,
|
|
4149
|
+
clock,
|
|
4150
|
+
overlay
|
|
4151
|
+
}) {
|
|
4152
|
+
const count = await cascadeAutoTransitions({
|
|
3546
4153
|
client,
|
|
3547
4154
|
instanceId,
|
|
3548
4155
|
actor,
|
|
3549
|
-
clientForGdr,
|
|
3550
|
-
clock,
|
|
3551
|
-
overlay
|
|
3552
|
-
);
|
|
3553
|
-
return await propagateToAncestors(
|
|
4156
|
+
...clientForGdr !== void 0 ? { clientForGdr } : {},
|
|
4157
|
+
...clock !== void 0 ? { clock } : {},
|
|
4158
|
+
...overlay !== void 0 ? { overlay } : {}
|
|
4159
|
+
});
|
|
4160
|
+
return await propagateToAncestors({
|
|
4161
|
+
client,
|
|
4162
|
+
instanceId,
|
|
4163
|
+
actor,
|
|
4164
|
+
...clientForGdr !== void 0 ? { clientForGdr } : {},
|
|
4165
|
+
...clock !== void 0 ? { clock } : {}
|
|
4166
|
+
}), count;
|
|
4167
|
+
}
|
|
4168
|
+
function engineOptionsForActor({
|
|
4169
|
+
actor,
|
|
4170
|
+
clock,
|
|
4171
|
+
clientForGdr
|
|
4172
|
+
}) {
|
|
4173
|
+
return { ...actor !== void 0 ? { actor } : {}, clock, clientForGdr };
|
|
3554
4174
|
}
|
|
3555
4175
|
async function abortAndPropagate(args) {
|
|
3556
4176
|
const { client, instanceId, reason, actor, clientForGdr, clock } = args, result = await abortInstance({
|
|
3557
4177
|
client,
|
|
3558
4178
|
instanceId,
|
|
3559
4179
|
...reason !== void 0 ? { reason } : {},
|
|
3560
|
-
options: {
|
|
4180
|
+
options: engineOptionsForActor({ actor, clock, clientForGdr })
|
|
3561
4181
|
});
|
|
3562
|
-
return result.fired && await propagateToAncestors(client, instanceId, actor, clientForGdr, clock), result.fired;
|
|
4182
|
+
return result.fired && await propagateToAncestors({ client, instanceId, actor, clientForGdr, clock }), result.fired;
|
|
3563
4183
|
}
|
|
3564
4184
|
function buildClientForGdr(defaultClient, resolver) {
|
|
3565
4185
|
return resolver === void 0 ? () => defaultClient : (parsed) => resolver(parsed) ?? defaultClient;
|
|
@@ -3575,15 +4195,25 @@ async function dispatchGatedWrite(args) {
|
|
|
3575
4195
|
access,
|
|
3576
4196
|
clock: args.clock,
|
|
3577
4197
|
...resourceClients !== void 0 ? { resourceClients } : {}
|
|
3578
|
-
}), ranOps = await apply(args.before, evaluation), cascaded = await cascade(
|
|
4198
|
+
}), ranOps = await apply(args.before, evaluation), cascaded = await cascade({
|
|
4199
|
+
client,
|
|
4200
|
+
instanceId,
|
|
4201
|
+
actor: args.actor,
|
|
4202
|
+
clientForGdr: args.clientForGdr,
|
|
4203
|
+
clock: args.clock
|
|
4204
|
+
});
|
|
3579
4205
|
return {
|
|
3580
|
-
instance: await reload(client, instanceId, tag),
|
|
4206
|
+
instance: await reload({ client, instanceId, tag }),
|
|
3581
4207
|
cascaded,
|
|
3582
4208
|
fired: !0,
|
|
3583
4209
|
...ranOps !== void 0 ? { ranOps } : {}
|
|
3584
4210
|
};
|
|
3585
4211
|
}
|
|
3586
|
-
function assertActionAllowed(
|
|
4212
|
+
function assertActionAllowed({
|
|
4213
|
+
evaluation,
|
|
4214
|
+
activity,
|
|
4215
|
+
action
|
|
4216
|
+
}) {
|
|
3587
4217
|
const actionEval = evaluation.currentStage.activities.find((t) => t.activity.name === activity)?.actions.find((a) => a.action.name === action);
|
|
3588
4218
|
if (actionEval?.allowed === !1 && actionEval.disabledReason)
|
|
3589
4219
|
throw new ActionDisabledError({ activity, action, reason: actionEval.disabledReason });
|
|
@@ -3621,7 +4251,7 @@ function buildEngineCallOptions(args) {
|
|
|
3621
4251
|
}
|
|
3622
4252
|
async function applyAction(args) {
|
|
3623
4253
|
const { client, instance, activity, action, params, actor, grants, clock, clientForGdr } = args, options = buildEngineCallOptions({ actor, grants, clock, clientForGdr });
|
|
3624
|
-
return
|
|
4254
|
+
return findCurrentActivityEntry(instance, activity)?.status === "pending" && await invokeActivity({ client, instanceId: instance._id, activity, options }), (await fireAction({
|
|
3625
4255
|
client,
|
|
3626
4256
|
instanceId: instance._id,
|
|
3627
4257
|
activity,
|
|
@@ -3631,15 +4261,12 @@ async function applyAction(args) {
|
|
|
3631
4261
|
})).ranOps;
|
|
3632
4262
|
}
|
|
3633
4263
|
async function deleteDefinition(args) {
|
|
3634
|
-
const { client, tag, definition, version, cascade: cascade2, reason } = args, { actor, clientForGdr } = await resolveOperationContext(args), clock = args.clock ?? wallClock, versions = await
|
|
3635
|
-
if (versions.length === 0)
|
|
3636
|
-
throw new Error(`No deployed definition for workflow ${definition}`);
|
|
3637
|
-
const targets = version === void 0 ? versions : versions.filter((d) => d.version === version);
|
|
4264
|
+
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);
|
|
3638
4265
|
if (targets.length === 0)
|
|
3639
4266
|
throw new Error(`Workflow definition ${definition} v${version} not deployed`);
|
|
3640
4267
|
const lastVersionGoes = targets.length === versions.length;
|
|
3641
|
-
await assertNoSpawnReferrers(client, tag, definition, targets, lastVersionGoes);
|
|
3642
|
-
const instanceIds = await nonTerminalInstanceIds(client, tag, definition, version);
|
|
4268
|
+
await assertNoSpawnReferrers({ client, tag, definition, targets, lastVersionGoes });
|
|
4269
|
+
const instanceIds = await nonTerminalInstanceIds({ client, tag, definition, version });
|
|
3643
4270
|
if (instanceIds.length > 0 && cascade2 !== !0)
|
|
3644
4271
|
throw new Error(
|
|
3645
4272
|
`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.`
|
|
@@ -3669,7 +4296,13 @@ async function deleteDefinition(args) {
|
|
|
3669
4296
|
deletedGuardCount
|
|
3670
4297
|
};
|
|
3671
4298
|
}
|
|
3672
|
-
async function assertNoSpawnReferrers(
|
|
4299
|
+
async function assertNoSpawnReferrers({
|
|
4300
|
+
client,
|
|
4301
|
+
tag,
|
|
4302
|
+
definition,
|
|
4303
|
+
targets,
|
|
4304
|
+
lastVersionGoes
|
|
4305
|
+
}) {
|
|
3673
4306
|
const deployed = await client.fetch(
|
|
3674
4307
|
`*[_type == "${schema.WORKFLOW_DEFINITION_TYPE}" && ${tagScopeFilter()}]`,
|
|
3675
4308
|
{ tag }
|
|
@@ -3685,7 +4318,12 @@ async function assertNoSpawnReferrers(client, tag, definition, targets, lastVers
|
|
|
3685
4318
|
);
|
|
3686
4319
|
}
|
|
3687
4320
|
}
|
|
3688
|
-
async function nonTerminalInstanceIds(
|
|
4321
|
+
async function nonTerminalInstanceIds({
|
|
4322
|
+
client,
|
|
4323
|
+
tag,
|
|
4324
|
+
definition,
|
|
4325
|
+
version
|
|
4326
|
+
}) {
|
|
3689
4327
|
const versionFilter = version === void 0 ? "" : " && pinnedVersion == $version";
|
|
3690
4328
|
return (await client.fetch(
|
|
3691
4329
|
`*[_type == "${WORKFLOW_INSTANCE_TYPE}" && definition == $definition && ${tagScopeFilter()} && !defined(completedAt)${versionFilter}] | order(_id asc){_id}`,
|
|
@@ -3722,14 +4360,19 @@ const workflow = {
|
|
|
3722
4360
|
* Cycles in the dependency graph error before any write happens.
|
|
3723
4361
|
*/
|
|
3724
4362
|
deployDefinitions: async (args) => {
|
|
3725
|
-
const { client, tag,
|
|
3726
|
-
validateTag(tag);
|
|
3727
|
-
|
|
3728
|
-
validateDefinition(def);
|
|
3729
|
-
const ordered = await sortByDependencies(client, definitions, tag), results = [], tx = client.transaction();
|
|
4363
|
+
const { client, tag, resourceAliases, definitions } = args;
|
|
4364
|
+
validateTag(tag), definitions.forEach(validateDefinition);
|
|
4365
|
+
const ordered = await sortByDependencies({ client, definitions, tag }), results = [], tx = client.transaction();
|
|
3730
4366
|
let hasWrites = !1;
|
|
3731
4367
|
for (const def of ordered) {
|
|
3732
|
-
const latest = await loadLatestDeployed(client, def.name, tag), plan = planDefinitionDeploy(
|
|
4368
|
+
const latest = await loadLatestDeployed({ client, definition: def.name, tag }), plan = planDefinitionDeploy({
|
|
4369
|
+
def,
|
|
4370
|
+
latest,
|
|
4371
|
+
target: {
|
|
4372
|
+
tag,
|
|
4373
|
+
...resourceAliases !== void 0 ? { resourceAliases } : {}
|
|
4374
|
+
}
|
|
4375
|
+
});
|
|
3733
4376
|
if (plan.status === "unchanged") {
|
|
3734
4377
|
results.push({ definition: def.name, version: plan.version, status: "unchanged" });
|
|
3735
4378
|
continue;
|
|
@@ -3766,7 +4409,7 @@ const workflow = {
|
|
|
3766
4409
|
effectsContext,
|
|
3767
4410
|
instanceId,
|
|
3768
4411
|
perspective
|
|
3769
|
-
} = args, { actor, clientForGdr } = await resolveOperationContext(args), clock = args.clock ?? wallClock, definition = await loadDefinition(client, definitionName, version, tag), id = instanceId ??
|
|
4412
|
+
} = args, { actor, clientForGdr } = await resolveOperationContext(args), clock = args.clock ?? wallClock, definition = await loadDefinition({ client, definition: definitionName, version, tag }), id = instanceId ?? instanceDocId(tag);
|
|
3770
4413
|
if (definition.stages.find((s) => s.name === definition.initialStage) === void 0)
|
|
3771
4414
|
throw new Error(
|
|
3772
4415
|
`Initial stage "${definition.initialStage}" missing in ${definitionName} v${definition.version}`
|
|
@@ -3782,7 +4425,7 @@ const workflow = {
|
|
|
3782
4425
|
workflowResource,
|
|
3783
4426
|
definitionName: definition.name,
|
|
3784
4427
|
...perspective !== void 0 ? { perspective } : {},
|
|
3785
|
-
recordDiscard: recordFieldDiscards(fieldDiscards, "workflow", now)
|
|
4428
|
+
recordDiscard: recordFieldDiscards({ target: fieldDiscards, scope: "workflow", at: now })
|
|
3786
4429
|
},
|
|
3787
4430
|
randomKey
|
|
3788
4431
|
}), effectivePerspective = perspective ?? derivePerspectiveFromFields(resolvedFields), base = buildInstanceBase({
|
|
@@ -3802,7 +4445,7 @@ const workflow = {
|
|
|
3802
4445
|
actor,
|
|
3803
4446
|
...fieldDiscards.length > 0 ? { extraHistory: fieldDiscards } : {}
|
|
3804
4447
|
});
|
|
3805
|
-
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);
|
|
4448
|
+
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 });
|
|
3806
4449
|
},
|
|
3807
4450
|
/**
|
|
3808
4451
|
* Fire an action against an activity. If the activity is pending, it is
|
|
@@ -3824,8 +4467,8 @@ const workflow = {
|
|
|
3824
4467
|
params,
|
|
3825
4468
|
idempotent,
|
|
3826
4469
|
resourceClients
|
|
3827
|
-
} = args, { access, actor, clientForGdr } = await resolveOperationContext(args), clock = args.clock ?? wallClock, before = await reload(client, instanceId, tag);
|
|
3828
|
-
return
|
|
4470
|
+
} = args, { access, actor, clientForGdr } = await resolveOperationContext(args), clock = args.clock ?? wallClock, before = await reload({ client, instanceId, tag });
|
|
4471
|
+
return findCurrentActivityEntry(before, activity) === void 0 && idempotent === !0 ? { instance: before, cascaded: 0, fired: !1 } : dispatchGatedWrite({
|
|
3829
4472
|
client,
|
|
3830
4473
|
tag,
|
|
3831
4474
|
instanceId,
|
|
@@ -3835,7 +4478,7 @@ const workflow = {
|
|
|
3835
4478
|
clock,
|
|
3836
4479
|
before,
|
|
3837
4480
|
...resourceClients !== void 0 ? { resourceClients } : {},
|
|
3838
|
-
apply: (instance, evaluation) => (assertActionAllowed(evaluation, activity, action), applyAction({
|
|
4481
|
+
apply: (instance, evaluation) => (assertActionAllowed({ evaluation, activity, action }), applyAction({
|
|
3839
4482
|
client,
|
|
3840
4483
|
instance,
|
|
3841
4484
|
activity,
|
|
@@ -3864,7 +4507,7 @@ const workflow = {
|
|
|
3864
4507
|
* never an `onChange` per keystroke.
|
|
3865
4508
|
*/
|
|
3866
4509
|
editField: async (args) => {
|
|
3867
|
-
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);
|
|
4510
|
+
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 });
|
|
3868
4511
|
return dispatchGatedWrite({
|
|
3869
4512
|
client,
|
|
3870
4513
|
tag,
|
|
@@ -3893,24 +4536,27 @@ const workflow = {
|
|
|
3893
4536
|
* appends an `effectHistory` entry, and (if `outputs` are supplied
|
|
3894
4537
|
* and the effect succeeded) upserts those values into
|
|
3895
4538
|
* `effectsContext` by key — so downstream effect bindings can
|
|
3896
|
-
* reference them.
|
|
4539
|
+
* reference them. Any `ops` the handler returned (`field.*`) are
|
|
4540
|
+
* validated and applied to the instance in the same commit, through the
|
|
4541
|
+
* same op applier an action's field ops use. Cascades after.
|
|
3897
4542
|
*/
|
|
3898
4543
|
completeEffect: async (args) => {
|
|
3899
|
-
const { client, tag, instanceId, effectKey, status, outputs, detail, error, durationMs } = args, { actor, clientForGdr } = await resolveOperationContext(args), clock = args.clock ?? wallClock;
|
|
4544
|
+
const { client, tag, instanceId, effectKey, status, outputs, ops, detail, error, durationMs } = args, { actor, clientForGdr } = await resolveOperationContext(args), clock = args.clock ?? wallClock;
|
|
3900
4545
|
await completeEffect({
|
|
3901
4546
|
client,
|
|
3902
4547
|
instanceId,
|
|
3903
4548
|
effectKey,
|
|
3904
4549
|
status,
|
|
3905
4550
|
...outputs !== void 0 ? { outputs } : {},
|
|
4551
|
+
...ops !== void 0 ? { ops } : {},
|
|
3906
4552
|
...detail !== void 0 ? { detail } : {},
|
|
3907
4553
|
...error !== void 0 ? { error } : {},
|
|
3908
4554
|
...durationMs !== void 0 ? { durationMs } : {},
|
|
3909
|
-
options: {
|
|
4555
|
+
options: engineOptionsForActor({ actor, clock, clientForGdr })
|
|
3910
4556
|
});
|
|
3911
|
-
const cascaded = await cascade(client, instanceId, actor, clientForGdr, clock);
|
|
4557
|
+
const cascaded = await cascade({ client, instanceId, actor, clientForGdr, clock });
|
|
3912
4558
|
return {
|
|
3913
|
-
instance: await reload(client, instanceId, tag),
|
|
4559
|
+
instance: await reload({ client, instanceId, tag }),
|
|
3914
4560
|
cascaded,
|
|
3915
4561
|
fired: !0
|
|
3916
4562
|
};
|
|
@@ -3925,11 +4571,11 @@ const workflow = {
|
|
|
3925
4571
|
* affected instances and the engine re-evaluates.
|
|
3926
4572
|
*/
|
|
3927
4573
|
tick: async (args) => {
|
|
3928
|
-
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);
|
|
4574
|
+
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);
|
|
3929
4575
|
await assertInstanceWriteAllowed({ instance: current, guards, identity: actor.id });
|
|
3930
|
-
const cascaded = await cascade(client, instanceId, actor, clientForGdr, clock);
|
|
4576
|
+
const cascaded = await cascade({ client, instanceId, actor, clientForGdr, clock });
|
|
3931
4577
|
return {
|
|
3932
|
-
instance: await reload(client, instanceId, tag),
|
|
4578
|
+
instance: await reload({ client, instanceId, tag }),
|
|
3933
4579
|
cascaded,
|
|
3934
4580
|
fired: cascaded > 0
|
|
3935
4581
|
};
|
|
@@ -3941,16 +4587,16 @@ const workflow = {
|
|
|
3941
4587
|
*/
|
|
3942
4588
|
setStage: async (args) => {
|
|
3943
4589
|
const { client, tag, instanceId, targetStage, reason } = args, { actor, clientForGdr } = await resolveOperationContext(args), clock = args.clock ?? wallClock;
|
|
3944
|
-
await reload(client, instanceId, tag);
|
|
4590
|
+
await reload({ client, instanceId, tag });
|
|
3945
4591
|
const result = await setStage({
|
|
3946
4592
|
client,
|
|
3947
4593
|
instanceId,
|
|
3948
4594
|
targetStage,
|
|
3949
4595
|
...reason !== void 0 ? { reason } : {},
|
|
3950
|
-
options: {
|
|
3951
|
-
}), cascaded = result.fired ? await cascade(client, instanceId, actor, clientForGdr, clock) : 0;
|
|
4596
|
+
options: engineOptionsForActor({ actor, clock, clientForGdr })
|
|
4597
|
+
}), cascaded = result.fired ? await cascade({ client, instanceId, actor, clientForGdr, clock }) : 0;
|
|
3952
4598
|
return {
|
|
3953
|
-
instance: await reload(client, instanceId, tag),
|
|
4599
|
+
instance: await reload({ client, instanceId, tag }),
|
|
3954
4600
|
cascaded,
|
|
3955
4601
|
fired: result.fired
|
|
3956
4602
|
};
|
|
@@ -3963,10 +4609,10 @@ const workflow = {
|
|
|
3963
4609
|
*/
|
|
3964
4610
|
abortInstance: async (args) => {
|
|
3965
4611
|
const { client, tag, instanceId, reason } = args, { actor, clientForGdr } = await resolveOperationContext(args), clock = args.clock ?? wallClock;
|
|
3966
|
-
await reload(client, instanceId, tag);
|
|
4612
|
+
await reload({ client, instanceId, tag });
|
|
3967
4613
|
const fired = await abortAndPropagate({ client, instanceId, reason, actor, clientForGdr, clock });
|
|
3968
4614
|
return {
|
|
3969
|
-
instance: await reload(client, instanceId, tag),
|
|
4615
|
+
instance: await reload({ client, instanceId, tag }),
|
|
3970
4616
|
cascaded: 0,
|
|
3971
4617
|
fired
|
|
3972
4618
|
};
|
|
@@ -3978,12 +4624,12 @@ const workflow = {
|
|
|
3978
4624
|
*/
|
|
3979
4625
|
getInstance: async (args) => {
|
|
3980
4626
|
const { client, tag, instanceId } = args;
|
|
3981
|
-
return validateTag(tag), reload(client, instanceId, tag);
|
|
4627
|
+
return validateTag(tag), reload({ client, instanceId, tag });
|
|
3982
4628
|
},
|
|
3983
4629
|
guardsForInstance: async (args) => {
|
|
3984
4630
|
const { client, tag, instanceId, resourceClients } = args;
|
|
3985
4631
|
validateTag(tag);
|
|
3986
|
-
const instance = await reload(client, instanceId, tag);
|
|
4632
|
+
const instance = await reload({ client, instanceId, tag });
|
|
3987
4633
|
return guardsForInstance({
|
|
3988
4634
|
client,
|
|
3989
4635
|
clientForGdr: buildClientForGdr(client, resourceClients),
|
|
@@ -3993,9 +4639,7 @@ const workflow = {
|
|
|
3993
4639
|
guardsForDefinition: async (args) => {
|
|
3994
4640
|
const { client, tag, workflowResource, definition, resourceClients } = args;
|
|
3995
4641
|
validateTag(tag);
|
|
3996
|
-
const definitions = await
|
|
3997
|
-
if (definitions.length === 0)
|
|
3998
|
-
throw new Error(`No deployed definition for workflow ${definition}`);
|
|
4642
|
+
const definitions = await loadDefinitionVersionsOrThrow({ client, definition, tag });
|
|
3999
4643
|
return guardsForDefinition({
|
|
4000
4644
|
client,
|
|
4001
4645
|
clientForGdr: buildClientForGdr(client, resourceClients),
|
|
@@ -4040,7 +4684,7 @@ const workflow = {
|
|
|
4040
4684
|
queryInScope: async (args) => {
|
|
4041
4685
|
const { client, tag, instanceId, groq, params, resourceClients } = args, clock = args.clock ?? wallClock;
|
|
4042
4686
|
validateTag(tag);
|
|
4043
|
-
const instance = await reload(client, instanceId, tag), clientForGdr = buildClientForGdr(client, resourceClients), snapshot = await hydrateSnapshot({ client, clientForGdr, instance }), reserved = buildParams({ instance, now: clock(), snapshot }), tree = groqJs.parse(groq, { params: { ...reserved, ...params } });
|
|
4687
|
+
const instance = await reload({ client, instanceId, tag }), clientForGdr = buildClientForGdr(client, resourceClients), snapshot = await hydrateSnapshot({ client, clientForGdr, instance }), reserved = buildParams({ instance, now: clock(), snapshot }), tree = groqJs.parse(groq, { params: { ...reserved, ...params } });
|
|
4044
4688
|
return await (await groqJs.evaluate(tree, {
|
|
4045
4689
|
dataset: snapshot.docs,
|
|
4046
4690
|
params: { ...reserved, ...params }
|
|
@@ -4050,13 +4694,13 @@ const workflow = {
|
|
|
4050
4694
|
* List every pending effect on the instance. Returns the same entries
|
|
4051
4695
|
* the runtime would see — claimed and unclaimed alike.
|
|
4052
4696
|
*/
|
|
4053
|
-
listPendingEffects: async (args) => (await reload(args.client, args.instanceId, args.tag)).pendingEffects,
|
|
4697
|
+
listPendingEffects: async (args) => (await reload({ client: args.client, instanceId: args.instanceId, tag: args.tag })).pendingEffects,
|
|
4054
4698
|
/**
|
|
4055
4699
|
* Filter pending effects on the instance by criteria. `claimed`
|
|
4056
4700
|
* filters on claim presence; `names` restricts to specific effect
|
|
4057
4701
|
* names. Both filters compose (AND).
|
|
4058
4702
|
*/
|
|
4059
|
-
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))),
|
|
4703
|
+
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))),
|
|
4060
4704
|
/**
|
|
4061
4705
|
* Project the instance from a given actor's perspective. Returns a
|
|
4062
4706
|
* `WorkflowEvaluation` with per-action verdicts (`allowed` + a
|
|
@@ -4105,7 +4749,7 @@ const workflow = {
|
|
|
4105
4749
|
children: async (args) => {
|
|
4106
4750
|
const { client, tag, instanceId, activity } = args;
|
|
4107
4751
|
validateTag(tag);
|
|
4108
|
-
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));
|
|
4752
|
+
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));
|
|
4109
4753
|
return ids.length === 0 ? [] : client.fetch(
|
|
4110
4754
|
`*[_id in $ids && ${tagScopeFilter()}] | order(startedAt asc)`,
|
|
4111
4755
|
{ ids, tag }
|
|
@@ -4201,7 +4845,7 @@ async function verifyDeployedDefinitionsInternal(args) {
|
|
|
4201
4845
|
locations: m.locations,
|
|
4202
4846
|
definitionId: m.definitionId
|
|
4203
4847
|
};
|
|
4204
|
-
if (await applyMissingHandler(missingHandler, info, log) === "fail")
|
|
4848
|
+
if (await applyMissingHandler({ policy: missingHandler, info, log }) === "fail")
|
|
4205
4849
|
throw new Error(
|
|
4206
4850
|
`verifyDeployedDefinitions: missing handler for "${m.name}" referenced by ${m.definitionId} at ${m.locations.join(", ")}`
|
|
4207
4851
|
);
|
|
@@ -4242,21 +4886,30 @@ async function drainEffectsInternal(args) {
|
|
|
4242
4886
|
validateTag(tag);
|
|
4243
4887
|
const log = logger("drainEffects"), drained = [], failed = [], skipped = [], skippedKeys = /* @__PURE__ */ new Set();
|
|
4244
4888
|
for (; ; ) {
|
|
4245
|
-
const before = await reload(client, instanceId, tag), candidate = before.pendingEffects.find(
|
|
4889
|
+
const before = await reload({ client, instanceId, tag }), candidate = before.pendingEffects.find(
|
|
4246
4890
|
(e) => e.claim === void 0 && !skippedKeys.has(e._key)
|
|
4247
4891
|
);
|
|
4248
4892
|
if (candidate === void 0) break;
|
|
4249
4893
|
const handler = effectHandlers[candidate.name];
|
|
4250
4894
|
if (handler === void 0) {
|
|
4251
|
-
await assertSkippableOrThrow(missingHandler, candidate, instanceId, log), skipped.push(candidate), skippedKeys.add(candidate._key);
|
|
4895
|
+
await assertSkippableOrThrow({ missingHandler, candidate, instanceId, log }), skipped.push(candidate), skippedKeys.add(candidate._key);
|
|
4252
4896
|
continue;
|
|
4253
4897
|
}
|
|
4254
|
-
if (!await claimPendingEffect(
|
|
4255
|
-
const { outputs, dispatchError } = await dispatchEffect(handler, candidate, {
|
|
4898
|
+
if (!await claimPendingEffect({
|
|
4256
4899
|
client,
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4900
|
+
instance: before,
|
|
4901
|
+
effectKey: candidate._key,
|
|
4902
|
+
drainerActor
|
|
4903
|
+
})) continue;
|
|
4904
|
+
const { outputs, ops, dispatchError } = await dispatchEffect({
|
|
4905
|
+
handler,
|
|
4906
|
+
candidate,
|
|
4907
|
+
ctx: {
|
|
4908
|
+
client,
|
|
4909
|
+
clientFor,
|
|
4910
|
+
instanceId,
|
|
4911
|
+
logger
|
|
4912
|
+
}
|
|
4260
4913
|
});
|
|
4261
4914
|
await reportEffectOutcome({
|
|
4262
4915
|
client,
|
|
@@ -4267,32 +4920,44 @@ async function drainEffectsInternal(args) {
|
|
|
4267
4920
|
effectKey: candidate._key,
|
|
4268
4921
|
access: completionAccess,
|
|
4269
4922
|
outputs,
|
|
4923
|
+
ops,
|
|
4270
4924
|
dispatchError
|
|
4271
4925
|
}), dispatchError === void 0 ? drained.push(candidate) : failed.push(candidate);
|
|
4272
4926
|
}
|
|
4273
4927
|
return { drained, failed, skipped };
|
|
4274
4928
|
}
|
|
4275
4929
|
async function reportEffectOutcome(args) {
|
|
4276
|
-
const { outputs, dispatchError, resourceClients, ...rest } = args;
|
|
4930
|
+
const { outputs, ops, dispatchError, resourceClients, ...rest } = args;
|
|
4277
4931
|
await workflow.completeEffect({
|
|
4278
4932
|
...rest,
|
|
4279
4933
|
...resourceClients !== void 0 ? { resourceClients } : {},
|
|
4280
4934
|
status: dispatchError === void 0 ? "done" : "failed",
|
|
4281
4935
|
...outputs !== void 0 ? { outputs } : {},
|
|
4936
|
+
...ops !== void 0 ? { ops } : {},
|
|
4282
4937
|
...dispatchError !== void 0 ? { error: dispatchError } : {}
|
|
4283
4938
|
});
|
|
4284
4939
|
}
|
|
4285
|
-
async function assertSkippableOrThrow(
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4940
|
+
async function assertSkippableOrThrow({
|
|
4941
|
+
missingHandler,
|
|
4942
|
+
candidate,
|
|
4943
|
+
instanceId,
|
|
4944
|
+
log
|
|
4945
|
+
}) {
|
|
4946
|
+
if (await applyMissingHandler({
|
|
4947
|
+
policy: missingHandler,
|
|
4948
|
+
info: { phase: "drain", name: candidate.name, effectKey: candidate._key, instanceId },
|
|
4289
4949
|
log
|
|
4290
|
-
) === "fail")
|
|
4950
|
+
}) === "fail")
|
|
4291
4951
|
throw new Error(
|
|
4292
4952
|
`drainEffects: no handler registered for "${candidate.name}" (effectKey=${candidate._key}, instanceId=${instanceId})`
|
|
4293
4953
|
);
|
|
4294
4954
|
}
|
|
4295
|
-
async function claimPendingEffect(
|
|
4955
|
+
async function claimPendingEffect({
|
|
4956
|
+
client,
|
|
4957
|
+
instance,
|
|
4958
|
+
effectKey,
|
|
4959
|
+
drainerActor
|
|
4960
|
+
}) {
|
|
4296
4961
|
const claim = {
|
|
4297
4962
|
_type: "pendingEffect.claim",
|
|
4298
4963
|
claimedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -4305,7 +4970,11 @@ async function claimPendingEffect(client, instance, effectKey, drainerActor) {
|
|
|
4305
4970
|
return !1;
|
|
4306
4971
|
}
|
|
4307
4972
|
}
|
|
4308
|
-
async function dispatchEffect(
|
|
4973
|
+
async function dispatchEffect({
|
|
4974
|
+
handler,
|
|
4975
|
+
candidate,
|
|
4976
|
+
ctx
|
|
4977
|
+
}) {
|
|
4309
4978
|
try {
|
|
4310
4979
|
const result = await handler(candidate.params, {
|
|
4311
4980
|
client: ctx.client,
|
|
@@ -4314,13 +4983,20 @@ async function dispatchEffect(handler, candidate, ctx) {
|
|
|
4314
4983
|
effectKey: candidate._key,
|
|
4315
4984
|
log: (message, extra) => ctx.logger(`effect.${candidate.name}`).info(message, extra)
|
|
4316
4985
|
});
|
|
4317
|
-
return
|
|
4986
|
+
return {
|
|
4987
|
+
...result?.outputs !== void 0 ? { outputs: result.outputs } : {},
|
|
4988
|
+
...result?.ops !== void 0 ? { ops: result.ops } : {}
|
|
4989
|
+
};
|
|
4318
4990
|
} catch (err) {
|
|
4319
4991
|
const message = err instanceof Error ? err.message : String(err), stack = err instanceof Error && err.stack !== void 0 ? err.stack : void 0;
|
|
4320
4992
|
return { dispatchError: stack !== void 0 ? { message, stack } : { message } };
|
|
4321
4993
|
}
|
|
4322
4994
|
}
|
|
4323
|
-
async function applyMissingHandler(
|
|
4995
|
+
async function applyMissingHandler({
|
|
4996
|
+
policy,
|
|
4997
|
+
info,
|
|
4998
|
+
log
|
|
4999
|
+
}) {
|
|
4324
5000
|
if (policy === "fail") return "fail";
|
|
4325
5001
|
if (policy === "skip")
|
|
4326
5002
|
return log.warn(`Missing effect handler "${info.name}" \u2014 skipping`, { info }), "skip";
|
|
@@ -4358,9 +5034,20 @@ function createInstanceSession(args) {
|
|
|
4358
5034
|
...clock !== void 0 ? { now: clock() } : {},
|
|
4359
5035
|
...grants !== void 0 ? { grants } : {}
|
|
4360
5036
|
});
|
|
4361
|
-
}, settleAfterApply = async (
|
|
4362
|
-
|
|
4363
|
-
|
|
5037
|
+
}, settleAfterApply = async ({
|
|
5038
|
+
actor,
|
|
5039
|
+
held,
|
|
5040
|
+
ranOps
|
|
5041
|
+
}) => {
|
|
5042
|
+
const cascaded = await cascade({
|
|
5043
|
+
client,
|
|
5044
|
+
instanceId: instance._id,
|
|
5045
|
+
actor,
|
|
5046
|
+
clientForGdr,
|
|
5047
|
+
...clock !== void 0 ? { clock } : {},
|
|
5048
|
+
overlay: held
|
|
5049
|
+
});
|
|
5050
|
+
return instance = await reload({ client, instanceId: instance._id, tag }), { instance, cascaded, fired: !0, ...ranOps !== void 0 ? { ranOps } : {} };
|
|
4364
5051
|
}, commit = async (run) => {
|
|
4365
5052
|
committing = !0;
|
|
4366
5053
|
const held = new Map(overlay);
|
|
@@ -4394,13 +5081,20 @@ function createInstanceSession(args) {
|
|
|
4394
5081
|
tick() {
|
|
4395
5082
|
return commit(async (actor, held) => {
|
|
4396
5083
|
await assertInstanceWriteAllowed({ instance, guards: heldGuards, identity: actor.id });
|
|
4397
|
-
const cascaded = await cascade(
|
|
4398
|
-
|
|
5084
|
+
const cascaded = await cascade({
|
|
5085
|
+
client,
|
|
5086
|
+
instanceId: instance._id,
|
|
5087
|
+
actor,
|
|
5088
|
+
clientForGdr,
|
|
5089
|
+
...clock !== void 0 ? { clock } : {},
|
|
5090
|
+
overlay: held
|
|
5091
|
+
});
|
|
5092
|
+
return instance = await reload({ client, instanceId: instance._id, tag }), { instance, cascaded, fired: cascaded > 0 };
|
|
4399
5093
|
});
|
|
4400
5094
|
},
|
|
4401
5095
|
fireAction({ activity, action, params }) {
|
|
4402
5096
|
return commit(async (actor, held) => {
|
|
4403
|
-
assertActionAllowed(await evaluateWith(held, heldGuards), activity, action);
|
|
5097
|
+
assertActionAllowed({ evaluation: await evaluateWith(held, heldGuards), activity, action });
|
|
4404
5098
|
const { grants } = await access(), ranOps = await applyAction({
|
|
4405
5099
|
client,
|
|
4406
5100
|
instance,
|
|
@@ -4412,7 +5106,7 @@ function createInstanceSession(args) {
|
|
|
4412
5106
|
...clock !== void 0 ? { clock } : {},
|
|
4413
5107
|
...params !== void 0 ? { params } : {}
|
|
4414
5108
|
});
|
|
4415
|
-
return settleAfterApply(actor, held, ranOps);
|
|
5109
|
+
return settleAfterApply({ actor, held, ranOps });
|
|
4416
5110
|
});
|
|
4417
5111
|
},
|
|
4418
5112
|
editField({ target, mode, value }) {
|
|
@@ -4429,7 +5123,7 @@ function createInstanceSession(args) {
|
|
|
4429
5123
|
...grants !== void 0 ? { grants } : {},
|
|
4430
5124
|
...clock !== void 0 ? { clock } : {}
|
|
4431
5125
|
});
|
|
4432
|
-
return settleAfterApply(actor, held, ranOps);
|
|
5126
|
+
return settleAfterApply({ actor, held, ranOps });
|
|
4433
5127
|
});
|
|
4434
5128
|
}
|
|
4435
5129
|
};
|
|
@@ -4564,8 +5258,12 @@ function createEngine(args) {
|
|
|
4564
5258
|
})
|
|
4565
5259
|
};
|
|
4566
5260
|
}
|
|
4567
|
-
function diffEntry(
|
|
4568
|
-
|
|
5261
|
+
function diffEntry({
|
|
5262
|
+
def,
|
|
5263
|
+
latestRaw,
|
|
5264
|
+
target
|
|
5265
|
+
}) {
|
|
5266
|
+
const plan = planDefinitionDeploy({ def, latest: asLatest(latestRaw), target }), existing = latestRaw ? stripSystemFields(latestRaw) : void 0;
|
|
4569
5267
|
return {
|
|
4570
5268
|
name: def.name,
|
|
4571
5269
|
version: plan.version,
|
|
@@ -4578,11 +5276,15 @@ function diffEntry(def, latestRaw, target) {
|
|
|
4578
5276
|
function asLatest(raw) {
|
|
4579
5277
|
return raw === void 0 ? void 0 : { version: typeof raw.version == "number" ? raw.version : 0, ...typeof raw.contentHash == "string" ? { contentHash: raw.contentHash } : {} };
|
|
4580
5278
|
}
|
|
4581
|
-
async function computeDiffEntries(
|
|
5279
|
+
async function computeDiffEntries({
|
|
5280
|
+
client,
|
|
5281
|
+
defs,
|
|
5282
|
+
target
|
|
5283
|
+
}) {
|
|
4582
5284
|
const entries = [];
|
|
4583
5285
|
for (const def of defs) {
|
|
4584
|
-
const latest = await loadLatestDeployed(client, def.name, target.tag);
|
|
4585
|
-
entries.push(diffEntry(def, latest, target));
|
|
5286
|
+
const latest = await loadLatestDeployed({ client, definition: def.name, tag: target.tag });
|
|
5287
|
+
entries.push(diffEntry({ def, latestRaw: latest, target }));
|
|
4586
5288
|
}
|
|
4587
5289
|
return entries;
|
|
4588
5290
|
}
|
|
@@ -4818,6 +5520,7 @@ exports.DISPLAY = DISPLAY;
|
|
|
4818
5520
|
exports.DRIVER_KIND_DISPLAY = DRIVER_KIND_DISPLAY;
|
|
4819
5521
|
exports.EFFECTS_CONTEXT_DISPLAY = EFFECTS_CONTEXT_DISPLAY;
|
|
4820
5522
|
exports.EditFieldDeniedError = EditFieldDeniedError;
|
|
5523
|
+
exports.EffectOpsInvalidError = EffectOpsInvalidError;
|
|
4821
5524
|
exports.FIELD_SLOT_DISPLAY = FIELD_SLOT_DISPLAY;
|
|
4822
5525
|
exports.GUARD_DOC_TYPE = GUARD_DOC_TYPE;
|
|
4823
5526
|
exports.GUARD_LIFTED_PREDICATE = GUARD_LIFTED_PREDICATE;
|