@sanity/workflow-engine 0.15.0 → 0.17.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/CHANGELOG.md +79 -0
- package/DATAMODEL.md +177 -33
- package/dist/_chunks-cjs/invariants.cjs +1377 -330
- package/dist/_chunks-es/invariants.js +1319 -326
- package/dist/define.cjs +3 -608
- package/dist/define.d.cts +125 -416
- package/dist/define.d.ts +125 -416
- package/dist/define.js +2 -607
- package/dist/index.cjs +5370 -3906
- package/dist/index.d.cts +1439 -597
- package/dist/index.d.ts +1439 -597
- package/dist/index.js +5191 -3829
- package/package.json +3 -3
package/dist/define.cjs
CHANGED
|
@@ -6,617 +6,12 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
|
|
7
7
|
var invariants = require("./_chunks-cjs/invariants.cjs");
|
|
8
8
|
|
|
9
|
-
function groq(strings, ...values) {
|
|
10
|
-
return strings.reduce((out, part, i) => i === 0 ? part : `${out}${serializeGroqValue(values[i - 1])}${part}`, "");
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function serializeGroqValue(value) {
|
|
14
|
-
const serialized = JSON.stringify(value);
|
|
15
|
-
if (serialized === void 0) throw new Error(`groq tag cannot serialize ${typeof value} — interpolate JSON-representable values only`);
|
|
16
|
-
return serialized;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function desugarWorkflow(authoring) {
|
|
20
|
-
const issues = [];
|
|
21
|
-
checkReservedRoleAliasKeys(authoring.roleAliases, issues);
|
|
22
|
-
const roleAliases = invariants.normalizeRoleAliases(authoring.roleAliases), ctx = {
|
|
23
|
-
issues: issues,
|
|
24
|
-
claimFields: /* @__PURE__ */ new Map,
|
|
25
|
-
claimedFields: /* @__PURE__ */ new Set,
|
|
26
|
-
roleAliases: roleAliases
|
|
27
|
-
}, workflowFields = desugarFieldEntries({
|
|
28
|
-
entries: authoring.fields,
|
|
29
|
-
path: [ "fields" ],
|
|
30
|
-
ctx: ctx
|
|
31
|
-
}), workflowLayer = layerOf(workflowFields), stages = authoring.stages.map((stage, i) => {
|
|
32
|
-
const path = [ "stages", i ], stageFields = desugarFieldEntries({
|
|
33
|
-
entries: stage.fields,
|
|
34
|
-
path: [ ...path, "fields" ],
|
|
35
|
-
ctx: ctx
|
|
36
|
-
}), stageEnv = {
|
|
37
|
-
layers: [ {
|
|
38
|
-
scope: "stage",
|
|
39
|
-
entries: layerOf(stageFields)
|
|
40
|
-
}, {
|
|
41
|
-
scope: "workflow",
|
|
42
|
-
entries: workflowLayer
|
|
43
|
-
} ]
|
|
44
|
-
}, activities = (stage.activities ?? []).map((activity, j) => desugarActivity({
|
|
45
|
-
activity: activity,
|
|
46
|
-
path: [ ...path, "activities", j ],
|
|
47
|
-
stageEnv: stageEnv,
|
|
48
|
-
ctx: ctx
|
|
49
|
-
})), transitions = (stage.transitions ?? []).map((transition, k) => desugarTransition({
|
|
50
|
-
transition: transition,
|
|
51
|
-
path: [ ...path, "transitions", k ],
|
|
52
|
-
stageEnv: stageEnv,
|
|
53
|
-
ctx: ctx
|
|
54
|
-
})), editable = desugarStageEditable({
|
|
55
|
-
overrides: stage.editable,
|
|
56
|
-
inScope: editableFieldNames({
|
|
57
|
-
workflowFields: workflowFields,
|
|
58
|
-
stageFields: stageFields,
|
|
59
|
-
activities: activities
|
|
60
|
-
}),
|
|
61
|
-
path: [ ...path, "editable" ],
|
|
62
|
-
ctx: ctx
|
|
63
|
-
});
|
|
64
|
-
return {
|
|
65
|
-
...stripUndefined({
|
|
66
|
-
name: stage.name,
|
|
67
|
-
title: stage.title,
|
|
68
|
-
description: stage.description,
|
|
69
|
-
groups: stage.groups,
|
|
70
|
-
guards: stage.guards?.map(desugarGuard)
|
|
71
|
-
}),
|
|
72
|
-
...stageFields ? {
|
|
73
|
-
fields: stageFields
|
|
74
|
-
} : {},
|
|
75
|
-
...activities.length > 0 ? {
|
|
76
|
-
activities: activities
|
|
77
|
-
} : {},
|
|
78
|
-
...transitions.length > 0 ? {
|
|
79
|
-
transitions: transitions
|
|
80
|
-
} : {},
|
|
81
|
-
...editable ? {
|
|
82
|
-
editable: editable
|
|
83
|
-
} : {}
|
|
84
|
-
};
|
|
85
|
-
});
|
|
86
|
-
return checkUnclaimedClaimFields(ctx), {
|
|
87
|
-
definition: {
|
|
88
|
-
...stripUndefined({
|
|
89
|
-
name: authoring.name,
|
|
90
|
-
title: authoring.title,
|
|
91
|
-
description: authoring.description,
|
|
92
|
-
groups: authoring.groups,
|
|
93
|
-
lifecycle: authoring.lifecycle,
|
|
94
|
-
applicableWhen: authoring.applicableWhen,
|
|
95
|
-
initialStage: authoring.initialStage,
|
|
96
|
-
predicates: authoring.predicates,
|
|
97
|
-
roleAliases: roleAliases
|
|
98
|
-
}),
|
|
99
|
-
...workflowFields ? {
|
|
100
|
-
fields: workflowFields
|
|
101
|
-
} : {},
|
|
102
|
-
stages: stages
|
|
103
|
-
},
|
|
104
|
-
issues: ctx.issues
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function checkReservedRoleAliasKeys(aliases, issues) {
|
|
109
|
-
for (const key of Object.keys(aliases ?? {})) key.startsWith("$") && issues.push({
|
|
110
|
-
path: [ "roleAliases", key ],
|
|
111
|
-
message: `role alias key "${key}" uses the reserved "$" prefix — that namespace is the engine's stored spelling for the universal fulfiller. Use "*" to mean "fulfills any gate", or rename the role.`
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const TODOLIST_OF = [ {
|
|
116
|
-
type: "string",
|
|
117
|
-
name: "label",
|
|
118
|
-
title: "Label"
|
|
119
|
-
}, {
|
|
120
|
-
type: "string",
|
|
121
|
-
name: "status",
|
|
122
|
-
title: "Status"
|
|
123
|
-
}, {
|
|
124
|
-
type: "assignee",
|
|
125
|
-
name: "assignee",
|
|
126
|
-
title: "Assignee"
|
|
127
|
-
}, {
|
|
128
|
-
type: "date",
|
|
129
|
-
name: "dueDate",
|
|
130
|
-
title: "Due date"
|
|
131
|
-
} ], NOTES_OF = [ {
|
|
132
|
-
type: "text",
|
|
133
|
-
name: "body",
|
|
134
|
-
title: "Body"
|
|
135
|
-
}, {
|
|
136
|
-
type: "actor",
|
|
137
|
-
name: "actor",
|
|
138
|
-
title: "Actor"
|
|
139
|
-
}, {
|
|
140
|
-
type: "datetime",
|
|
141
|
-
name: "at",
|
|
142
|
-
title: "At"
|
|
143
|
-
} ];
|
|
144
|
-
|
|
145
|
-
function desugarFieldEntries({entries: entries, path: path, ctx: ctx}) {
|
|
146
|
-
return !entries || entries.length === 0 ? entries === void 0 ? void 0 : [] : entries.map((entry, i) => desugarFieldEntry({
|
|
147
|
-
entry: entry,
|
|
148
|
-
path: [ ...path, i ],
|
|
149
|
-
ctx: ctx
|
|
150
|
-
}));
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function desugarFieldEntry({entry: entry, path: path, ctx: ctx}) {
|
|
154
|
-
if (entry.type === "claim") return desugarClaimField({
|
|
155
|
-
entry: entry,
|
|
156
|
-
path: path,
|
|
157
|
-
ctx: ctx
|
|
158
|
-
});
|
|
159
|
-
if (entry.type === "todoList" || entry.type === "notes") return desugarListField({
|
|
160
|
-
entry: entry,
|
|
161
|
-
path: path,
|
|
162
|
-
ctx: ctx
|
|
163
|
-
});
|
|
164
|
-
const editable = normalizeEditable({
|
|
165
|
-
editable: entry.editable,
|
|
166
|
-
path: [ ...path, "editable" ],
|
|
167
|
-
ctx: ctx
|
|
168
|
-
});
|
|
169
|
-
return {
|
|
170
|
-
...stripUndefined({
|
|
171
|
-
type: entry.type,
|
|
172
|
-
name: entry.name,
|
|
173
|
-
title: entry.title,
|
|
174
|
-
description: entry.description,
|
|
175
|
-
group: normalizeGroup(entry.group),
|
|
176
|
-
required: entry.required,
|
|
177
|
-
initialValue: entry.initialValue,
|
|
178
|
-
editable: editable,
|
|
179
|
-
types: entry.types,
|
|
180
|
-
fields: entry.fields,
|
|
181
|
-
of: entry.of
|
|
182
|
-
})
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
function desugarClaimField({entry: entry, path: path, ctx: ctx}) {
|
|
187
|
-
const desugared = {
|
|
188
|
-
...stripUndefined({
|
|
189
|
-
name: entry.name,
|
|
190
|
-
title: entry.title,
|
|
191
|
-
description: entry.description,
|
|
192
|
-
group: normalizeGroup(entry.group)
|
|
193
|
-
}),
|
|
194
|
-
type: "actor"
|
|
195
|
-
};
|
|
196
|
-
return ctx.claimFields.set(desugared, {
|
|
197
|
-
name: entry.name,
|
|
198
|
-
path: path
|
|
199
|
-
}), desugared;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
function desugarListField({entry: entry, path: path, ctx: ctx}) {
|
|
203
|
-
const editable = normalizeEditable({
|
|
204
|
-
editable: entry.editable,
|
|
205
|
-
path: [ ...path, "editable" ],
|
|
206
|
-
ctx: ctx
|
|
207
|
-
});
|
|
208
|
-
return {
|
|
209
|
-
...stripUndefined({
|
|
210
|
-
name: entry.name,
|
|
211
|
-
title: entry.title,
|
|
212
|
-
description: entry.description,
|
|
213
|
-
group: normalizeGroup(entry.group),
|
|
214
|
-
required: entry.required,
|
|
215
|
-
initialValue: entry.initialValue,
|
|
216
|
-
editable: editable
|
|
217
|
-
}),
|
|
218
|
-
type: "array",
|
|
219
|
-
of: entry.type === "todoList" ? TODOLIST_OF : NOTES_OF
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
function normalizeEditable({editable: editable, path: path, ctx: ctx}) {
|
|
224
|
-
if (editable === void 0 || editable === !0) return editable;
|
|
225
|
-
if (Array.isArray(editable)) {
|
|
226
|
-
const condition = rolesCondition(editable, ctx.roleAliases);
|
|
227
|
-
if (condition === void 0) {
|
|
228
|
-
ctx.issues.push({
|
|
229
|
-
path: path,
|
|
230
|
-
message: "editable: [] names no roles — use `true` to open the field to anyone in its scope, or list at least one role"
|
|
231
|
-
});
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
return condition;
|
|
235
|
-
}
|
|
236
|
-
return editable;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
function editableFieldNames({workflowFields: workflowFields, stageFields: stageFields, activities: activities}) {
|
|
240
|
-
const names = /* @__PURE__ */ new Set, collect = entries => {
|
|
241
|
-
for (const entry of entries ?? []) entry.editable !== void 0 && names.add(entry.name);
|
|
242
|
-
};
|
|
243
|
-
collect(workflowFields), collect(stageFields);
|
|
244
|
-
for (const activity of activities) collect(activity.fields);
|
|
245
|
-
return names;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
function desugarStageEditable({overrides: overrides, inScope: inScope, path: path, ctx: ctx}) {
|
|
249
|
-
if (overrides === void 0) return;
|
|
250
|
-
const out = {};
|
|
251
|
-
for (const [name, value] of Object.entries(overrides)) {
|
|
252
|
-
if (!inScope.has(name)) {
|
|
253
|
-
ctx.issues.push({
|
|
254
|
-
path: [ ...path, name ],
|
|
255
|
-
message: `stage editable override "${name}" does not narrow an editable field in scope — name a workflow/stage/activity field of this stage that declares \`editable\``
|
|
256
|
-
});
|
|
257
|
-
continue;
|
|
258
|
-
}
|
|
259
|
-
const normalized = normalizeEditable({
|
|
260
|
-
editable: value,
|
|
261
|
-
path: [ ...path, name ],
|
|
262
|
-
ctx: ctx
|
|
263
|
-
});
|
|
264
|
-
normalized !== void 0 && (out[name] = normalized);
|
|
265
|
-
}
|
|
266
|
-
return Object.keys(out).length > 0 ? out : void 0;
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
function layerOf(entries) {
|
|
270
|
-
return new Map((entries ?? []).map(entry => [ entry.name, entry ]));
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
function normalizeGroup(group) {
|
|
274
|
-
return group === void 0 || Array.isArray(group) ? group : [ group ];
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
function desugarActivity({activity: activity, path: path, stageEnv: stageEnv, ctx: ctx}) {
|
|
278
|
-
const activityFields = desugarFieldEntries({
|
|
279
|
-
entries: activity.fields,
|
|
280
|
-
path: [ ...path, "fields" ],
|
|
281
|
-
ctx: ctx
|
|
282
|
-
}), env = {
|
|
283
|
-
layers: [ {
|
|
284
|
-
scope: "activity",
|
|
285
|
-
entries: layerOf(activityFields)
|
|
286
|
-
}, ...stageEnv.layers ]
|
|
287
|
-
}, ops = desugarOps({
|
|
288
|
-
ops: activity.ops,
|
|
289
|
-
path: [ ...path, "ops" ],
|
|
290
|
-
env: env,
|
|
291
|
-
firingActivity: activity.name,
|
|
292
|
-
ctx: ctx
|
|
293
|
-
}), actions = (activity.actions ?? []).map((action, a) => desugarAction({
|
|
294
|
-
action: action,
|
|
295
|
-
path: [ ...path, "actions", a ],
|
|
296
|
-
env: env,
|
|
297
|
-
activityName: activity.name,
|
|
298
|
-
ctx: ctx
|
|
299
|
-
})), target = desugarTarget({
|
|
300
|
-
target: activity.target,
|
|
301
|
-
env: env,
|
|
302
|
-
path: [ ...path, "target" ],
|
|
303
|
-
ctx: ctx
|
|
304
|
-
});
|
|
305
|
-
return {
|
|
306
|
-
...stripUndefined({
|
|
307
|
-
name: activity.name,
|
|
308
|
-
title: activity.title,
|
|
309
|
-
description: activity.description,
|
|
310
|
-
groups: activity.groups,
|
|
311
|
-
group: normalizeGroup(activity.group),
|
|
312
|
-
kind: activity.kind,
|
|
313
|
-
filter: activity.filter,
|
|
314
|
-
requirements: activity.requirements,
|
|
315
|
-
completeWhen: activity.completeWhen,
|
|
316
|
-
failWhen: activity.failWhen,
|
|
317
|
-
effects: activity.effects,
|
|
318
|
-
subworkflows: activity.subworkflows
|
|
319
|
-
}),
|
|
320
|
-
activation: activity.activation ?? "manual",
|
|
321
|
-
...target ? {
|
|
322
|
-
target: target
|
|
323
|
-
} : {},
|
|
324
|
-
...activityFields ? {
|
|
325
|
-
fields: activityFields
|
|
326
|
-
} : {},
|
|
327
|
-
...ops ? {
|
|
328
|
-
ops: ops
|
|
329
|
-
} : {},
|
|
330
|
-
...actions.length > 0 ? {
|
|
331
|
-
actions: actions
|
|
332
|
-
} : {}
|
|
333
|
-
};
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
const TARGET_DOC_KINDS = [ "doc.ref", "doc.refs", "release.ref" ];
|
|
337
|
-
|
|
338
|
-
function desugarTarget({target: target, env: env, path: path, ctx: ctx}) {
|
|
339
|
-
if (target === void 0 || target.type === "url") return target;
|
|
340
|
-
const ref = typeof target.field == "string" ? {
|
|
341
|
-
field: target.field
|
|
342
|
-
} : target.field, field = resolveRef({
|
|
343
|
-
ref: ref,
|
|
344
|
-
env: env,
|
|
345
|
-
path: [ ...path, "field" ],
|
|
346
|
-
ctx: ctx
|
|
347
|
-
}) ?? fallbackRef(ref), entry = entryAt(env, field);
|
|
348
|
-
return entry && !TARGET_DOC_KINDS.includes(entry.type) && ctx.issues.push({
|
|
349
|
-
path: [ ...path, "field" ],
|
|
350
|
-
message: `manual activity target references "${field.field}" of kind "${entry.type}" — a deep-link target needs a document-valued entry (${TARGET_DOC_KINDS.join(", ")})`
|
|
351
|
-
}), {
|
|
352
|
-
type: "field",
|
|
353
|
-
field: field
|
|
354
|
-
};
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
function desugarAction({action: action, path: path, env: env, activityName: activityName, ctx: ctx}) {
|
|
358
|
-
if ("type" in action) return desugarClaimAction({
|
|
359
|
-
action: action,
|
|
360
|
-
path: path,
|
|
361
|
-
env: env,
|
|
362
|
-
ctx: ctx
|
|
363
|
-
});
|
|
364
|
-
const filter = invariants.andConditions([ rolesCondition(action.roles, ctx.roleAliases), action.filter ]), ops = desugarOps({
|
|
365
|
-
ops: action.ops,
|
|
366
|
-
path: [ ...path, "ops" ],
|
|
367
|
-
env: env,
|
|
368
|
-
firingActivity: activityName,
|
|
369
|
-
ctx: ctx
|
|
370
|
-
}) ?? [];
|
|
371
|
-
return action.status !== void 0 && ops.push({
|
|
372
|
-
type: "status.set",
|
|
373
|
-
activity: activityName,
|
|
374
|
-
status: action.status
|
|
375
|
-
}), {
|
|
376
|
-
...stripUndefined({
|
|
377
|
-
name: action.name,
|
|
378
|
-
title: action.title,
|
|
379
|
-
description: action.description,
|
|
380
|
-
group: normalizeGroup(action.group),
|
|
381
|
-
params: action.params,
|
|
382
|
-
effects: action.effects
|
|
383
|
-
}),
|
|
384
|
-
...filter ? {
|
|
385
|
-
filter: filter
|
|
386
|
-
} : {},
|
|
387
|
-
...ops.length > 0 ? {
|
|
388
|
-
ops: ops
|
|
389
|
-
} : {}
|
|
390
|
-
};
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
function desugarClaimAction({action: action, path: path, env: env, ctx: ctx}) {
|
|
394
|
-
const ref = typeof action.field == "string" ? {
|
|
395
|
-
field: action.field
|
|
396
|
-
} : action.field, resolved = resolveRef({
|
|
397
|
-
ref: ref,
|
|
398
|
-
env: env,
|
|
399
|
-
path: [ ...path, "field" ],
|
|
400
|
-
ctx: ctx
|
|
401
|
-
});
|
|
402
|
-
if (resolved) {
|
|
403
|
-
const entry = entryAt(env, resolved);
|
|
404
|
-
entry && entry.type !== "actor" && ctx.issues.push({
|
|
405
|
-
path: [ ...path, "field" ],
|
|
406
|
-
message: `claim action "${action.name}" references "${resolved.field}" of kind "${entry.type}" — a claim pair needs an actor-valued entry (a "claim" or "actor" field declaration)`
|
|
407
|
-
}), checkShadowedClaimTarget({
|
|
408
|
-
actionName: action.name,
|
|
409
|
-
field: ref.field,
|
|
410
|
-
resolved: resolved,
|
|
411
|
-
env: env,
|
|
412
|
-
path: [ ...path, "field" ],
|
|
413
|
-
ctx: ctx
|
|
414
|
-
}), entry && ctx.claimedFields.add(entry);
|
|
415
|
-
}
|
|
416
|
-
const noSteal = `!defined($fields.${ref.field})`, filter = invariants.andConditions([ noSteal, rolesCondition(action.roles, ctx.roleAliases), action.filter ]), ops = resolved ? [ {
|
|
417
|
-
type: "field.set",
|
|
418
|
-
target: resolved,
|
|
419
|
-
value: {
|
|
420
|
-
type: "actor"
|
|
421
|
-
}
|
|
422
|
-
} ] : [];
|
|
423
|
-
return {
|
|
424
|
-
...stripUndefined({
|
|
425
|
-
name: action.name,
|
|
426
|
-
title: action.title,
|
|
427
|
-
description: action.description,
|
|
428
|
-
group: normalizeGroup(action.group),
|
|
429
|
-
params: action.params,
|
|
430
|
-
effects: action.effects
|
|
431
|
-
}),
|
|
432
|
-
filter: filter,
|
|
433
|
-
...ops.length > 0 ? {
|
|
434
|
-
ops: ops
|
|
435
|
-
} : {}
|
|
436
|
-
};
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
function checkShadowedClaimTarget({actionName: actionName, field: field, resolved: resolved, env: env, path: path, ctx: ctx}) {
|
|
440
|
-
const nearest = env.layers.find(l => l.entries.has(field));
|
|
441
|
-
nearest === void 0 || nearest.scope === resolved.scope || ctx.issues.push({
|
|
442
|
-
path: path,
|
|
443
|
-
message: `claim action "${actionName}" targets "${field}" at scope "${resolved.scope}", but a nearer ${nearest.scope}-scope entry of the same name shadows it in $fields — the no-steal filter would read the shadowing entry while the claim writes the "${resolved.scope}" one. Rename one of the entries or claim the nearer one`
|
|
444
|
-
});
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
function checkUnclaimedClaimFields(ctx) {
|
|
448
|
-
for (const [entry, {name: name, path: path}] of ctx.claimFields) ctx.claimedFields.has(entry) || ctx.issues.push({
|
|
449
|
-
path: path,
|
|
450
|
-
message: `claim field "${name}" is never referenced by a claim action — you announced the pattern and wrote half of it. Declare a defineAction({ type: "claim", field: "${name}" }) or use a raw actor entry`
|
|
451
|
-
});
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
function desugarTransition({transition: transition, path: path, stageEnv: stageEnv, ctx: ctx}) {
|
|
455
|
-
const ops = desugarOps({
|
|
456
|
-
ops: transition.ops,
|
|
457
|
-
path: [ ...path, "ops" ],
|
|
458
|
-
env: stageEnv,
|
|
459
|
-
firingActivity: void 0,
|
|
460
|
-
ctx: ctx
|
|
461
|
-
});
|
|
462
|
-
return {
|
|
463
|
-
...stripUndefined({
|
|
464
|
-
name: transition.name,
|
|
465
|
-
title: transition.title,
|
|
466
|
-
description: transition.description,
|
|
467
|
-
to: transition.to,
|
|
468
|
-
effects: transition.effects
|
|
469
|
-
}),
|
|
470
|
-
filter: transition.filter ?? "$allActivitiesDone",
|
|
471
|
-
...ops ? {
|
|
472
|
-
ops: ops
|
|
473
|
-
} : {}
|
|
474
|
-
};
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
function desugarGuard(guard) {
|
|
478
|
-
const {match: match, metadata: metadata, ...rest} = guard, {idRefs: idRefs, ...matchRest} = match;
|
|
479
|
-
return {
|
|
480
|
-
...rest,
|
|
481
|
-
match: {
|
|
482
|
-
...matchRest,
|
|
483
|
-
...idRefs !== void 0 ? {
|
|
484
|
-
idRefs: idRefs.map(invariants.printGuardRead)
|
|
485
|
-
} : {}
|
|
486
|
-
},
|
|
487
|
-
...metadata !== void 0 ? {
|
|
488
|
-
metadata: Object.fromEntries(Object.entries(metadata).map(([key, read]) => [ key, invariants.printGuardRead(read) ]))
|
|
489
|
-
} : {}
|
|
490
|
-
};
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
function desugarOps({ops: ops, path: path, env: env, firingActivity: firingActivity, ctx: ctx}) {
|
|
494
|
-
if (ops) return ops.map((op, i) => desugarOp({
|
|
495
|
-
op: op,
|
|
496
|
-
path: [ ...path, i ],
|
|
497
|
-
env: env,
|
|
498
|
-
firingActivity: firingActivity,
|
|
499
|
-
ctx: ctx
|
|
500
|
-
}));
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
function desugarOp({op: op, path: path, env: env, firingActivity: firingActivity, ctx: ctx}) {
|
|
504
|
-
if (op.type === "status.set") {
|
|
505
|
-
const activity = op.activity ?? firingActivity;
|
|
506
|
-
return activity === void 0 && ctx.issues.push({
|
|
507
|
-
path: [ ...path, "activity" ],
|
|
508
|
-
message: "status.set outside an action must name its target activity"
|
|
509
|
-
}), {
|
|
510
|
-
type: "status.set",
|
|
511
|
-
activity: activity ?? "",
|
|
512
|
-
status: op.status
|
|
513
|
-
};
|
|
514
|
-
}
|
|
515
|
-
if (op.type === "audit") return desugarAuditOp({
|
|
516
|
-
op: op,
|
|
517
|
-
path: path,
|
|
518
|
-
env: env,
|
|
519
|
-
ctx: ctx
|
|
520
|
-
});
|
|
521
|
-
const target = resolveRef({
|
|
522
|
-
ref: op.target,
|
|
523
|
-
env: env,
|
|
524
|
-
path: [ ...path, "target" ],
|
|
525
|
-
ctx: ctx
|
|
526
|
-
}) ?? fallbackRef(op.target);
|
|
527
|
-
return {
|
|
528
|
-
...op,
|
|
529
|
-
target: target
|
|
530
|
-
};
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
const AUDIT_STAMPS = {
|
|
534
|
-
actor: {
|
|
535
|
-
type: "actor"
|
|
536
|
-
},
|
|
537
|
-
at: {
|
|
538
|
-
type: "now"
|
|
539
|
-
}
|
|
540
|
-
};
|
|
541
|
-
|
|
542
|
-
function desugarAuditOp({op: op, path: path, env: env, ctx: ctx}) {
|
|
543
|
-
const target = resolveRef({
|
|
544
|
-
ref: op.target,
|
|
545
|
-
env: env,
|
|
546
|
-
path: [ ...path, "target" ],
|
|
547
|
-
ctx: ctx
|
|
548
|
-
}) ?? fallbackRef(op.target);
|
|
549
|
-
if (op.value.type !== "object") return ctx.issues.push({
|
|
550
|
-
path: [ ...path, "value" ],
|
|
551
|
-
message: `audit value must be an object source carrying the domain fields (got "${op.value.type}")`
|
|
552
|
-
}), {
|
|
553
|
-
type: "field.append",
|
|
554
|
-
target: target,
|
|
555
|
-
value: op.value
|
|
556
|
-
};
|
|
557
|
-
const actorField = op.stampFields?.actor ?? "actor", atField = op.stampFields?.at ?? "at", fields = {
|
|
558
|
-
...op.value.fields
|
|
559
|
-
};
|
|
560
|
-
for (const [stamp, source] of [ [ actorField, AUDIT_STAMPS.actor ], [ atField, AUDIT_STAMPS.at ] ]) {
|
|
561
|
-
if (stamp in fields) {
|
|
562
|
-
ctx.issues.push({
|
|
563
|
-
path: [ ...path, "value", "fields", stamp ],
|
|
564
|
-
message: `audit stamp field "${stamp}" collides with an authored domain field — rename yours or remap the stamp via stampFields`
|
|
565
|
-
});
|
|
566
|
-
continue;
|
|
567
|
-
}
|
|
568
|
-
fields[stamp] = source;
|
|
569
|
-
}
|
|
570
|
-
return {
|
|
571
|
-
type: "field.append",
|
|
572
|
-
target: target,
|
|
573
|
-
value: {
|
|
574
|
-
type: "object",
|
|
575
|
-
fields: fields
|
|
576
|
-
}
|
|
577
|
-
};
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
function resolveRef({ref: ref, env: env, path: path, ctx: ctx}) {
|
|
581
|
-
const layers = ref.scope === void 0 ? env.layers : env.layers.filter(l => l.scope === ref.scope);
|
|
582
|
-
for (const layer of layers) if (layer.entries.has(ref.field)) return {
|
|
583
|
-
scope: layer.scope,
|
|
584
|
-
field: ref.field
|
|
585
|
-
};
|
|
586
|
-
const reachable = env.layers.flatMap(l => [ ...l.entries.keys() ].map(n => `${l.scope}:${n}`));
|
|
587
|
-
ctx.issues.push({
|
|
588
|
-
path: path,
|
|
589
|
-
message: `field reference "${ref.field}"${ref.scope ? ` (scope "${ref.scope}")` : ""} does not resolve to a declared entry. Reachable: ${reachable.join(", ") || "(none)"}`
|
|
590
|
-
});
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
function entryAt(env, ref) {
|
|
594
|
-
return env.layers.find(l => l.scope === ref.scope)?.entries.get(ref.field);
|
|
595
|
-
}
|
|
596
|
-
|
|
597
|
-
function fallbackRef(ref) {
|
|
598
|
-
return {
|
|
599
|
-
scope: ref.scope ?? "workflow",
|
|
600
|
-
field: ref.field
|
|
601
|
-
};
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
function rolesCondition(roles, aliases) {
|
|
605
|
-
if (!(!roles || roles.length === 0)) return groq`count($actor.roles[@ in ${invariants.expandRequiredRoles(roles, aliases)}]) > 0`;
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
function stripUndefined(obj) {
|
|
609
|
-
const out = {};
|
|
610
|
-
for (const [k, value] of Object.entries(obj)) value !== void 0 && (out[k] = value);
|
|
611
|
-
return out;
|
|
612
|
-
}
|
|
613
|
-
|
|
614
9
|
function defineWorkflow(definition) {
|
|
615
10
|
const label = invariants.labelFor("defineWorkflow", definition), parsed = invariants.parseOrThrow({
|
|
616
11
|
schema: invariants.AuthoringWorkflowSchema,
|
|
617
12
|
input: definition,
|
|
618
13
|
label: label
|
|
619
|
-
}), {definition: stored, issues: desugarIssues} = desugarWorkflow(parsed), issues = [ ...desugarIssues, ...invariants.checkWorkflowInvariants(stored) ];
|
|
14
|
+
}), {definition: stored, issues: desugarIssues} = invariants.desugarWorkflow(parsed), issues = [ ...desugarIssues, ...invariants.checkWorkflowInvariants(stored) ];
|
|
620
15
|
if (issues.length > 0) throw new Error(invariants.formatValidationError(label, issues));
|
|
621
16
|
return stored;
|
|
622
17
|
}
|
|
@@ -719,6 +114,8 @@ exports.GUARD_PREDICATE_VARS = invariants.GUARD_PREDICATE_VARS;
|
|
|
719
114
|
|
|
720
115
|
exports.RESERVED_CONDITION_VARS = invariants.RESERVED_CONDITION_VARS;
|
|
721
116
|
|
|
117
|
+
exports.groq = invariants.groq;
|
|
118
|
+
|
|
722
119
|
exports.defineAction = defineAction;
|
|
723
120
|
|
|
724
121
|
exports.defineActivity = defineActivity;
|
|
@@ -742,5 +139,3 @@ exports.defineTransition = defineTransition;
|
|
|
742
139
|
exports.defineWorkflow = defineWorkflow;
|
|
743
140
|
|
|
744
141
|
exports.defineWorkflowConfig = defineWorkflowConfig;
|
|
745
|
-
|
|
746
|
-
exports.groq = groq;
|