@sanity/workflow-engine 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -0
- package/dist/_chunks-cjs/schema.cjs +809 -119
- package/dist/_chunks-cjs/schema.cjs.map +1 -1
- package/dist/_chunks-es/schema.js +810 -120
- package/dist/_chunks-es/schema.js.map +1 -1
- package/dist/define.cjs +384 -140
- package/dist/define.cjs.map +1 -1
- package/dist/define.d.cts +1996 -11940
- package/dist/define.d.ts +1996 -11940
- package/dist/define.js +385 -140
- package/dist/define.js.map +1 -1
- package/dist/index.cjs +2724 -2064
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4759 -18067
- package/dist/index.d.ts +4759 -18067
- package/dist/index.js +2766 -2106
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/define.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import * as v from "valibot";
|
|
2
|
-
import { andConditions, expandRequiredRoles, normalizeRoleAliases, GROQ_IDENTIFIER, formatValidationError, issuesFromValibot, AuthoringWorkflowSchema, AuthoringStageSchema, AuthoringActivitySchema, AuthoringActionSchema, AuthoringTransitionSchema, AuthoringFieldEntrySchema, AuthoringOpSchema,
|
|
2
|
+
import { printGuardRead, andConditions, expandRequiredRoles, normalizeRoleAliases, RESERVED_CONDITION_VARS, GROQ_IDENTIFIER, CONDITION_VARS, formatValidationError, issuesFromValibot, AuthoringWorkflowSchema, WorkflowConfigSchema, AuthoringStageSchema, AuthoringActivitySchema, AuthoringActionSchema, AuthoringTransitionSchema, AuthoringFieldEntrySchema, AuthoringOpSchema, AuthoringGuardSchema, EffectSchema } from "./_chunks-es/schema.js";
|
|
3
|
+
import { FILTER_SCOPE_VARS, GUARD_PREDICATE_VARS } from "./_chunks-es/schema.js";
|
|
3
4
|
function groq(strings, ...values) {
|
|
4
5
|
return strings.reduce((out, part, i) => i === 0 ? part : `${out}${serializeGroqValue(values[i - 1])}${part}`, "");
|
|
5
6
|
}
|
|
@@ -19,28 +20,28 @@ function desugarWorkflow(authoring) {
|
|
|
19
20
|
claimFields: /* @__PURE__ */ new Map(),
|
|
20
21
|
claimedFields: /* @__PURE__ */ new Set(),
|
|
21
22
|
roleAliases
|
|
22
|
-
}, workflowFields = desugarFieldEntries(authoring.fields, ["fields"], ctx), workflowLayer = layerOf(workflowFields), stages = authoring.stages.map((stage, i) => {
|
|
23
|
-
const path = ["stages", i], stageFields = desugarFieldEntries(stage.fields, [...path, "fields"], ctx), stageEnv = {
|
|
23
|
+
}, workflowFields = desugarFieldEntries({ entries: authoring.fields, path: ["fields"], ctx }), workflowLayer = layerOf(workflowFields), stages = authoring.stages.map((stage, i) => {
|
|
24
|
+
const path = ["stages", i], stageFields = desugarFieldEntries({ entries: stage.fields, path: [...path, "fields"], ctx }), stageEnv = {
|
|
24
25
|
layers: [
|
|
25
26
|
{ scope: "stage", entries: layerOf(stageFields) },
|
|
26
27
|
{ scope: "workflow", entries: workflowLayer }
|
|
27
28
|
]
|
|
28
29
|
}, activities = (stage.activities ?? []).map(
|
|
29
|
-
(activity, j) => desugarActivity(activity, [...path, "activities", j], stageEnv, ctx)
|
|
30
|
+
(activity, j) => desugarActivity({ activity, path: [...path, "activities", j], stageEnv, ctx })
|
|
30
31
|
), transitions = (stage.transitions ?? []).map(
|
|
31
|
-
(transition, k) => desugarTransition(transition, [...path, "transitions", k], stageEnv, ctx)
|
|
32
|
-
), editable = desugarStageEditable(
|
|
33
|
-
stage.editable,
|
|
34
|
-
|
|
35
|
-
[...path, "editable"],
|
|
32
|
+
(transition, k) => desugarTransition({ transition, path: [...path, "transitions", k], stageEnv, ctx })
|
|
33
|
+
), editable = desugarStageEditable({
|
|
34
|
+
overrides: stage.editable,
|
|
35
|
+
inScope: editableFieldNames({ workflowFields, stageFields, activities }),
|
|
36
|
+
path: [...path, "editable"],
|
|
36
37
|
ctx
|
|
37
|
-
);
|
|
38
|
+
});
|
|
38
39
|
return {
|
|
39
40
|
...stripUndefined({
|
|
40
41
|
name: stage.name,
|
|
41
42
|
title: stage.title,
|
|
42
43
|
description: stage.description,
|
|
43
|
-
guards: stage.guards
|
|
44
|
+
guards: stage.guards?.map(desugarGuard)
|
|
44
45
|
}),
|
|
45
46
|
...stageFields ? { fields: stageFields } : {},
|
|
46
47
|
...activities.length > 0 ? { activities } : {},
|
|
@@ -53,7 +54,7 @@ function desugarWorkflow(authoring) {
|
|
|
53
54
|
name: authoring.name,
|
|
54
55
|
title: authoring.title,
|
|
55
56
|
description: authoring.description,
|
|
56
|
-
|
|
57
|
+
lifecycle: authoring.lifecycle,
|
|
57
58
|
initialStage: authoring.initialStage,
|
|
58
59
|
predicates: authoring.predicates,
|
|
59
60
|
roleAliases
|
|
@@ -79,13 +80,22 @@ const TODOLIST_OF = [
|
|
|
79
80
|
{ type: "actor", name: "actor", title: "Actor" },
|
|
80
81
|
{ type: "datetime", name: "at", title: "At" }
|
|
81
82
|
];
|
|
82
|
-
function desugarFieldEntries(
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
83
|
+
function desugarFieldEntries({
|
|
84
|
+
entries,
|
|
85
|
+
path,
|
|
86
|
+
ctx
|
|
87
|
+
}) {
|
|
88
|
+
return !entries || entries.length === 0 ? entries === void 0 ? void 0 : [] : entries.map((entry, i) => desugarFieldEntry({ entry, path: [...path, i], ctx }));
|
|
89
|
+
}
|
|
90
|
+
function desugarFieldEntry({
|
|
91
|
+
entry,
|
|
92
|
+
path,
|
|
93
|
+
ctx
|
|
94
|
+
}) {
|
|
95
|
+
if (entry.type === "claim") return desugarClaimField({ entry, path, ctx });
|
|
96
|
+
if (entry.type === "todoList" || entry.type === "notes")
|
|
97
|
+
return desugarListField({ entry, path, ctx });
|
|
98
|
+
const editable = normalizeEditable({ editable: entry.editable, path: [...path, "editable"], ctx });
|
|
89
99
|
return {
|
|
90
100
|
...stripUndefined({
|
|
91
101
|
type: entry.type,
|
|
@@ -100,15 +110,23 @@ function desugarFieldEntry(entry, path, ctx) {
|
|
|
100
110
|
})
|
|
101
111
|
};
|
|
102
112
|
}
|
|
103
|
-
function desugarClaimField(
|
|
113
|
+
function desugarClaimField({
|
|
114
|
+
entry,
|
|
115
|
+
path,
|
|
116
|
+
ctx
|
|
117
|
+
}) {
|
|
104
118
|
const desugared = {
|
|
105
119
|
...stripUndefined({ name: entry.name, title: entry.title, description: entry.description }),
|
|
106
120
|
type: "actor"
|
|
107
121
|
};
|
|
108
122
|
return ctx.claimFields.set(desugared, { name: entry.name, path }), desugared;
|
|
109
123
|
}
|
|
110
|
-
function desugarListField(
|
|
111
|
-
|
|
124
|
+
function desugarListField({
|
|
125
|
+
entry,
|
|
126
|
+
path,
|
|
127
|
+
ctx
|
|
128
|
+
}) {
|
|
129
|
+
const editable = normalizeEditable({ editable: entry.editable, path: [...path, "editable"], ctx });
|
|
112
130
|
return {
|
|
113
131
|
...stripUndefined({
|
|
114
132
|
name: entry.name,
|
|
@@ -122,14 +140,18 @@ function desugarListField(entry, path, ctx) {
|
|
|
122
140
|
of: entry.type === "todoList" ? TODOLIST_OF : NOTES_OF
|
|
123
141
|
};
|
|
124
142
|
}
|
|
125
|
-
function normalizeEditable(
|
|
143
|
+
function normalizeEditable({
|
|
144
|
+
editable,
|
|
145
|
+
path,
|
|
146
|
+
ctx
|
|
147
|
+
}) {
|
|
126
148
|
if (editable === void 0 || editable === !0) return editable;
|
|
127
149
|
if (Array.isArray(editable)) {
|
|
128
150
|
const condition = rolesCondition(editable, ctx.roleAliases);
|
|
129
151
|
if (condition === void 0) {
|
|
130
152
|
ctx.issues.push({
|
|
131
153
|
path,
|
|
132
|
-
message: "editable: [] names no roles \u2014 use `true` to open the
|
|
154
|
+
message: "editable: [] names no roles \u2014 use `true` to open the field to anyone in its scope, or list at least one role"
|
|
133
155
|
});
|
|
134
156
|
return;
|
|
135
157
|
}
|
|
@@ -137,7 +159,11 @@ function normalizeEditable(editable, path, ctx) {
|
|
|
137
159
|
}
|
|
138
160
|
return editable;
|
|
139
161
|
}
|
|
140
|
-
function
|
|
162
|
+
function editableFieldNames({
|
|
163
|
+
workflowFields,
|
|
164
|
+
stageFields,
|
|
165
|
+
activities
|
|
166
|
+
}) {
|
|
141
167
|
const names = /* @__PURE__ */ new Set(), collect = (entries) => {
|
|
142
168
|
for (const entry of entries ?? []) entry.editable !== void 0 && names.add(entry.name);
|
|
143
169
|
};
|
|
@@ -145,18 +171,23 @@ function editableSlotNames(workflowFields, stageFields, activities) {
|
|
|
145
171
|
for (const activity of activities) collect(activity.fields);
|
|
146
172
|
return names;
|
|
147
173
|
}
|
|
148
|
-
function desugarStageEditable(
|
|
174
|
+
function desugarStageEditable({
|
|
175
|
+
overrides,
|
|
176
|
+
inScope,
|
|
177
|
+
path,
|
|
178
|
+
ctx
|
|
179
|
+
}) {
|
|
149
180
|
if (overrides === void 0) return;
|
|
150
181
|
const out = {};
|
|
151
182
|
for (const [name, value] of Object.entries(overrides)) {
|
|
152
183
|
if (!inScope.has(name)) {
|
|
153
184
|
ctx.issues.push({
|
|
154
185
|
path: [...path, name],
|
|
155
|
-
message: `stage editable override "${name}" does not narrow an editable
|
|
186
|
+
message: `stage editable override "${name}" does not narrow an editable field in scope \u2014 name a workflow/stage/activity field of this stage that declares \`editable\``
|
|
156
187
|
});
|
|
157
188
|
continue;
|
|
158
189
|
}
|
|
159
|
-
const normalized = normalizeEditable(value, [...path, name], ctx);
|
|
190
|
+
const normalized = normalizeEditable({ editable: value, path: [...path, name], ctx });
|
|
160
191
|
normalized !== void 0 && (out[name] = normalized);
|
|
161
192
|
}
|
|
162
193
|
return Object.keys(out).length > 0 ? out : void 0;
|
|
@@ -164,12 +195,27 @@ function desugarStageEditable(overrides, inScope, path, ctx) {
|
|
|
164
195
|
function layerOf(entries) {
|
|
165
196
|
return new Map((entries ?? []).map((entry) => [entry.name, entry]));
|
|
166
197
|
}
|
|
167
|
-
function desugarActivity(
|
|
168
|
-
|
|
198
|
+
function desugarActivity({
|
|
199
|
+
activity,
|
|
200
|
+
path,
|
|
201
|
+
stageEnv,
|
|
202
|
+
ctx
|
|
203
|
+
}) {
|
|
204
|
+
const activityFields = desugarFieldEntries({
|
|
205
|
+
entries: activity.fields,
|
|
206
|
+
path: [...path, "fields"],
|
|
207
|
+
ctx
|
|
208
|
+
}), env = {
|
|
169
209
|
layers: [{ scope: "activity", entries: layerOf(activityFields) }, ...stageEnv.layers]
|
|
170
|
-
}, ops = desugarOps(
|
|
171
|
-
|
|
172
|
-
|
|
210
|
+
}, ops = desugarOps({
|
|
211
|
+
ops: activity.ops,
|
|
212
|
+
path: [...path, "ops"],
|
|
213
|
+
env,
|
|
214
|
+
firingActivity: activity.name,
|
|
215
|
+
ctx
|
|
216
|
+
}), actions = (activity.actions ?? []).map(
|
|
217
|
+
(action, a) => desugarAction({ action, path: [...path, "actions", a], env, activityName: activity.name, ctx })
|
|
218
|
+
), target = desugarTarget({ target: activity.target, env, path: [...path, "target"], ctx });
|
|
173
219
|
return {
|
|
174
220
|
...stripUndefined({
|
|
175
221
|
name: activity.name,
|
|
@@ -191,18 +237,29 @@ function desugarActivity(activity, path, stageEnv, ctx) {
|
|
|
191
237
|
};
|
|
192
238
|
}
|
|
193
239
|
const TARGET_DOC_KINDS = ["doc.ref", "doc.refs", "release.ref"];
|
|
194
|
-
function desugarTarget(
|
|
240
|
+
function desugarTarget({
|
|
241
|
+
target,
|
|
242
|
+
env,
|
|
243
|
+
path,
|
|
244
|
+
ctx
|
|
245
|
+
}) {
|
|
195
246
|
if (target === void 0 || target.type === "url") return target;
|
|
196
|
-
const ref = typeof target.field == "string" ? { field: target.field } : target.field, field = resolveRef(ref, env, [...path, "field"], ctx) ?? fallbackRef(ref), entry = entryAt(env, field);
|
|
247
|
+
const ref = typeof target.field == "string" ? { field: target.field } : target.field, field = resolveRef({ ref, env, path: [...path, "field"], ctx }) ?? fallbackRef(ref), entry = entryAt(env, field);
|
|
197
248
|
return entry && !TARGET_DOC_KINDS.includes(entry.type) && ctx.issues.push({
|
|
198
249
|
path: [...path, "field"],
|
|
199
250
|
message: `manual activity target references "${field.field}" of kind "${entry.type}" \u2014 a deep-link target needs a document-valued entry (${TARGET_DOC_KINDS.join(", ")})`
|
|
200
251
|
}), { type: "field", field };
|
|
201
252
|
}
|
|
202
|
-
function desugarAction(
|
|
253
|
+
function desugarAction({
|
|
254
|
+
action,
|
|
255
|
+
path,
|
|
256
|
+
env,
|
|
257
|
+
activityName,
|
|
258
|
+
ctx
|
|
259
|
+
}) {
|
|
203
260
|
if ("type" in action)
|
|
204
|
-
return desugarClaimAction(action, path, env, ctx);
|
|
205
|
-
const filter = andConditions([rolesCondition(action.roles, ctx.roleAliases), action.filter]), ops = desugarOps(action.ops, [...path, "ops"], env, activityName, ctx) ?? [];
|
|
261
|
+
return desugarClaimAction({ action, path, env, ctx });
|
|
262
|
+
const filter = andConditions([rolesCondition(action.roles, ctx.roleAliases), action.filter]), ops = desugarOps({ ops: action.ops, path: [...path, "ops"], env, firingActivity: activityName, ctx }) ?? [];
|
|
206
263
|
return action.status !== void 0 && ops.push({ type: "status.set", activity: activityName, status: action.status }), {
|
|
207
264
|
...stripUndefined({
|
|
208
265
|
name: action.name,
|
|
@@ -215,14 +272,26 @@ function desugarAction(action, path, env, activityName, ctx) {
|
|
|
215
272
|
...ops.length > 0 ? { ops } : {}
|
|
216
273
|
};
|
|
217
274
|
}
|
|
218
|
-
function desugarClaimAction(
|
|
219
|
-
|
|
275
|
+
function desugarClaimAction({
|
|
276
|
+
action,
|
|
277
|
+
path,
|
|
278
|
+
env,
|
|
279
|
+
ctx
|
|
280
|
+
}) {
|
|
281
|
+
const ref = typeof action.field == "string" ? { field: action.field } : action.field, resolved = resolveRef({ ref, env, path: [...path, "field"], ctx });
|
|
220
282
|
if (resolved) {
|
|
221
283
|
const entry = entryAt(env, resolved);
|
|
222
284
|
entry && entry.type !== "actor" && ctx.issues.push({
|
|
223
285
|
path: [...path, "field"],
|
|
224
286
|
message: `claim action "${action.name}" references "${resolved.field}" of kind "${entry.type}" \u2014 a claim pair needs an actor-valued entry (a "claim" or "actor" field declaration)`
|
|
225
|
-
}), checkShadowedClaimTarget(
|
|
287
|
+
}), checkShadowedClaimTarget({
|
|
288
|
+
actionName: action.name,
|
|
289
|
+
field: ref.field,
|
|
290
|
+
resolved,
|
|
291
|
+
env,
|
|
292
|
+
path: [...path, "field"],
|
|
293
|
+
ctx
|
|
294
|
+
}), entry && ctx.claimedFields.add(entry);
|
|
226
295
|
}
|
|
227
296
|
const noSteal = `!defined($fields.${ref.field})`, filter = andConditions([
|
|
228
297
|
noSteal,
|
|
@@ -241,7 +310,14 @@ function desugarClaimAction(action, path, env, ctx) {
|
|
|
241
310
|
...ops.length > 0 ? { ops } : {}
|
|
242
311
|
};
|
|
243
312
|
}
|
|
244
|
-
function checkShadowedClaimTarget(
|
|
313
|
+
function checkShadowedClaimTarget({
|
|
314
|
+
actionName,
|
|
315
|
+
field,
|
|
316
|
+
resolved,
|
|
317
|
+
env,
|
|
318
|
+
path,
|
|
319
|
+
ctx
|
|
320
|
+
}) {
|
|
245
321
|
const nearest = env.layers.find((l) => l.entries.has(field));
|
|
246
322
|
nearest === void 0 || nearest.scope === resolved.scope || ctx.issues.push({
|
|
247
323
|
path,
|
|
@@ -255,8 +331,19 @@ function checkUnclaimedClaimFields(ctx) {
|
|
|
255
331
|
message: `claim field "${name}" is never referenced by a claim action \u2014 you announced the pattern and wrote half of it. Declare a defineAction({ type: "claim", field: "${name}" }) or use a raw actor entry`
|
|
256
332
|
});
|
|
257
333
|
}
|
|
258
|
-
function desugarTransition(
|
|
259
|
-
|
|
334
|
+
function desugarTransition({
|
|
335
|
+
transition,
|
|
336
|
+
path,
|
|
337
|
+
stageEnv,
|
|
338
|
+
ctx
|
|
339
|
+
}) {
|
|
340
|
+
const ops = desugarOps({
|
|
341
|
+
ops: transition.ops,
|
|
342
|
+
path: [...path, "ops"],
|
|
343
|
+
env: stageEnv,
|
|
344
|
+
firingActivity: void 0,
|
|
345
|
+
ctx
|
|
346
|
+
});
|
|
260
347
|
return {
|
|
261
348
|
...stripUndefined({
|
|
262
349
|
name: transition.name,
|
|
@@ -269,11 +356,38 @@ function desugarTransition(transition, path, stageEnv, ctx) {
|
|
|
269
356
|
...ops ? { ops } : {}
|
|
270
357
|
};
|
|
271
358
|
}
|
|
272
|
-
function
|
|
273
|
-
|
|
274
|
-
|
|
359
|
+
function desugarGuard(guard) {
|
|
360
|
+
const { match, metadata, ...rest } = guard, { idRefs, ...matchRest } = match;
|
|
361
|
+
return {
|
|
362
|
+
...rest,
|
|
363
|
+
match: {
|
|
364
|
+
...matchRest,
|
|
365
|
+
...idRefs !== void 0 ? { idRefs: idRefs.map(printGuardRead) } : {}
|
|
366
|
+
},
|
|
367
|
+
...metadata !== void 0 ? {
|
|
368
|
+
metadata: Object.fromEntries(
|
|
369
|
+
Object.entries(metadata).map(([key, read]) => [key, printGuardRead(read)])
|
|
370
|
+
)
|
|
371
|
+
} : {}
|
|
372
|
+
};
|
|
275
373
|
}
|
|
276
|
-
function
|
|
374
|
+
function desugarOps({
|
|
375
|
+
ops,
|
|
376
|
+
path,
|
|
377
|
+
env,
|
|
378
|
+
firingActivity,
|
|
379
|
+
ctx
|
|
380
|
+
}) {
|
|
381
|
+
if (ops)
|
|
382
|
+
return ops.map((op, i) => desugarOp({ op, path: [...path, i], env, firingActivity, ctx }));
|
|
383
|
+
}
|
|
384
|
+
function desugarOp({
|
|
385
|
+
op,
|
|
386
|
+
path,
|
|
387
|
+
env,
|
|
388
|
+
firingActivity,
|
|
389
|
+
ctx
|
|
390
|
+
}) {
|
|
277
391
|
if (op.type === "status.set") {
|
|
278
392
|
const activity = op.activity ?? firingActivity;
|
|
279
393
|
return activity === void 0 && ctx.issues.push({
|
|
@@ -281,13 +395,18 @@ function desugarOp(op, path, env, firingActivity, ctx) {
|
|
|
281
395
|
message: "status.set outside an action must name its target activity"
|
|
282
396
|
}), { type: "status.set", activity: activity ?? "", status: op.status };
|
|
283
397
|
}
|
|
284
|
-
if (op.type === "audit") return desugarAuditOp(op, path, env, ctx);
|
|
285
|
-
const target = resolveRef(op.target, env, [...path, "target"], ctx) ?? fallbackRef(op.target);
|
|
398
|
+
if (op.type === "audit") return desugarAuditOp({ op, path, env, ctx });
|
|
399
|
+
const target = resolveRef({ ref: op.target, env, path: [...path, "target"], ctx }) ?? fallbackRef(op.target);
|
|
286
400
|
return { ...op, target };
|
|
287
401
|
}
|
|
288
402
|
const AUDIT_STAMPS = { actor: { type: "actor" }, at: { type: "now" } };
|
|
289
|
-
function desugarAuditOp(
|
|
290
|
-
|
|
403
|
+
function desugarAuditOp({
|
|
404
|
+
op,
|
|
405
|
+
path,
|
|
406
|
+
env,
|
|
407
|
+
ctx
|
|
408
|
+
}) {
|
|
409
|
+
const target = resolveRef({ ref: op.target, env, path: [...path, "target"], ctx }) ?? fallbackRef(op.target);
|
|
291
410
|
if (op.value.type !== "object")
|
|
292
411
|
return ctx.issues.push({
|
|
293
412
|
path: [...path, "value"],
|
|
@@ -309,7 +428,12 @@ function desugarAuditOp(op, path, env, ctx) {
|
|
|
309
428
|
}
|
|
310
429
|
return { type: "field.append", target, value: { type: "object", fields } };
|
|
311
430
|
}
|
|
312
|
-
function resolveRef(
|
|
431
|
+
function resolveRef({
|
|
432
|
+
ref,
|
|
433
|
+
env,
|
|
434
|
+
path,
|
|
435
|
+
ctx
|
|
436
|
+
}) {
|
|
313
437
|
const layers = ref.scope === void 0 ? env.layers : env.layers.filter((l) => l.scope === ref.scope);
|
|
314
438
|
for (const layer of layers)
|
|
315
439
|
if (layer.entries.has(ref.field)) return { scope: layer.scope, field: ref.field };
|
|
@@ -335,91 +459,109 @@ function stripUndefined(obj) {
|
|
|
335
459
|
value !== void 0 && (out[k] = value);
|
|
336
460
|
return out;
|
|
337
461
|
}
|
|
338
|
-
const
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
"assigned",
|
|
342
|
-
"can",
|
|
343
|
-
"row",
|
|
344
|
-
"now",
|
|
345
|
-
"effects",
|
|
346
|
-
"activities",
|
|
347
|
-
"subworkflows",
|
|
348
|
-
"self",
|
|
349
|
-
"stage",
|
|
350
|
-
"parent",
|
|
351
|
-
"ancestors",
|
|
352
|
-
"allActivitiesDone",
|
|
353
|
-
"anyActivityFailed"
|
|
354
|
-
], PREDICATE_CALLER_VARS = ["actor", "assigned", "can"];
|
|
462
|
+
const PREDICATE_CALLER_VARS = CONDITION_VARS.filter((v2) => v2.binding === "caller").map(
|
|
463
|
+
(v2) => v2.name
|
|
464
|
+
);
|
|
355
465
|
function knownList(ids) {
|
|
356
466
|
return [...ids].map((s) => `"${s}"`).join(", ");
|
|
357
467
|
}
|
|
358
|
-
function checkDuplicates(
|
|
468
|
+
function checkDuplicates({
|
|
469
|
+
names,
|
|
470
|
+
what,
|
|
471
|
+
issues
|
|
472
|
+
}) {
|
|
359
473
|
const seen = /* @__PURE__ */ new Set();
|
|
360
474
|
for (const { name, path } of names)
|
|
361
475
|
seen.has(name) && issues.push({ path, message: `duplicate ${what} "${name}" (already declared earlier)` }), seen.add(name);
|
|
362
476
|
return seen;
|
|
363
477
|
}
|
|
364
478
|
function checkStages(def, issues) {
|
|
365
|
-
const stageNames = checkDuplicates(
|
|
366
|
-
def.stages.map((stage, i) => ({ name: stage.name, path: ["stages", i, "name"] })),
|
|
367
|
-
"stage name",
|
|
479
|
+
const stageNames = checkDuplicates({
|
|
480
|
+
names: def.stages.map((stage, i) => ({ name: stage.name, path: ["stages", i, "name"] })),
|
|
481
|
+
what: "stage name",
|
|
368
482
|
issues
|
|
369
|
-
);
|
|
483
|
+
});
|
|
370
484
|
for (const [i, stage] of def.stages.entries()) {
|
|
371
|
-
const activityNames = checkDuplicates(
|
|
372
|
-
(stage.activities ?? []).map((activity, j) => ({
|
|
485
|
+
const activityNames = checkDuplicates({
|
|
486
|
+
names: (stage.activities ?? []).map((activity, j) => ({
|
|
373
487
|
name: activity.name,
|
|
374
488
|
path: ["stages", i, "activities", j, "name"]
|
|
375
489
|
})),
|
|
376
|
-
`activity name in stage "${stage.name}"`,
|
|
490
|
+
what: `activity name in stage "${stage.name}"`,
|
|
377
491
|
issues
|
|
378
|
-
);
|
|
379
|
-
checkDuplicates(
|
|
380
|
-
(stage.transitions ?? []).map((t, k) => ({
|
|
492
|
+
});
|
|
493
|
+
checkDuplicates({
|
|
494
|
+
names: (stage.transitions ?? []).map((t, k) => ({
|
|
381
495
|
name: t.name,
|
|
382
496
|
path: ["stages", i, "transitions", k, "name"]
|
|
383
497
|
})),
|
|
384
|
-
`transition name in stage "${stage.name}"`,
|
|
498
|
+
what: `transition name in stage "${stage.name}"`,
|
|
385
499
|
issues
|
|
386
|
-
), checkActivities(def, i, activityNames, issues);
|
|
500
|
+
}), checkActivities({ def, i, activityNames, issues });
|
|
387
501
|
}
|
|
388
502
|
return stageNames;
|
|
389
503
|
}
|
|
390
|
-
function checkActivities(
|
|
504
|
+
function checkActivities({
|
|
505
|
+
def,
|
|
506
|
+
i,
|
|
507
|
+
activityNames,
|
|
508
|
+
issues
|
|
509
|
+
}) {
|
|
391
510
|
const stage = def.stages[i];
|
|
392
511
|
for (const [j, activity] of (stage.activities ?? []).entries()) {
|
|
393
512
|
const path = ["stages", i, "activities", j];
|
|
394
|
-
checkDuplicates(
|
|
395
|
-
(activity.actions ?? []).map((action, a) => ({
|
|
513
|
+
checkDuplicates({
|
|
514
|
+
names: (activity.actions ?? []).map((action, a) => ({
|
|
396
515
|
name: action.name,
|
|
397
516
|
path: [...path, "actions", a, "name"]
|
|
398
517
|
})),
|
|
399
|
-
`action name in activity "${activity.name}"`,
|
|
518
|
+
what: `action name in activity "${activity.name}"`,
|
|
400
519
|
issues
|
|
401
|
-
), checkStatusSetTargets(activity, activityNames, path, issues);
|
|
520
|
+
}), checkStatusSetTargets({ activity, activityNames, path, issues });
|
|
402
521
|
}
|
|
403
522
|
}
|
|
404
|
-
function checkStatusSetTargets(
|
|
405
|
-
|
|
523
|
+
function checkStatusSetTargets({
|
|
524
|
+
activity,
|
|
525
|
+
activityNames,
|
|
526
|
+
path,
|
|
527
|
+
issues
|
|
528
|
+
}) {
|
|
529
|
+
checkStatusSetOps({ ops: activity.ops, activityNames, path: [...path, "ops"], issues });
|
|
406
530
|
for (const [a, action] of (activity.actions ?? []).entries())
|
|
407
|
-
checkStatusSetOps(
|
|
531
|
+
checkStatusSetOps({
|
|
532
|
+
ops: action.ops,
|
|
533
|
+
activityNames,
|
|
534
|
+
path: [...path, "actions", a, "ops"],
|
|
535
|
+
issues
|
|
536
|
+
});
|
|
408
537
|
}
|
|
409
|
-
function checkStatusSetOps(
|
|
538
|
+
function checkStatusSetOps({
|
|
539
|
+
ops,
|
|
540
|
+
activityNames,
|
|
541
|
+
path,
|
|
542
|
+
issues
|
|
543
|
+
}) {
|
|
410
544
|
for (const [o, op] of (ops ?? []).entries())
|
|
411
545
|
op.type !== "status.set" || activityNames.has(op.activity) || issues.push({
|
|
412
546
|
path: [...path, o, "activity"],
|
|
413
547
|
message: `status.set targets activity "${op.activity}" which is not declared in this stage. Known activities: ${knownList(activityNames)}`
|
|
414
548
|
});
|
|
415
549
|
}
|
|
416
|
-
function checkInitialStage(
|
|
550
|
+
function checkInitialStage({
|
|
551
|
+
def,
|
|
552
|
+
stageNames,
|
|
553
|
+
issues
|
|
554
|
+
}) {
|
|
417
555
|
stageNames.has(def.initialStage) || issues.push({
|
|
418
556
|
path: ["initialStage"],
|
|
419
557
|
message: `initialStage "${def.initialStage}" is not a declared stage. Known stages: ${knownList(stageNames)}`
|
|
420
558
|
});
|
|
421
559
|
}
|
|
422
|
-
function checkTransitionTargets(
|
|
560
|
+
function checkTransitionTargets({
|
|
561
|
+
def,
|
|
562
|
+
stageNames,
|
|
563
|
+
issues
|
|
564
|
+
}) {
|
|
423
565
|
for (const [i, stage] of def.stages.entries())
|
|
424
566
|
for (const [k, t] of (stage.transitions ?? []).entries())
|
|
425
567
|
stageNames.has(t.to) || issues.push({
|
|
@@ -431,20 +573,32 @@ function checkEffectNames(def, issues) {
|
|
|
431
573
|
const sites = [];
|
|
432
574
|
for (const [i, stage] of def.stages.entries()) {
|
|
433
575
|
for (const [j, activity] of (stage.activities ?? []).entries()) {
|
|
434
|
-
collectEffects(
|
|
576
|
+
collectEffects({
|
|
577
|
+
effects: activity.effects,
|
|
578
|
+
path: ["stages", i, "activities", j, "effects"],
|
|
579
|
+
sites
|
|
580
|
+
});
|
|
435
581
|
for (const [a, action] of (activity.actions ?? []).entries())
|
|
436
|
-
collectEffects(
|
|
437
|
-
action.effects,
|
|
438
|
-
["stages", i, "activities", j, "actions", a, "effects"],
|
|
582
|
+
collectEffects({
|
|
583
|
+
effects: action.effects,
|
|
584
|
+
path: ["stages", i, "activities", j, "actions", a, "effects"],
|
|
439
585
|
sites
|
|
440
|
-
);
|
|
586
|
+
});
|
|
441
587
|
}
|
|
442
588
|
for (const [k, t] of (stage.transitions ?? []).entries())
|
|
443
|
-
collectEffects(t.effects, ["stages", i, "transitions", k, "effects"], sites);
|
|
589
|
+
collectEffects({ effects: t.effects, path: ["stages", i, "transitions", k, "effects"], sites });
|
|
444
590
|
}
|
|
445
|
-
checkDuplicates(
|
|
591
|
+
checkDuplicates({
|
|
592
|
+
names: sites,
|
|
593
|
+
what: "effect name (registry key \u2014 unique per definition)",
|
|
594
|
+
issues
|
|
595
|
+
});
|
|
446
596
|
}
|
|
447
|
-
function collectEffects(
|
|
597
|
+
function collectEffects({
|
|
598
|
+
effects,
|
|
599
|
+
path,
|
|
600
|
+
sites
|
|
601
|
+
}) {
|
|
448
602
|
for (const [e, effect] of (effects ?? []).entries())
|
|
449
603
|
sites.push({ name: effect.name, path: [...path, e, "name"] });
|
|
450
604
|
}
|
|
@@ -455,11 +609,11 @@ function checkGuardNames(def, issues) {
|
|
|
455
609
|
path: ["stages", i, "guards", g, "name"]
|
|
456
610
|
}))
|
|
457
611
|
);
|
|
458
|
-
checkDuplicates(
|
|
459
|
-
sites,
|
|
460
|
-
"guard name (the guard lake _id derives from it \u2014 unique per definition)",
|
|
612
|
+
checkDuplicates({
|
|
613
|
+
names: sites,
|
|
614
|
+
what: "guard name (the guard lake _id derives from it \u2014 unique per definition)",
|
|
461
615
|
issues
|
|
462
|
-
);
|
|
616
|
+
});
|
|
463
617
|
}
|
|
464
618
|
function fieldScopes(def) {
|
|
465
619
|
const scopes = [
|
|
@@ -502,11 +656,11 @@ function checkRequiredField(def, issues) {
|
|
|
502
656
|
}
|
|
503
657
|
function checkFieldEntryNames(def, issues) {
|
|
504
658
|
for (const { entries, path, label } of fieldScopes(def))
|
|
505
|
-
checkDuplicates(
|
|
506
|
-
(entries ?? []).map((entry, n) => ({ name: entry.name, path: [...path, n, "name"] })),
|
|
507
|
-
`field entry name in ${label}`,
|
|
659
|
+
checkDuplicates({
|
|
660
|
+
names: (entries ?? []).map((entry, n) => ({ name: entry.name, path: [...path, n, "name"] })),
|
|
661
|
+
what: `field entry name in ${label}`,
|
|
508
662
|
issues
|
|
509
|
-
);
|
|
663
|
+
});
|
|
510
664
|
}
|
|
511
665
|
function checkPredicates(def, issues) {
|
|
512
666
|
const reserved = new Set(RESERVED_CONDITION_VARS), names = Object.keys(def.predicates ?? {});
|
|
@@ -516,9 +670,14 @@ function checkPredicates(def, issues) {
|
|
|
516
670
|
message: `predicate "${name}" shadows the built-in $${name} \u2014 pick another name`
|
|
517
671
|
});
|
|
518
672
|
for (const [name, groq2] of Object.entries(def.predicates ?? {}))
|
|
519
|
-
checkPredicateBody(name, groq2, names, issues);
|
|
520
|
-
}
|
|
521
|
-
function checkPredicateBody(
|
|
673
|
+
checkPredicateBody({ name, groq: groq2, names, issues });
|
|
674
|
+
}
|
|
675
|
+
function checkPredicateBody({
|
|
676
|
+
name,
|
|
677
|
+
groq: groq2,
|
|
678
|
+
names,
|
|
679
|
+
issues
|
|
680
|
+
}) {
|
|
522
681
|
for (const caller of PREDICATE_CALLER_VARS)
|
|
523
682
|
referencesVar(groq2, caller) && issues.push({
|
|
524
683
|
path: ["predicates", name],
|
|
@@ -548,33 +707,74 @@ function nonActionFilterConditionSites(def) {
|
|
|
548
707
|
groq: t.filter,
|
|
549
708
|
path: ["stages", i, "transitions", k, "filter"],
|
|
550
709
|
label: `transition "${t.name}" filter`
|
|
551
|
-
}), collectBindingSites(
|
|
552
|
-
t.effects,
|
|
553
|
-
["stages", i, "transitions", k, "effects"],
|
|
554
|
-
`transition "${t.name}"`,
|
|
710
|
+
}), collectBindingSites({
|
|
711
|
+
effects: t.effects,
|
|
712
|
+
path: ["stages", i, "transitions", k, "effects"],
|
|
713
|
+
label: `transition "${t.name}"`,
|
|
714
|
+
sites
|
|
715
|
+
}), collectOpWhereSites({
|
|
716
|
+
ops: t.ops,
|
|
717
|
+
path: ["stages", i, "transitions", k, "ops"],
|
|
718
|
+
label: `transition "${t.name}"`,
|
|
555
719
|
sites
|
|
556
|
-
);
|
|
720
|
+
});
|
|
557
721
|
for (const [j, activity] of (stage.activities ?? []).entries())
|
|
558
|
-
collectActivityConditionSites(activity, ["stages", i, "activities", j], sites);
|
|
722
|
+
collectActivityConditionSites({ activity, path: ["stages", i, "activities", j], sites });
|
|
559
723
|
}
|
|
560
724
|
return sites;
|
|
561
725
|
}
|
|
562
|
-
function
|
|
726
|
+
function collectOpWhereSites({
|
|
727
|
+
ops,
|
|
728
|
+
path,
|
|
729
|
+
label,
|
|
730
|
+
sites
|
|
731
|
+
}) {
|
|
732
|
+
for (const [o, op] of (ops ?? []).entries())
|
|
733
|
+
op.where !== void 0 && sites.push({
|
|
734
|
+
groq: op.where,
|
|
735
|
+
path: [...path, o, "where"],
|
|
736
|
+
label: `${label} ${op.type} where`
|
|
737
|
+
});
|
|
738
|
+
}
|
|
739
|
+
function collectActivityConditionSites({
|
|
740
|
+
activity,
|
|
741
|
+
path,
|
|
742
|
+
sites
|
|
743
|
+
}) {
|
|
563
744
|
for (const field of ["filter", "completeWhen", "failWhen"]) {
|
|
564
745
|
const groq2 = activity[field];
|
|
565
746
|
groq2 !== void 0 && sites.push({ groq: groq2, path: [...path, field], label: `activity "${activity.name}".${field}` });
|
|
566
747
|
}
|
|
567
|
-
collectBindingSites(
|
|
748
|
+
collectBindingSites({
|
|
749
|
+
effects: activity.effects,
|
|
750
|
+
path: [...path, "effects"],
|
|
751
|
+
label: `activity "${activity.name}"`,
|
|
752
|
+
sites
|
|
753
|
+
}), collectOpWhereSites({
|
|
754
|
+
ops: activity.ops,
|
|
755
|
+
path: [...path, "ops"],
|
|
756
|
+
label: `activity "${activity.name}"`,
|
|
757
|
+
sites
|
|
758
|
+
});
|
|
568
759
|
for (const [a, action] of (activity.actions ?? []).entries())
|
|
569
|
-
collectBindingSites(
|
|
570
|
-
action.effects,
|
|
571
|
-
[...path, "actions", a, "effects"],
|
|
572
|
-
`action "${action.name}"`,
|
|
760
|
+
collectBindingSites({
|
|
761
|
+
effects: action.effects,
|
|
762
|
+
path: [...path, "actions", a, "effects"],
|
|
763
|
+
label: `action "${action.name}"`,
|
|
573
764
|
sites
|
|
574
|
-
)
|
|
575
|
-
|
|
765
|
+
}), collectOpWhereSites({
|
|
766
|
+
ops: action.ops,
|
|
767
|
+
path: [...path, "actions", a, "ops"],
|
|
768
|
+
label: `action "${action.name}"`,
|
|
769
|
+
sites
|
|
770
|
+
});
|
|
771
|
+
collectSubworkflowSites({ activity, path, sites });
|
|
576
772
|
}
|
|
577
|
-
function collectSubworkflowSites(
|
|
773
|
+
function collectSubworkflowSites({
|
|
774
|
+
activity,
|
|
775
|
+
path,
|
|
776
|
+
sites
|
|
777
|
+
}) {
|
|
578
778
|
const sub = activity.subworkflows;
|
|
579
779
|
if (sub === void 0) return;
|
|
580
780
|
const subPath = [...path, "subworkflows"];
|
|
@@ -594,7 +794,12 @@ function collectSubworkflowSites(activity, path, sites) {
|
|
|
594
794
|
label: `activity "${activity.name}".subworkflows.${group} "${key}"`
|
|
595
795
|
});
|
|
596
796
|
}
|
|
597
|
-
function collectBindingSites(
|
|
797
|
+
function collectBindingSites({
|
|
798
|
+
effects,
|
|
799
|
+
path,
|
|
800
|
+
label,
|
|
801
|
+
sites
|
|
802
|
+
}) {
|
|
598
803
|
for (const [e, effect] of (effects ?? []).entries())
|
|
599
804
|
for (const [key, groq2] of Object.entries(effect.bindings ?? {}))
|
|
600
805
|
sites.push({
|
|
@@ -652,36 +857,67 @@ function checkActivityKinds(def, issues) {
|
|
|
652
857
|
}
|
|
653
858
|
function checkWorkflowInvariants(def) {
|
|
654
859
|
const issues = [], stageNames = checkStages(def, issues);
|
|
655
|
-
return checkInitialStage(def, stageNames, issues), checkTransitionTargets(def, stageNames, issues), checkEffectNames(def, issues), checkGuardNames(def, issues), checkFieldEntryNames(def, issues), checkRequiredField(def, issues), checkPredicates(def, issues), checkCanOutsideActionFilters(def, issues), checkAssigneesEntries(def, issues), checkActivityKinds(def, issues), issues;
|
|
860
|
+
return checkInitialStage({ def, stageNames, issues }), checkTransitionTargets({ def, stageNames, issues }), checkEffectNames(def, issues), checkGuardNames(def, issues), checkFieldEntryNames(def, issues), checkRequiredField(def, issues), checkPredicates(def, issues), checkCanOutsideActionFilters(def, issues), checkAssigneesEntries(def, issues), checkActivityKinds(def, issues), issues;
|
|
656
861
|
}
|
|
657
862
|
function defineWorkflow(definition) {
|
|
658
|
-
const label = labelFor("defineWorkflow", definition), parsed = parseOrThrow(AuthoringWorkflowSchema, definition, label), { definition: stored, issues: desugarIssues } = desugarWorkflow(parsed), issues = [...desugarIssues, ...checkWorkflowInvariants(stored)];
|
|
863
|
+
const label = labelFor("defineWorkflow", definition), parsed = parseOrThrow({ schema: AuthoringWorkflowSchema, input: definition, label }), { definition: stored, issues: desugarIssues } = desugarWorkflow(parsed), issues = [...desugarIssues, ...checkWorkflowInvariants(stored)];
|
|
659
864
|
if (issues.length > 0) throw new Error(formatValidationError(label, issues));
|
|
660
865
|
return stored;
|
|
661
866
|
}
|
|
867
|
+
function defineWorkflowConfig(config) {
|
|
868
|
+
return parseOrThrow({ schema: WorkflowConfigSchema, input: config, label: "defineWorkflowConfig" });
|
|
869
|
+
}
|
|
662
870
|
function defineStage(stage) {
|
|
663
|
-
return parseOrThrow(
|
|
871
|
+
return parseOrThrow({
|
|
872
|
+
schema: AuthoringStageSchema,
|
|
873
|
+
input: stage,
|
|
874
|
+
label: labelFor("defineStage", stage)
|
|
875
|
+
});
|
|
664
876
|
}
|
|
665
877
|
function defineActivity(activity) {
|
|
666
|
-
return parseOrThrow(
|
|
878
|
+
return parseOrThrow({
|
|
879
|
+
schema: AuthoringActivitySchema,
|
|
880
|
+
input: activity,
|
|
881
|
+
label: labelFor("defineActivity", activity)
|
|
882
|
+
});
|
|
667
883
|
}
|
|
668
884
|
function defineAction(action) {
|
|
669
|
-
return parseOrThrow(
|
|
885
|
+
return parseOrThrow({
|
|
886
|
+
schema: AuthoringActionSchema,
|
|
887
|
+
input: action,
|
|
888
|
+
label: labelFor("defineAction", action)
|
|
889
|
+
});
|
|
670
890
|
}
|
|
671
891
|
function defineTransition(transition) {
|
|
672
|
-
return parseOrThrow(
|
|
892
|
+
return parseOrThrow({
|
|
893
|
+
schema: AuthoringTransitionSchema,
|
|
894
|
+
input: transition,
|
|
895
|
+
label: transitionLabel(transition)
|
|
896
|
+
});
|
|
673
897
|
}
|
|
674
898
|
function defineField(entry) {
|
|
675
|
-
return parseOrThrow(
|
|
899
|
+
return parseOrThrow({
|
|
900
|
+
schema: AuthoringFieldEntrySchema,
|
|
901
|
+
input: entry,
|
|
902
|
+
label: labelFor("defineField", entry)
|
|
903
|
+
});
|
|
676
904
|
}
|
|
677
905
|
function defineOp(op) {
|
|
678
|
-
return parseOrThrow(AuthoringOpSchema, op, "defineOp");
|
|
906
|
+
return parseOrThrow({ schema: AuthoringOpSchema, input: op, label: "defineOp" });
|
|
679
907
|
}
|
|
680
908
|
function defineGuard(guard) {
|
|
681
|
-
return parseOrThrow(
|
|
909
|
+
return parseOrThrow({
|
|
910
|
+
schema: AuthoringGuardSchema,
|
|
911
|
+
input: guard,
|
|
912
|
+
label: labelFor("defineGuard", guard)
|
|
913
|
+
});
|
|
682
914
|
}
|
|
683
915
|
function defineEffect(effect) {
|
|
684
|
-
return parseOrThrow(
|
|
916
|
+
return parseOrThrow({
|
|
917
|
+
schema: EffectSchema,
|
|
918
|
+
input: effect,
|
|
919
|
+
label: labelFor("defineEffect", effect)
|
|
920
|
+
});
|
|
685
921
|
}
|
|
686
922
|
function transitionLabel(transition) {
|
|
687
923
|
const { name, to } = transition;
|
|
@@ -691,7 +927,11 @@ function defineEffectDescriptor(descriptor) {
|
|
|
691
927
|
if (!descriptor.name) throw new Error("EffectDescriptor missing name");
|
|
692
928
|
return descriptor;
|
|
693
929
|
}
|
|
694
|
-
function parseOrThrow(
|
|
930
|
+
function parseOrThrow({
|
|
931
|
+
schema,
|
|
932
|
+
input,
|
|
933
|
+
label
|
|
934
|
+
}) {
|
|
695
935
|
const result = v.safeParse(schema, input);
|
|
696
936
|
if (!result.success)
|
|
697
937
|
throw new Error(formatValidationError(label, issuesFromValibot(result.issues)));
|
|
@@ -705,6 +945,10 @@ function labelFor(fn, value) {
|
|
|
705
945
|
return fn;
|
|
706
946
|
}
|
|
707
947
|
export {
|
|
948
|
+
CONDITION_VARS,
|
|
949
|
+
FILTER_SCOPE_VARS,
|
|
950
|
+
GUARD_PREDICATE_VARS,
|
|
951
|
+
RESERVED_CONDITION_VARS,
|
|
708
952
|
defineAction,
|
|
709
953
|
defineActivity,
|
|
710
954
|
defineEffect,
|
|
@@ -715,6 +959,7 @@ export {
|
|
|
715
959
|
defineStage,
|
|
716
960
|
defineTransition,
|
|
717
961
|
defineWorkflow,
|
|
962
|
+
defineWorkflowConfig,
|
|
718
963
|
groq
|
|
719
964
|
};
|
|
720
965
|
//# sourceMappingURL=define.js.map
|