@sanity/workflow-engine 0.11.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 +237 -116
- package/dist/_chunks-cjs/schema.cjs.map +1 -1
- package/dist/_chunks-es/schema.js +238 -117
- package/dist/_chunks-es/schema.js.map +1 -1
- package/dist/define.cjs +461 -210
- package/dist/define.cjs.map +1 -1
- package/dist/define.d.cts +1123 -9270
- package/dist/define.d.ts +1123 -9270
- package/dist/define.js +462 -211
- package/dist/define.js.map +1 -1
- package/dist/index.cjs +2244 -1384
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1706 -12804
- package/dist/index.d.ts +1706 -12804
- package/dist/index.js +2246 -1386
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/define.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as v from "valibot";
|
|
2
|
-
import { andConditions, expandRequiredRoles, GROQ_IDENTIFIER, formatValidationError, issuesFromValibot, AuthoringWorkflowSchema, AuthoringStageSchema,
|
|
2
|
+
import { andConditions, expandRequiredRoles, normalizeRoleAliases, GROQ_IDENTIFIER, formatValidationError, issuesFromValibot, AuthoringWorkflowSchema, AuthoringStageSchema, AuthoringActivitySchema, AuthoringActionSchema, AuthoringTransitionSchema, AuthoringFieldEntrySchema, AuthoringOpSchema, GuardSchema, EffectSchema } from "./_chunks-es/schema.js";
|
|
3
3
|
function groq(strings, ...values) {
|
|
4
4
|
return strings.reduce((out, part, i) => i === 0 ? part : `${out}${serializeGroqValue(values[i - 1])}${part}`, "");
|
|
5
5
|
}
|
|
@@ -12,27 +12,29 @@ function serializeGroqValue(value) {
|
|
|
12
12
|
return serialized;
|
|
13
13
|
}
|
|
14
14
|
function desugarWorkflow(authoring) {
|
|
15
|
-
const
|
|
16
|
-
|
|
15
|
+
const issues = [];
|
|
16
|
+
checkReservedRoleAliasKeys(authoring.roleAliases, issues);
|
|
17
|
+
const roleAliases = normalizeRoleAliases(authoring.roleAliases), ctx = {
|
|
18
|
+
issues,
|
|
17
19
|
claimFields: /* @__PURE__ */ new Map(),
|
|
18
20
|
claimedFields: /* @__PURE__ */ new Set(),
|
|
19
|
-
roleAliases
|
|
20
|
-
}, workflowFields = desugarFieldEntries(authoring.fields, ["fields"], ctx), workflowLayer = layerOf(workflowFields), stages = authoring.stages.map((stage, i) => {
|
|
21
|
-
const path = ["stages", i], stageFields = desugarFieldEntries(stage.fields, [...path, "fields"], ctx), stageEnv = {
|
|
21
|
+
roleAliases
|
|
22
|
+
}, workflowFields = desugarFieldEntries({ entries: authoring.fields, path: ["fields"], ctx }), workflowLayer = layerOf(workflowFields), stages = authoring.stages.map((stage, i) => {
|
|
23
|
+
const path = ["stages", i], stageFields = desugarFieldEntries({ entries: stage.fields, path: [...path, "fields"], ctx }), stageEnv = {
|
|
22
24
|
layers: [
|
|
23
25
|
{ scope: "stage", entries: layerOf(stageFields) },
|
|
24
26
|
{ scope: "workflow", entries: workflowLayer }
|
|
25
27
|
]
|
|
26
|
-
},
|
|
27
|
-
(
|
|
28
|
+
}, activities = (stage.activities ?? []).map(
|
|
29
|
+
(activity, j) => desugarActivity({ activity, path: [...path, "activities", j], stageEnv, ctx })
|
|
28
30
|
), transitions = (stage.transitions ?? []).map(
|
|
29
|
-
(transition, k) => desugarTransition(transition, [...path, "transitions", k], stageEnv, ctx)
|
|
30
|
-
), editable = desugarStageEditable(
|
|
31
|
-
stage.editable,
|
|
32
|
-
editableSlotNames(workflowFields, stageFields,
|
|
33
|
-
[...path, "editable"],
|
|
31
|
+
(transition, k) => desugarTransition({ transition, path: [...path, "transitions", k], stageEnv, ctx })
|
|
32
|
+
), editable = desugarStageEditable({
|
|
33
|
+
overrides: stage.editable,
|
|
34
|
+
inScope: editableSlotNames({ workflowFields, stageFields, activities }),
|
|
35
|
+
path: [...path, "editable"],
|
|
34
36
|
ctx
|
|
35
|
-
);
|
|
37
|
+
});
|
|
36
38
|
return {
|
|
37
39
|
...stripUndefined({
|
|
38
40
|
name: stage.name,
|
|
@@ -41,7 +43,7 @@ function desugarWorkflow(authoring) {
|
|
|
41
43
|
guards: stage.guards
|
|
42
44
|
}),
|
|
43
45
|
...stageFields ? { fields: stageFields } : {},
|
|
44
|
-
...
|
|
46
|
+
...activities.length > 0 ? { activities } : {},
|
|
45
47
|
...transitions.length > 0 ? { transitions } : {},
|
|
46
48
|
...editable ? { editable } : {}
|
|
47
49
|
};
|
|
@@ -49,43 +51,99 @@ function desugarWorkflow(authoring) {
|
|
|
49
51
|
return checkUnclaimedClaimFields(ctx), { definition: {
|
|
50
52
|
...stripUndefined({
|
|
51
53
|
name: authoring.name,
|
|
52
|
-
version: authoring.version,
|
|
53
54
|
title: authoring.title,
|
|
54
55
|
description: authoring.description,
|
|
55
56
|
role: authoring.role,
|
|
56
57
|
initialStage: authoring.initialStage,
|
|
57
58
|
predicates: authoring.predicates,
|
|
58
|
-
roleAliases
|
|
59
|
+
roleAliases
|
|
59
60
|
}),
|
|
60
61
|
...workflowFields ? { fields: workflowFields } : {},
|
|
61
62
|
stages
|
|
62
63
|
}, issues: ctx.issues };
|
|
63
64
|
}
|
|
64
|
-
function
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
source: { type: "write" }
|
|
71
|
-
};
|
|
72
|
-
return ctx.claimFields.set(desugared, { name: entry.name, path: [...path, i] }), desugared;
|
|
73
|
-
}
|
|
74
|
-
const editable = normalizeEditable(entry.editable, [...path, i, "editable"], ctx);
|
|
75
|
-
return {
|
|
76
|
-
...stripUndefined({
|
|
77
|
-
type: entry.type,
|
|
78
|
-
name: entry.name,
|
|
79
|
-
title: entry.title,
|
|
80
|
-
description: entry.description,
|
|
81
|
-
required: entry.required,
|
|
82
|
-
editable
|
|
83
|
-
}),
|
|
84
|
-
source: entry.source
|
|
85
|
-
};
|
|
86
|
-
});
|
|
65
|
+
function checkReservedRoleAliasKeys(aliases, issues) {
|
|
66
|
+
for (const key of Object.keys(aliases ?? {}))
|
|
67
|
+
key.startsWith("$") && issues.push({
|
|
68
|
+
path: ["roleAliases", key],
|
|
69
|
+
message: `role alias key "${key}" uses the reserved "$" prefix \u2014 that namespace is the engine's stored spelling for the universal fulfiller. Use "*" to mean "fulfills any gate", or rename the role.`
|
|
70
|
+
});
|
|
87
71
|
}
|
|
88
|
-
|
|
72
|
+
const TODOLIST_OF = [
|
|
73
|
+
{ type: "string", name: "label", title: "Label" },
|
|
74
|
+
{ type: "string", name: "status", title: "Status" },
|
|
75
|
+
{ type: "assignee", name: "assignee", title: "Assignee" },
|
|
76
|
+
{ type: "date", name: "dueDate", title: "Due date" }
|
|
77
|
+
], NOTES_OF = [
|
|
78
|
+
{ type: "text", name: "body", title: "Body" },
|
|
79
|
+
{ type: "actor", name: "actor", title: "Actor" },
|
|
80
|
+
{ type: "datetime", name: "at", title: "At" }
|
|
81
|
+
];
|
|
82
|
+
function desugarFieldEntries({
|
|
83
|
+
entries,
|
|
84
|
+
path,
|
|
85
|
+
ctx
|
|
86
|
+
}) {
|
|
87
|
+
return !entries || entries.length === 0 ? entries === void 0 ? void 0 : [] : entries.map((entry, i) => desugarFieldEntry({ entry, path: [...path, i], ctx }));
|
|
88
|
+
}
|
|
89
|
+
function desugarFieldEntry({
|
|
90
|
+
entry,
|
|
91
|
+
path,
|
|
92
|
+
ctx
|
|
93
|
+
}) {
|
|
94
|
+
if (entry.type === "claim") return desugarClaimField({ entry, path, ctx });
|
|
95
|
+
if (entry.type === "todoList" || entry.type === "notes")
|
|
96
|
+
return desugarListField({ entry, path, ctx });
|
|
97
|
+
const editable = normalizeEditable({ editable: entry.editable, path: [...path, "editable"], ctx });
|
|
98
|
+
return {
|
|
99
|
+
...stripUndefined({
|
|
100
|
+
type: entry.type,
|
|
101
|
+
name: entry.name,
|
|
102
|
+
title: entry.title,
|
|
103
|
+
description: entry.description,
|
|
104
|
+
required: entry.required,
|
|
105
|
+
initialValue: entry.initialValue,
|
|
106
|
+
editable,
|
|
107
|
+
fields: entry.fields,
|
|
108
|
+
of: entry.of
|
|
109
|
+
})
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
function desugarClaimField({
|
|
113
|
+
entry,
|
|
114
|
+
path,
|
|
115
|
+
ctx
|
|
116
|
+
}) {
|
|
117
|
+
const desugared = {
|
|
118
|
+
...stripUndefined({ name: entry.name, title: entry.title, description: entry.description }),
|
|
119
|
+
type: "actor"
|
|
120
|
+
};
|
|
121
|
+
return ctx.claimFields.set(desugared, { name: entry.name, path }), desugared;
|
|
122
|
+
}
|
|
123
|
+
function desugarListField({
|
|
124
|
+
entry,
|
|
125
|
+
path,
|
|
126
|
+
ctx
|
|
127
|
+
}) {
|
|
128
|
+
const editable = normalizeEditable({ editable: entry.editable, path: [...path, "editable"], ctx });
|
|
129
|
+
return {
|
|
130
|
+
...stripUndefined({
|
|
131
|
+
name: entry.name,
|
|
132
|
+
title: entry.title,
|
|
133
|
+
description: entry.description,
|
|
134
|
+
required: entry.required,
|
|
135
|
+
initialValue: entry.initialValue,
|
|
136
|
+
editable
|
|
137
|
+
}),
|
|
138
|
+
type: "array",
|
|
139
|
+
of: entry.type === "todoList" ? TODOLIST_OF : NOTES_OF
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function normalizeEditable({
|
|
143
|
+
editable,
|
|
144
|
+
path,
|
|
145
|
+
ctx
|
|
146
|
+
}) {
|
|
89
147
|
if (editable === void 0 || editable === !0) return editable;
|
|
90
148
|
if (Array.isArray(editable)) {
|
|
91
149
|
const condition = rolesCondition(editable, ctx.roleAliases);
|
|
@@ -100,26 +158,35 @@ function normalizeEditable(editable, path, ctx) {
|
|
|
100
158
|
}
|
|
101
159
|
return editable;
|
|
102
160
|
}
|
|
103
|
-
function editableSlotNames(
|
|
161
|
+
function editableSlotNames({
|
|
162
|
+
workflowFields,
|
|
163
|
+
stageFields,
|
|
164
|
+
activities
|
|
165
|
+
}) {
|
|
104
166
|
const names = /* @__PURE__ */ new Set(), collect = (entries) => {
|
|
105
167
|
for (const entry of entries ?? []) entry.editable !== void 0 && names.add(entry.name);
|
|
106
168
|
};
|
|
107
169
|
collect(workflowFields), collect(stageFields);
|
|
108
|
-
for (const
|
|
170
|
+
for (const activity of activities) collect(activity.fields);
|
|
109
171
|
return names;
|
|
110
172
|
}
|
|
111
|
-
function desugarStageEditable(
|
|
173
|
+
function desugarStageEditable({
|
|
174
|
+
overrides,
|
|
175
|
+
inScope,
|
|
176
|
+
path,
|
|
177
|
+
ctx
|
|
178
|
+
}) {
|
|
112
179
|
if (overrides === void 0) return;
|
|
113
180
|
const out = {};
|
|
114
181
|
for (const [name, value] of Object.entries(overrides)) {
|
|
115
182
|
if (!inScope.has(name)) {
|
|
116
183
|
ctx.issues.push({
|
|
117
184
|
path: [...path, name],
|
|
118
|
-
message: `stage editable override "${name}" does not narrow an editable slot in scope \u2014 name a workflow/stage/
|
|
185
|
+
message: `stage editable override "${name}" does not narrow an editable slot in scope \u2014 name a workflow/stage/activity slot of this stage that declares \`editable\``
|
|
119
186
|
});
|
|
120
187
|
continue;
|
|
121
188
|
}
|
|
122
|
-
const normalized = normalizeEditable(value, [...path, name], ctx);
|
|
189
|
+
const normalized = normalizeEditable({ editable: value, path: [...path, name], ctx });
|
|
123
190
|
normalized !== void 0 && (out[name] = normalized);
|
|
124
191
|
}
|
|
125
192
|
return Object.keys(out).length > 0 ? out : void 0;
|
|
@@ -127,46 +194,72 @@ function desugarStageEditable(overrides, inScope, path, ctx) {
|
|
|
127
194
|
function layerOf(entries) {
|
|
128
195
|
return new Map((entries ?? []).map((entry) => [entry.name, entry]));
|
|
129
196
|
}
|
|
130
|
-
function
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
197
|
+
function desugarActivity({
|
|
198
|
+
activity,
|
|
199
|
+
path,
|
|
200
|
+
stageEnv,
|
|
201
|
+
ctx
|
|
202
|
+
}) {
|
|
203
|
+
const activityFields = desugarFieldEntries({
|
|
204
|
+
entries: activity.fields,
|
|
205
|
+
path: [...path, "fields"],
|
|
206
|
+
ctx
|
|
207
|
+
}), env = {
|
|
208
|
+
layers: [{ scope: "activity", entries: layerOf(activityFields) }, ...stageEnv.layers]
|
|
209
|
+
}, ops = desugarOps({
|
|
210
|
+
ops: activity.ops,
|
|
211
|
+
path: [...path, "ops"],
|
|
212
|
+
env,
|
|
213
|
+
firingActivity: activity.name,
|
|
214
|
+
ctx
|
|
215
|
+
}), actions = (activity.actions ?? []).map(
|
|
216
|
+
(action, a) => desugarAction({ action, path: [...path, "actions", a], env, activityName: activity.name, ctx })
|
|
217
|
+
), target = desugarTarget({ target: activity.target, env, path: [...path, "target"], ctx });
|
|
136
218
|
return {
|
|
137
219
|
...stripUndefined({
|
|
138
|
-
name:
|
|
139
|
-
title:
|
|
140
|
-
description:
|
|
141
|
-
kind:
|
|
142
|
-
filter:
|
|
143
|
-
requirements:
|
|
144
|
-
completeWhen:
|
|
145
|
-
failWhen:
|
|
146
|
-
effects:
|
|
147
|
-
subworkflows:
|
|
220
|
+
name: activity.name,
|
|
221
|
+
title: activity.title,
|
|
222
|
+
description: activity.description,
|
|
223
|
+
kind: activity.kind,
|
|
224
|
+
filter: activity.filter,
|
|
225
|
+
requirements: activity.requirements,
|
|
226
|
+
completeWhen: activity.completeWhen,
|
|
227
|
+
failWhen: activity.failWhen,
|
|
228
|
+
effects: activity.effects,
|
|
229
|
+
subworkflows: activity.subworkflows
|
|
148
230
|
}),
|
|
149
|
-
activation:
|
|
231
|
+
activation: activity.activation ?? "manual",
|
|
150
232
|
...target ? { target } : {},
|
|
151
|
-
...
|
|
233
|
+
...activityFields ? { fields: activityFields } : {},
|
|
152
234
|
...ops ? { ops } : {},
|
|
153
235
|
...actions.length > 0 ? { actions } : {}
|
|
154
236
|
};
|
|
155
237
|
}
|
|
156
238
|
const TARGET_DOC_KINDS = ["doc.ref", "doc.refs", "release.ref"];
|
|
157
|
-
function desugarTarget(
|
|
239
|
+
function desugarTarget({
|
|
240
|
+
target,
|
|
241
|
+
env,
|
|
242
|
+
path,
|
|
243
|
+
ctx
|
|
244
|
+
}) {
|
|
158
245
|
if (target === void 0 || target.type === "url") return target;
|
|
159
|
-
const ref = typeof target.field == "string" ? { field: target.field } : target.field, field = resolveRef(ref, env, [...path, "field"], ctx) ?? fallbackRef(ref), entry = entryAt(env, field);
|
|
246
|
+
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);
|
|
160
247
|
return entry && !TARGET_DOC_KINDS.includes(entry.type) && ctx.issues.push({
|
|
161
248
|
path: [...path, "field"],
|
|
162
|
-
message: `manual
|
|
249
|
+
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(", ")})`
|
|
163
250
|
}), { type: "field", field };
|
|
164
251
|
}
|
|
165
|
-
function desugarAction(
|
|
252
|
+
function desugarAction({
|
|
253
|
+
action,
|
|
254
|
+
path,
|
|
255
|
+
env,
|
|
256
|
+
activityName,
|
|
257
|
+
ctx
|
|
258
|
+
}) {
|
|
166
259
|
if ("type" in action)
|
|
167
|
-
return desugarClaimAction(action, path, env, ctx);
|
|
168
|
-
const filter = andConditions([rolesCondition(action.roles, ctx.roleAliases), action.filter]), ops = desugarOps(action.ops, [...path, "ops"], env,
|
|
169
|
-
return action.status !== void 0 && ops.push({ type: "status.set",
|
|
260
|
+
return desugarClaimAction({ action, path, env, ctx });
|
|
261
|
+
const filter = andConditions([rolesCondition(action.roles, ctx.roleAliases), action.filter]), ops = desugarOps({ ops: action.ops, path: [...path, "ops"], env, firingActivity: activityName, ctx }) ?? [];
|
|
262
|
+
return action.status !== void 0 && ops.push({ type: "status.set", activity: activityName, status: action.status }), {
|
|
170
263
|
...stripUndefined({
|
|
171
264
|
name: action.name,
|
|
172
265
|
title: action.title,
|
|
@@ -178,14 +271,26 @@ function desugarAction(action, path, env, taskName, ctx) {
|
|
|
178
271
|
...ops.length > 0 ? { ops } : {}
|
|
179
272
|
};
|
|
180
273
|
}
|
|
181
|
-
function desugarClaimAction(
|
|
182
|
-
|
|
274
|
+
function desugarClaimAction({
|
|
275
|
+
action,
|
|
276
|
+
path,
|
|
277
|
+
env,
|
|
278
|
+
ctx
|
|
279
|
+
}) {
|
|
280
|
+
const ref = typeof action.field == "string" ? { field: action.field } : action.field, resolved = resolveRef({ ref, env, path: [...path, "field"], ctx });
|
|
183
281
|
if (resolved) {
|
|
184
282
|
const entry = entryAt(env, resolved);
|
|
185
|
-
entry && entry.type !== "
|
|
283
|
+
entry && entry.type !== "actor" && ctx.issues.push({
|
|
284
|
+
path: [...path, "field"],
|
|
285
|
+
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)`
|
|
286
|
+
}), checkShadowedClaimTarget({
|
|
287
|
+
actionName: action.name,
|
|
288
|
+
field: ref.field,
|
|
289
|
+
resolved,
|
|
290
|
+
env,
|
|
186
291
|
path: [...path, "field"],
|
|
187
|
-
|
|
188
|
-
}),
|
|
292
|
+
ctx
|
|
293
|
+
}), entry && ctx.claimedFields.add(entry);
|
|
189
294
|
}
|
|
190
295
|
const noSteal = `!defined($fields.${ref.field})`, filter = andConditions([
|
|
191
296
|
noSteal,
|
|
@@ -204,7 +309,14 @@ function desugarClaimAction(action, path, env, ctx) {
|
|
|
204
309
|
...ops.length > 0 ? { ops } : {}
|
|
205
310
|
};
|
|
206
311
|
}
|
|
207
|
-
function checkShadowedClaimTarget(
|
|
312
|
+
function checkShadowedClaimTarget({
|
|
313
|
+
actionName,
|
|
314
|
+
field,
|
|
315
|
+
resolved,
|
|
316
|
+
env,
|
|
317
|
+
path,
|
|
318
|
+
ctx
|
|
319
|
+
}) {
|
|
208
320
|
const nearest = env.layers.find((l) => l.entries.has(field));
|
|
209
321
|
nearest === void 0 || nearest.scope === resolved.scope || ctx.issues.push({
|
|
210
322
|
path,
|
|
@@ -215,11 +327,22 @@ function checkUnclaimedClaimFields(ctx) {
|
|
|
215
327
|
for (const [entry, { name, path }] of ctx.claimFields)
|
|
216
328
|
ctx.claimedFields.has(entry) || ctx.issues.push({
|
|
217
329
|
path,
|
|
218
|
-
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
|
|
330
|
+
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`
|
|
219
331
|
});
|
|
220
332
|
}
|
|
221
|
-
function desugarTransition(
|
|
222
|
-
|
|
333
|
+
function desugarTransition({
|
|
334
|
+
transition,
|
|
335
|
+
path,
|
|
336
|
+
stageEnv,
|
|
337
|
+
ctx
|
|
338
|
+
}) {
|
|
339
|
+
const ops = desugarOps({
|
|
340
|
+
ops: transition.ops,
|
|
341
|
+
path: [...path, "ops"],
|
|
342
|
+
env: stageEnv,
|
|
343
|
+
firingActivity: void 0,
|
|
344
|
+
ctx
|
|
345
|
+
});
|
|
223
346
|
return {
|
|
224
347
|
...stripUndefined({
|
|
225
348
|
name: transition.name,
|
|
@@ -228,29 +351,46 @@ function desugarTransition(transition, path, stageEnv, ctx) {
|
|
|
228
351
|
to: transition.to,
|
|
229
352
|
effects: transition.effects
|
|
230
353
|
}),
|
|
231
|
-
filter: transition.filter ?? "$
|
|
354
|
+
filter: transition.filter ?? "$allActivitiesDone",
|
|
232
355
|
...ops ? { ops } : {}
|
|
233
356
|
};
|
|
234
357
|
}
|
|
235
|
-
function desugarOps(
|
|
358
|
+
function desugarOps({
|
|
359
|
+
ops,
|
|
360
|
+
path,
|
|
361
|
+
env,
|
|
362
|
+
firingActivity,
|
|
363
|
+
ctx
|
|
364
|
+
}) {
|
|
236
365
|
if (ops)
|
|
237
|
-
return ops.map((op, i) => desugarOp(op, [...path, i], env,
|
|
238
|
-
}
|
|
239
|
-
function desugarOp(
|
|
366
|
+
return ops.map((op, i) => desugarOp({ op, path: [...path, i], env, firingActivity, ctx }));
|
|
367
|
+
}
|
|
368
|
+
function desugarOp({
|
|
369
|
+
op,
|
|
370
|
+
path,
|
|
371
|
+
env,
|
|
372
|
+
firingActivity,
|
|
373
|
+
ctx
|
|
374
|
+
}) {
|
|
240
375
|
if (op.type === "status.set") {
|
|
241
|
-
const
|
|
242
|
-
return
|
|
243
|
-
path: [...path, "
|
|
244
|
-
message: "status.set outside an action must name its target
|
|
245
|
-
}), { type: "status.set",
|
|
376
|
+
const activity = op.activity ?? firingActivity;
|
|
377
|
+
return activity === void 0 && ctx.issues.push({
|
|
378
|
+
path: [...path, "activity"],
|
|
379
|
+
message: "status.set outside an action must name its target activity"
|
|
380
|
+
}), { type: "status.set", activity: activity ?? "", status: op.status };
|
|
246
381
|
}
|
|
247
|
-
if (op.type === "audit") return desugarAuditOp(op, path, env, ctx);
|
|
248
|
-
const target = resolveRef(op.target, env, [...path, "target"], ctx) ?? fallbackRef(op.target);
|
|
382
|
+
if (op.type === "audit") return desugarAuditOp({ op, path, env, ctx });
|
|
383
|
+
const target = resolveRef({ ref: op.target, env, path: [...path, "target"], ctx }) ?? fallbackRef(op.target);
|
|
249
384
|
return { ...op, target };
|
|
250
385
|
}
|
|
251
386
|
const AUDIT_STAMPS = { actor: { type: "actor" }, at: { type: "now" } };
|
|
252
|
-
function desugarAuditOp(
|
|
253
|
-
|
|
387
|
+
function desugarAuditOp({
|
|
388
|
+
op,
|
|
389
|
+
path,
|
|
390
|
+
env,
|
|
391
|
+
ctx
|
|
392
|
+
}) {
|
|
393
|
+
const target = resolveRef({ ref: op.target, env, path: [...path, "target"], ctx }) ?? fallbackRef(op.target);
|
|
254
394
|
if (op.value.type !== "object")
|
|
255
395
|
return ctx.issues.push({
|
|
256
396
|
path: [...path, "value"],
|
|
@@ -272,7 +412,12 @@ function desugarAuditOp(op, path, env, ctx) {
|
|
|
272
412
|
}
|
|
273
413
|
return { type: "field.append", target, value: { type: "object", fields } };
|
|
274
414
|
}
|
|
275
|
-
function resolveRef(
|
|
415
|
+
function resolveRef({
|
|
416
|
+
ref,
|
|
417
|
+
env,
|
|
418
|
+
path,
|
|
419
|
+
ctx
|
|
420
|
+
}) {
|
|
276
421
|
const layers = ref.scope === void 0 ? env.layers : env.layers.filter((l) => l.scope === ref.scope);
|
|
277
422
|
for (const layer of layers)
|
|
278
423
|
if (layer.entries.has(ref.field)) return { scope: layer.scope, field: ref.field };
|
|
@@ -306,83 +451,115 @@ const RESERVED_CONDITION_VARS = [
|
|
|
306
451
|
"row",
|
|
307
452
|
"now",
|
|
308
453
|
"effects",
|
|
309
|
-
"
|
|
454
|
+
"activities",
|
|
310
455
|
"subworkflows",
|
|
311
456
|
"self",
|
|
312
457
|
"stage",
|
|
313
458
|
"parent",
|
|
314
459
|
"ancestors",
|
|
315
|
-
"
|
|
316
|
-
"
|
|
460
|
+
"allActivitiesDone",
|
|
461
|
+
"anyActivityFailed"
|
|
317
462
|
], PREDICATE_CALLER_VARS = ["actor", "assigned", "can"];
|
|
318
463
|
function knownList(ids) {
|
|
319
464
|
return [...ids].map((s) => `"${s}"`).join(", ");
|
|
320
465
|
}
|
|
321
|
-
function checkDuplicates(
|
|
466
|
+
function checkDuplicates({
|
|
467
|
+
names,
|
|
468
|
+
what,
|
|
469
|
+
issues
|
|
470
|
+
}) {
|
|
322
471
|
const seen = /* @__PURE__ */ new Set();
|
|
323
472
|
for (const { name, path } of names)
|
|
324
473
|
seen.has(name) && issues.push({ path, message: `duplicate ${what} "${name}" (already declared earlier)` }), seen.add(name);
|
|
325
474
|
return seen;
|
|
326
475
|
}
|
|
327
476
|
function checkStages(def, issues) {
|
|
328
|
-
const stageNames = checkDuplicates(
|
|
329
|
-
def.stages.map((stage, i) => ({ name: stage.name, path: ["stages", i, "name"] })),
|
|
330
|
-
"stage name",
|
|
477
|
+
const stageNames = checkDuplicates({
|
|
478
|
+
names: def.stages.map((stage, i) => ({ name: stage.name, path: ["stages", i, "name"] })),
|
|
479
|
+
what: "stage name",
|
|
331
480
|
issues
|
|
332
|
-
);
|
|
481
|
+
});
|
|
333
482
|
for (const [i, stage] of def.stages.entries()) {
|
|
334
|
-
const
|
|
335
|
-
(stage.
|
|
336
|
-
name:
|
|
337
|
-
path: ["stages", i, "
|
|
483
|
+
const activityNames = checkDuplicates({
|
|
484
|
+
names: (stage.activities ?? []).map((activity, j) => ({
|
|
485
|
+
name: activity.name,
|
|
486
|
+
path: ["stages", i, "activities", j, "name"]
|
|
338
487
|
})),
|
|
339
|
-
`
|
|
488
|
+
what: `activity name in stage "${stage.name}"`,
|
|
340
489
|
issues
|
|
341
|
-
);
|
|
342
|
-
checkDuplicates(
|
|
343
|
-
(stage.transitions ?? []).map((t, k) => ({
|
|
490
|
+
});
|
|
491
|
+
checkDuplicates({
|
|
492
|
+
names: (stage.transitions ?? []).map((t, k) => ({
|
|
344
493
|
name: t.name,
|
|
345
494
|
path: ["stages", i, "transitions", k, "name"]
|
|
346
495
|
})),
|
|
347
|
-
`transition name in stage "${stage.name}"`,
|
|
496
|
+
what: `transition name in stage "${stage.name}"`,
|
|
348
497
|
issues
|
|
349
|
-
),
|
|
498
|
+
}), checkActivities({ def, i, activityNames, issues });
|
|
350
499
|
}
|
|
351
500
|
return stageNames;
|
|
352
501
|
}
|
|
353
|
-
function
|
|
502
|
+
function checkActivities({
|
|
503
|
+
def,
|
|
504
|
+
i,
|
|
505
|
+
activityNames,
|
|
506
|
+
issues
|
|
507
|
+
}) {
|
|
354
508
|
const stage = def.stages[i];
|
|
355
|
-
for (const [j,
|
|
356
|
-
const path = ["stages", i, "
|
|
357
|
-
checkDuplicates(
|
|
358
|
-
(
|
|
509
|
+
for (const [j, activity] of (stage.activities ?? []).entries()) {
|
|
510
|
+
const path = ["stages", i, "activities", j];
|
|
511
|
+
checkDuplicates({
|
|
512
|
+
names: (activity.actions ?? []).map((action, a) => ({
|
|
359
513
|
name: action.name,
|
|
360
514
|
path: [...path, "actions", a, "name"]
|
|
361
515
|
})),
|
|
362
|
-
`action name in
|
|
516
|
+
what: `action name in activity "${activity.name}"`,
|
|
363
517
|
issues
|
|
364
|
-
), checkStatusSetTargets(
|
|
518
|
+
}), checkStatusSetTargets({ activity, activityNames, path, issues });
|
|
365
519
|
}
|
|
366
520
|
}
|
|
367
|
-
function checkStatusSetTargets(
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
521
|
+
function checkStatusSetTargets({
|
|
522
|
+
activity,
|
|
523
|
+
activityNames,
|
|
524
|
+
path,
|
|
525
|
+
issues
|
|
526
|
+
}) {
|
|
527
|
+
checkStatusSetOps({ ops: activity.ops, activityNames, path: [...path, "ops"], issues });
|
|
528
|
+
for (const [a, action] of (activity.actions ?? []).entries())
|
|
529
|
+
checkStatusSetOps({
|
|
530
|
+
ops: action.ops,
|
|
531
|
+
activityNames,
|
|
532
|
+
path: [...path, "actions", a, "ops"],
|
|
533
|
+
issues
|
|
534
|
+
});
|
|
371
535
|
}
|
|
372
|
-
function checkStatusSetOps(
|
|
536
|
+
function checkStatusSetOps({
|
|
537
|
+
ops,
|
|
538
|
+
activityNames,
|
|
539
|
+
path,
|
|
540
|
+
issues
|
|
541
|
+
}) {
|
|
373
542
|
for (const [o, op] of (ops ?? []).entries())
|
|
374
|
-
op.type !== "status.set" ||
|
|
375
|
-
path: [...path, o, "
|
|
376
|
-
message: `status.set targets
|
|
543
|
+
op.type !== "status.set" || activityNames.has(op.activity) || issues.push({
|
|
544
|
+
path: [...path, o, "activity"],
|
|
545
|
+
message: `status.set targets activity "${op.activity}" which is not declared in this stage. Known activities: ${knownList(activityNames)}`
|
|
377
546
|
});
|
|
378
547
|
}
|
|
379
|
-
function checkInitialStage(
|
|
548
|
+
function checkInitialStage({
|
|
549
|
+
def,
|
|
550
|
+
stageNames,
|
|
551
|
+
issues
|
|
552
|
+
}) {
|
|
380
553
|
stageNames.has(def.initialStage) || issues.push({
|
|
381
554
|
path: ["initialStage"],
|
|
382
555
|
message: `initialStage "${def.initialStage}" is not a declared stage. Known stages: ${knownList(stageNames)}`
|
|
383
556
|
});
|
|
384
557
|
}
|
|
385
|
-
function checkTransitionTargets(
|
|
558
|
+
function checkTransitionTargets({
|
|
559
|
+
def,
|
|
560
|
+
stageNames,
|
|
561
|
+
issues
|
|
562
|
+
}) {
|
|
386
563
|
for (const [i, stage] of def.stages.entries())
|
|
387
564
|
for (const [k, t] of (stage.transitions ?? []).entries())
|
|
388
565
|
stageNames.has(t.to) || issues.push({
|
|
@@ -393,17 +570,33 @@ function checkTransitionTargets(def, stageNames, issues) {
|
|
|
393
570
|
function checkEffectNames(def, issues) {
|
|
394
571
|
const sites = [];
|
|
395
572
|
for (const [i, stage] of def.stages.entries()) {
|
|
396
|
-
for (const [j,
|
|
397
|
-
collectEffects(
|
|
398
|
-
|
|
399
|
-
|
|
573
|
+
for (const [j, activity] of (stage.activities ?? []).entries()) {
|
|
574
|
+
collectEffects({
|
|
575
|
+
effects: activity.effects,
|
|
576
|
+
path: ["stages", i, "activities", j, "effects"],
|
|
577
|
+
sites
|
|
578
|
+
});
|
|
579
|
+
for (const [a, action] of (activity.actions ?? []).entries())
|
|
580
|
+
collectEffects({
|
|
581
|
+
effects: action.effects,
|
|
582
|
+
path: ["stages", i, "activities", j, "actions", a, "effects"],
|
|
583
|
+
sites
|
|
584
|
+
});
|
|
400
585
|
}
|
|
401
586
|
for (const [k, t] of (stage.transitions ?? []).entries())
|
|
402
|
-
collectEffects(t.effects, ["stages", i, "transitions", k, "effects"], sites);
|
|
587
|
+
collectEffects({ effects: t.effects, path: ["stages", i, "transitions", k, "effects"], sites });
|
|
403
588
|
}
|
|
404
|
-
checkDuplicates(
|
|
589
|
+
checkDuplicates({
|
|
590
|
+
names: sites,
|
|
591
|
+
what: "effect name (registry key \u2014 unique per definition)",
|
|
592
|
+
issues
|
|
593
|
+
});
|
|
405
594
|
}
|
|
406
|
-
function collectEffects(
|
|
595
|
+
function collectEffects({
|
|
596
|
+
effects,
|
|
597
|
+
path,
|
|
598
|
+
sites
|
|
599
|
+
}) {
|
|
407
600
|
for (const [e, effect] of (effects ?? []).entries())
|
|
408
601
|
sites.push({ name: effect.name, path: [...path, e, "name"] });
|
|
409
602
|
}
|
|
@@ -414,11 +607,11 @@ function checkGuardNames(def, issues) {
|
|
|
414
607
|
path: ["stages", i, "guards", g, "name"]
|
|
415
608
|
}))
|
|
416
609
|
);
|
|
417
|
-
checkDuplicates(
|
|
418
|
-
sites,
|
|
419
|
-
"guard name (the guard lake _id derives from it \u2014 unique per definition)",
|
|
610
|
+
checkDuplicates({
|
|
611
|
+
names: sites,
|
|
612
|
+
what: "guard name (the guard lake _id derives from it \u2014 unique per definition)",
|
|
420
613
|
issues
|
|
421
|
-
);
|
|
614
|
+
});
|
|
422
615
|
}
|
|
423
616
|
function fieldScopes(def) {
|
|
424
617
|
const scopes = [
|
|
@@ -431,12 +624,12 @@ function fieldScopes(def) {
|
|
|
431
624
|
path: ["stages", i, "fields"],
|
|
432
625
|
label: `stage "${stage.name}" fields`
|
|
433
626
|
});
|
|
434
|
-
for (const [j,
|
|
627
|
+
for (const [j, activity] of (stage.activities ?? []).entries())
|
|
435
628
|
scopes.push({
|
|
436
|
-
entries:
|
|
437
|
-
scope: "
|
|
438
|
-
path: ["stages", i, "
|
|
439
|
-
label: `
|
|
629
|
+
entries: activity.fields,
|
|
630
|
+
scope: "activity",
|
|
631
|
+
path: ["stages", i, "activities", j, "fields"],
|
|
632
|
+
label: `activity "${activity.name}" fields`
|
|
440
633
|
});
|
|
441
634
|
}
|
|
442
635
|
return scopes;
|
|
@@ -444,21 +637,28 @@ function fieldScopes(def) {
|
|
|
444
637
|
function checkRequiredField(def, issues) {
|
|
445
638
|
for (const { entries, scope, path, label } of fieldScopes(def))
|
|
446
639
|
for (const [n, entry] of (entries ?? []).entries())
|
|
447
|
-
entry.required === !0
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
640
|
+
if (entry.required === !0) {
|
|
641
|
+
if (scope !== "workflow")
|
|
642
|
+
issues.push({
|
|
643
|
+
path: [...path, n, "required"],
|
|
644
|
+
message: `${label} entry "${entry.name}" is \`required\`, but \`required\` applies only to workflow-scope \`input\` entries \u2014 only those are caller-supplied at start/spawn`
|
|
645
|
+
});
|
|
646
|
+
else if (entry.initialValue?.type !== "input") {
|
|
647
|
+
const seed = entry.initialValue?.type ?? "working memory (no initialValue)";
|
|
648
|
+
issues.push({
|
|
649
|
+
path: [...path, n, "required"],
|
|
650
|
+
message: `field entry "${entry.name}" has initialValue "${seed}" \u2014 \`required\` applies only to \`input\`-sourced entries (the caller fills them at start/spawn)`
|
|
651
|
+
});
|
|
652
|
+
}
|
|
653
|
+
}
|
|
454
654
|
}
|
|
455
655
|
function checkFieldEntryNames(def, issues) {
|
|
456
656
|
for (const { entries, path, label } of fieldScopes(def))
|
|
457
|
-
checkDuplicates(
|
|
458
|
-
(entries ?? []).map((entry, n) => ({ name: entry.name, path: [...path, n, "name"] })),
|
|
459
|
-
`field entry name in ${label}`,
|
|
657
|
+
checkDuplicates({
|
|
658
|
+
names: (entries ?? []).map((entry, n) => ({ name: entry.name, path: [...path, n, "name"] })),
|
|
659
|
+
what: `field entry name in ${label}`,
|
|
460
660
|
issues
|
|
461
|
-
);
|
|
661
|
+
});
|
|
462
662
|
}
|
|
463
663
|
function checkPredicates(def, issues) {
|
|
464
664
|
const reserved = new Set(RESERVED_CONDITION_VARS), names = Object.keys(def.predicates ?? {});
|
|
@@ -468,9 +668,14 @@ function checkPredicates(def, issues) {
|
|
|
468
668
|
message: `predicate "${name}" shadows the built-in $${name} \u2014 pick another name`
|
|
469
669
|
});
|
|
470
670
|
for (const [name, groq2] of Object.entries(def.predicates ?? {}))
|
|
471
|
-
checkPredicateBody(name, groq2, names, issues);
|
|
472
|
-
}
|
|
473
|
-
function checkPredicateBody(
|
|
671
|
+
checkPredicateBody({ name, groq: groq2, names, issues });
|
|
672
|
+
}
|
|
673
|
+
function checkPredicateBody({
|
|
674
|
+
name,
|
|
675
|
+
groq: groq2,
|
|
676
|
+
names,
|
|
677
|
+
issues
|
|
678
|
+
}) {
|
|
474
679
|
for (const caller of PREDICATE_CALLER_VARS)
|
|
475
680
|
referencesVar(groq2, caller) && issues.push({
|
|
476
681
|
path: ["predicates", name],
|
|
@@ -500,40 +705,53 @@ function nonActionFilterConditionSites(def) {
|
|
|
500
705
|
groq: t.filter,
|
|
501
706
|
path: ["stages", i, "transitions", k, "filter"],
|
|
502
707
|
label: `transition "${t.name}" filter`
|
|
503
|
-
}), collectBindingSites(
|
|
504
|
-
t.effects,
|
|
505
|
-
["stages", i, "transitions", k, "effects"],
|
|
506
|
-
`transition "${t.name}"`,
|
|
708
|
+
}), collectBindingSites({
|
|
709
|
+
effects: t.effects,
|
|
710
|
+
path: ["stages", i, "transitions", k, "effects"],
|
|
711
|
+
label: `transition "${t.name}"`,
|
|
507
712
|
sites
|
|
508
|
-
);
|
|
509
|
-
for (const [j,
|
|
510
|
-
|
|
713
|
+
});
|
|
714
|
+
for (const [j, activity] of (stage.activities ?? []).entries())
|
|
715
|
+
collectActivityConditionSites({ activity, path: ["stages", i, "activities", j], sites });
|
|
511
716
|
}
|
|
512
717
|
return sites;
|
|
513
718
|
}
|
|
514
|
-
function
|
|
719
|
+
function collectActivityConditionSites({
|
|
720
|
+
activity,
|
|
721
|
+
path,
|
|
722
|
+
sites
|
|
723
|
+
}) {
|
|
515
724
|
for (const field of ["filter", "completeWhen", "failWhen"]) {
|
|
516
|
-
const groq2 =
|
|
517
|
-
groq2 !== void 0 && sites.push({ groq: groq2, path: [...path, field], label: `
|
|
725
|
+
const groq2 = activity[field];
|
|
726
|
+
groq2 !== void 0 && sites.push({ groq: groq2, path: [...path, field], label: `activity "${activity.name}".${field}` });
|
|
518
727
|
}
|
|
519
|
-
collectBindingSites(
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
728
|
+
collectBindingSites({
|
|
729
|
+
effects: activity.effects,
|
|
730
|
+
path: [...path, "effects"],
|
|
731
|
+
label: `activity "${activity.name}"`,
|
|
732
|
+
sites
|
|
733
|
+
});
|
|
734
|
+
for (const [a, action] of (activity.actions ?? []).entries())
|
|
735
|
+
collectBindingSites({
|
|
736
|
+
effects: action.effects,
|
|
737
|
+
path: [...path, "actions", a, "effects"],
|
|
738
|
+
label: `action "${action.name}"`,
|
|
525
739
|
sites
|
|
526
|
-
);
|
|
527
|
-
collectSubworkflowSites(
|
|
528
|
-
}
|
|
529
|
-
function collectSubworkflowSites(
|
|
530
|
-
|
|
740
|
+
});
|
|
741
|
+
collectSubworkflowSites({ activity, path, sites });
|
|
742
|
+
}
|
|
743
|
+
function collectSubworkflowSites({
|
|
744
|
+
activity,
|
|
745
|
+
path,
|
|
746
|
+
sites
|
|
747
|
+
}) {
|
|
748
|
+
const sub = activity.subworkflows;
|
|
531
749
|
if (sub === void 0) return;
|
|
532
750
|
const subPath = [...path, "subworkflows"];
|
|
533
751
|
sites.push({
|
|
534
752
|
groq: sub.forEach,
|
|
535
753
|
path: [...subPath, "forEach"],
|
|
536
|
-
label: `
|
|
754
|
+
label: `activity "${activity.name}".subworkflows.forEach`
|
|
537
755
|
});
|
|
538
756
|
for (const [group, record] of [
|
|
539
757
|
["with", sub.with],
|
|
@@ -543,10 +761,15 @@ function collectSubworkflowSites(task, path, sites) {
|
|
|
543
761
|
sites.push({
|
|
544
762
|
groq: groq2,
|
|
545
763
|
path: [...subPath, group, key],
|
|
546
|
-
label: `
|
|
764
|
+
label: `activity "${activity.name}".subworkflows.${group} "${key}"`
|
|
547
765
|
});
|
|
548
766
|
}
|
|
549
|
-
function collectBindingSites(
|
|
767
|
+
function collectBindingSites({
|
|
768
|
+
effects,
|
|
769
|
+
path,
|
|
770
|
+
label,
|
|
771
|
+
sites
|
|
772
|
+
}) {
|
|
550
773
|
for (const [e, effect] of (effects ?? []).entries())
|
|
551
774
|
for (const [key, groq2] of Object.entries(effect.bindings ?? {}))
|
|
552
775
|
sites.push({
|
|
@@ -562,12 +785,12 @@ function checkAssigneesEntries(def, issues) {
|
|
|
562
785
|
message: "at most one assignees-kind field entry per scope \u2014 the inbox reads it by kind"
|
|
563
786
|
});
|
|
564
787
|
}
|
|
565
|
-
function
|
|
788
|
+
function activityShape(activity) {
|
|
566
789
|
return {
|
|
567
|
-
hasActions: (
|
|
568
|
-
hasEffects: (
|
|
569
|
-
hasCompleteWhen:
|
|
570
|
-
hasSubworkflows:
|
|
790
|
+
hasActions: (activity.actions ?? []).length > 0,
|
|
791
|
+
hasEffects: (activity.effects ?? []).length > 0,
|
|
792
|
+
hasCompleteWhen: activity.completeWhen !== void 0,
|
|
793
|
+
hasSubworkflows: activity.subworkflows !== void 0
|
|
571
794
|
};
|
|
572
795
|
}
|
|
573
796
|
const KIND_SHAPE_RULES = {
|
|
@@ -575,7 +798,7 @@ const KIND_SHAPE_RULES = {
|
|
|
575
798
|
service: (s) => s.hasEffects ? [] : ["needs at least one effect \u2014 the automated work it performs"],
|
|
576
799
|
manual: (s) => [
|
|
577
800
|
s.hasActions || s.hasCompleteWhen ? void 0 : "needs a way to complete \u2014 an action to acknowledge it, or a `completeWhen` predicate to verify it (otherwise it auto-resolves on activation)",
|
|
578
|
-
s.hasSubworkflows ? "is off-system work, not a fan-out \u2014 drop `subworkflows` or move it to its own
|
|
801
|
+
s.hasSubworkflows ? "is off-system work, not a fan-out \u2014 drop `subworkflows` or move it to its own activity" : void 0
|
|
579
802
|
].filter((m) => m !== void 0),
|
|
580
803
|
receive: (s) => [
|
|
581
804
|
s.hasCompleteWhen || s.hasSubworkflows ? void 0 : "needs a `completeWhen` (or `subworkflows`) condition to wait on",
|
|
@@ -587,53 +810,77 @@ const KIND_SHAPE_RULES = {
|
|
|
587
810
|
s.hasSubworkflows ? "resolves on activation, so it cannot fan out with `subworkflows`" : void 0
|
|
588
811
|
].filter((m) => m !== void 0)
|
|
589
812
|
};
|
|
590
|
-
function
|
|
813
|
+
function checkActivityKinds(def, issues) {
|
|
591
814
|
for (const [i, stage] of def.stages.entries())
|
|
592
|
-
for (const [j,
|
|
593
|
-
const path = ["stages", i, "
|
|
594
|
-
if (
|
|
815
|
+
for (const [j, activity] of (stage.activities ?? []).entries()) {
|
|
816
|
+
const path = ["stages", i, "activities", j];
|
|
817
|
+
if (activity.target !== void 0 && activity.kind !== "manual" && issues.push({
|
|
595
818
|
path: [...path, "target"],
|
|
596
|
-
message: `
|
|
597
|
-
}),
|
|
598
|
-
for (const fault of KIND_SHAPE_RULES[
|
|
819
|
+
message: `activity "${activity.name}" has a \`target\` but is not \`kind: "manual"\` \u2014 a target is the deep-link for off-system manual work`
|
|
820
|
+
}), activity.kind !== void 0)
|
|
821
|
+
for (const fault of KIND_SHAPE_RULES[activity.kind](activityShape(activity)))
|
|
599
822
|
issues.push({
|
|
600
823
|
path: [...path, "kind"],
|
|
601
|
-
message: `
|
|
824
|
+
message: `activity "${activity.name}" declares kind "${activity.kind}" but ${fault}`
|
|
602
825
|
});
|
|
603
826
|
}
|
|
604
827
|
}
|
|
605
828
|
function checkWorkflowInvariants(def) {
|
|
606
829
|
const issues = [], stageNames = checkStages(def, issues);
|
|
607
|
-
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),
|
|
830
|
+
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;
|
|
608
831
|
}
|
|
609
832
|
function defineWorkflow(definition) {
|
|
610
|
-
const label = labelFor("defineWorkflow", definition), parsed = parseOrThrow(AuthoringWorkflowSchema, definition, label), { definition: stored, issues: desugarIssues } = desugarWorkflow(parsed), issues = [...desugarIssues, ...checkWorkflowInvariants(stored)];
|
|
833
|
+
const label = labelFor("defineWorkflow", definition), parsed = parseOrThrow({ schema: AuthoringWorkflowSchema, input: definition, label }), { definition: stored, issues: desugarIssues } = desugarWorkflow(parsed), issues = [...desugarIssues, ...checkWorkflowInvariants(stored)];
|
|
611
834
|
if (issues.length > 0) throw new Error(formatValidationError(label, issues));
|
|
612
835
|
return stored;
|
|
613
836
|
}
|
|
614
837
|
function defineStage(stage) {
|
|
615
|
-
return parseOrThrow(
|
|
838
|
+
return parseOrThrow({
|
|
839
|
+
schema: AuthoringStageSchema,
|
|
840
|
+
input: stage,
|
|
841
|
+
label: labelFor("defineStage", stage)
|
|
842
|
+
});
|
|
616
843
|
}
|
|
617
|
-
function
|
|
618
|
-
return parseOrThrow(
|
|
844
|
+
function defineActivity(activity) {
|
|
845
|
+
return parseOrThrow({
|
|
846
|
+
schema: AuthoringActivitySchema,
|
|
847
|
+
input: activity,
|
|
848
|
+
label: labelFor("defineActivity", activity)
|
|
849
|
+
});
|
|
619
850
|
}
|
|
620
851
|
function defineAction(action) {
|
|
621
|
-
return parseOrThrow(
|
|
852
|
+
return parseOrThrow({
|
|
853
|
+
schema: AuthoringActionSchema,
|
|
854
|
+
input: action,
|
|
855
|
+
label: labelFor("defineAction", action)
|
|
856
|
+
});
|
|
622
857
|
}
|
|
623
858
|
function defineTransition(transition) {
|
|
624
|
-
return parseOrThrow(
|
|
859
|
+
return parseOrThrow({
|
|
860
|
+
schema: AuthoringTransitionSchema,
|
|
861
|
+
input: transition,
|
|
862
|
+
label: transitionLabel(transition)
|
|
863
|
+
});
|
|
625
864
|
}
|
|
626
865
|
function defineField(entry) {
|
|
627
|
-
return parseOrThrow(
|
|
866
|
+
return parseOrThrow({
|
|
867
|
+
schema: AuthoringFieldEntrySchema,
|
|
868
|
+
input: entry,
|
|
869
|
+
label: labelFor("defineField", entry)
|
|
870
|
+
});
|
|
628
871
|
}
|
|
629
872
|
function defineOp(op) {
|
|
630
|
-
return parseOrThrow(AuthoringOpSchema, op, "defineOp");
|
|
873
|
+
return parseOrThrow({ schema: AuthoringOpSchema, input: op, label: "defineOp" });
|
|
631
874
|
}
|
|
632
875
|
function defineGuard(guard) {
|
|
633
|
-
return parseOrThrow(GuardSchema, guard, labelFor("defineGuard", guard));
|
|
876
|
+
return parseOrThrow({ schema: GuardSchema, input: guard, label: labelFor("defineGuard", guard) });
|
|
634
877
|
}
|
|
635
878
|
function defineEffect(effect) {
|
|
636
|
-
return parseOrThrow(
|
|
879
|
+
return parseOrThrow({
|
|
880
|
+
schema: EffectSchema,
|
|
881
|
+
input: effect,
|
|
882
|
+
label: labelFor("defineEffect", effect)
|
|
883
|
+
});
|
|
637
884
|
}
|
|
638
885
|
function transitionLabel(transition) {
|
|
639
886
|
const { name, to } = transition;
|
|
@@ -643,7 +890,11 @@ function defineEffectDescriptor(descriptor) {
|
|
|
643
890
|
if (!descriptor.name) throw new Error("EffectDescriptor missing name");
|
|
644
891
|
return descriptor;
|
|
645
892
|
}
|
|
646
|
-
function parseOrThrow(
|
|
893
|
+
function parseOrThrow({
|
|
894
|
+
schema,
|
|
895
|
+
input,
|
|
896
|
+
label
|
|
897
|
+
}) {
|
|
647
898
|
const result = v.safeParse(schema, input);
|
|
648
899
|
if (!result.success)
|
|
649
900
|
throw new Error(formatValidationError(label, issuesFromValibot(result.issues)));
|
|
@@ -658,13 +909,13 @@ function labelFor(fn, value) {
|
|
|
658
909
|
}
|
|
659
910
|
export {
|
|
660
911
|
defineAction,
|
|
912
|
+
defineActivity,
|
|
661
913
|
defineEffect,
|
|
662
914
|
defineEffectDescriptor,
|
|
663
915
|
defineField,
|
|
664
916
|
defineGuard,
|
|
665
917
|
defineOp,
|
|
666
918
|
defineStage,
|
|
667
|
-
defineTask,
|
|
668
919
|
defineTransition,
|
|
669
920
|
defineWorkflow,
|
|
670
921
|
groq
|