@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/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, GuardSchema, EffectSchema } from "./_chunks-es/schema.js";
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
- editableSlotNames(workflowFields, stageFields, activities),
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
- role: authoring.role,
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(entries, path, ctx) {
83
- return !entries || entries.length === 0 ? entries === void 0 ? void 0 : [] : entries.map((entry, i) => desugarFieldEntry(entry, [...path, i], ctx));
84
- }
85
- function desugarFieldEntry(entry, path, ctx) {
86
- if (entry.type === "claim") return desugarClaimField(entry, path, ctx);
87
- if (entry.type === "todoList" || entry.type === "notes") return desugarListField(entry, path, ctx);
88
- const editable = normalizeEditable(entry.editable, [...path, "editable"], ctx);
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(entry, path, ctx) {
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(entry, path, ctx) {
111
- const editable = normalizeEditable(entry.editable, [...path, "editable"], ctx);
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(editable, path, ctx) {
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 slot to anyone in its scope, or list at least one role"
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 editableSlotNames(workflowFields, stageFields, activities) {
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(overrides, inScope, path, ctx) {
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 slot in scope \u2014 name a workflow/stage/activity slot of this stage that declares \`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(activity, path, stageEnv, ctx) {
168
- const activityFields = desugarFieldEntries(activity.fields, [...path, "fields"], ctx), env = {
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(activity.ops, [...path, "ops"], env, activity.name, ctx), actions = (activity.actions ?? []).map(
171
- (action, a) => desugarAction(action, [...path, "actions", a], env, activity.name, ctx)
172
- ), target = desugarTarget(activity.target, env, [...path, "target"], ctx);
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(target, env, path, ctx) {
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(action, path, env, activityName, ctx) {
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(action, path, env, ctx) {
219
- const ref = typeof action.field == "string" ? { field: action.field } : action.field, resolved = resolveRef(ref, env, [...path, "field"], ctx);
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(action.name, ref.field, resolved, env, [...path, "field"], ctx), entry && ctx.claimedFields.add(entry);
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(actionName, field, resolved, env, path, ctx) {
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(transition, path, stageEnv, ctx) {
259
- const ops = desugarOps(transition.ops, [...path, "ops"], stageEnv, void 0, ctx);
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 desugarOps(ops, path, env, firingActivity, ctx) {
273
- if (ops)
274
- return ops.map((op, i) => desugarOp(op, [...path, i], env, firingActivity, ctx));
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 desugarOp(op, path, env, firingActivity, ctx) {
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(op, path, env, ctx) {
290
- const target = resolveRef(op.target, env, [...path, "target"], ctx) ?? fallbackRef(op.target);
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(ref, env, path, ctx) {
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 RESERVED_CONDITION_VARS = [
339
- "fields",
340
- "actor",
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(names, what, issues) {
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(def, i, activityNames, issues) {
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(activity, activityNames, path, issues) {
405
- checkStatusSetOps(activity.ops, activityNames, [...path, "ops"], issues);
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(action.ops, activityNames, [...path, "actions", a, "ops"], issues);
531
+ checkStatusSetOps({
532
+ ops: action.ops,
533
+ activityNames,
534
+ path: [...path, "actions", a, "ops"],
535
+ issues
536
+ });
408
537
  }
409
- function checkStatusSetOps(ops, activityNames, path, issues) {
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(def, stageNames, issues) {
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(def, stageNames, issues) {
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(activity.effects, ["stages", i, "activities", j, "effects"], sites);
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(sites, "effect name (registry key \u2014 unique per definition)", issues);
591
+ checkDuplicates({
592
+ names: sites,
593
+ what: "effect name (registry key \u2014 unique per definition)",
594
+ issues
595
+ });
446
596
  }
447
- function collectEffects(effects, path, sites) {
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(name, groq2, names, issues) {
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 collectActivityConditionSites(activity, path, sites) {
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(activity.effects, [...path, "effects"], `activity "${activity.name}"`, sites);
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
- collectSubworkflowSites(activity, path, sites);
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(activity, path, sites) {
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(effects, path, label, sites) {
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(AuthoringStageSchema, stage, labelFor("defineStage", stage));
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(AuthoringActivitySchema, activity, labelFor("defineActivity", activity));
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(AuthoringActionSchema, action, labelFor("defineAction", action));
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(AuthoringTransitionSchema, transition, transitionLabel(transition));
892
+ return parseOrThrow({
893
+ schema: AuthoringTransitionSchema,
894
+ input: transition,
895
+ label: transitionLabel(transition)
896
+ });
673
897
  }
674
898
  function defineField(entry) {
675
- return parseOrThrow(AuthoringFieldEntrySchema, entry, labelFor("defineField", entry));
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(GuardSchema, guard, labelFor("defineGuard", guard));
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(EffectSchema, effect, labelFor("defineEffect", effect));
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(schema, input, label) {
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