@sanity/workflow-engine 0.14.0 → 0.16.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.
@@ -0,0 +1,3208 @@
1
+ "use strict";
2
+
3
+ var v = require("valibot"), groqConditionDescribe = require("@sanity/groq-condition-describe"), groqJs = require("groq-js");
4
+
5
+ function _interopNamespaceCompat(e) {
6
+ if (e && typeof e == "object" && "default" in e) return e;
7
+ var n = /* @__PURE__ */ Object.create(null);
8
+ return e && Object.keys(e).forEach(function(k) {
9
+ if (k !== "default") {
10
+ var d = Object.getOwnPropertyDescriptor(e, k);
11
+ Object.defineProperty(n, k, d.get ? d : {
12
+ enumerable: !0,
13
+ get: function() {
14
+ return e[k];
15
+ }
16
+ });
17
+ }
18
+ }), n.default = e, Object.freeze(n);
19
+ }
20
+
21
+ var v__namespace = /* @__PURE__ */ _interopNamespaceCompat(v);
22
+
23
+ function isCascadeFired(action) {
24
+ return action.when !== void 0;
25
+ }
26
+
27
+ function deriveActivityKind(activity) {
28
+ if (activity.target !== void 0) return "manual";
29
+ const actions = activity.actions ?? [];
30
+ return actions.some(a => !isCascadeFired(a)) ? "user" : actions.some(a => (a.effects ?? []).length > 0 || a.spawn !== void 0) ? "service" : actions.length > 0 ? "receive" : "script";
31
+ }
32
+
33
+ function deriveExecutorClassification(activity) {
34
+ if (activity.target !== void 0) return "off-system";
35
+ const actions = activity.actions ?? [], cascadeFired = actions.filter(isCascadeFired).length;
36
+ return cascadeFired === actions.length ? "autonomous" : cascadeFired === 0 ? "interactive" : "hybrid";
37
+ }
38
+
39
+ function driverKind(actor) {
40
+ return actor.kind === "person" || actor.kind === "agent" ? actor.kind : "service";
41
+ }
42
+
43
+ function errorMessage(err) {
44
+ return (err instanceof Error ? err.message : String(err)).replace(/[^\P{Cc}\n\t]/gu, "");
45
+ }
46
+
47
+ function rethrowWithContext(err, context) {
48
+ throw new Error(`${context}: ${errorMessage(err)}`, {
49
+ cause: err
50
+ });
51
+ }
52
+
53
+ function andConditions(parts) {
54
+ const present = parts.filter(p => p !== void 0);
55
+ if (present.length !== 0) return present.length === 1 ? present[0] : present.map(p => `(${p})`).join(" && ");
56
+ }
57
+
58
+ const KNOWN_SCHEMES = /* @__PURE__ */ new Set([ "dataset", "canvas", "media-library", "dashboard" ]), KNOWN_SCHEMES_TEXT = [ ...KNOWN_SCHEMES ].join(", ");
59
+
60
+ function parseGdr(uri) {
61
+ const colon = uri.indexOf(":");
62
+ if (colon < 0) throw new Error(`Invalid GDR "${uri}": must be a URI of form "<scheme>:<...id-parts>". Known schemes: ${KNOWN_SCHEMES_TEXT}.`);
63
+ const scheme = uri.slice(0, colon), rest = uri.slice(colon + 1);
64
+ if (!KNOWN_SCHEMES.has(scheme)) throw new Error(`Invalid GDR "${uri}": unknown scheme "${scheme}". Known: ${KNOWN_SCHEMES_TEXT}.`);
65
+ const parts = rest.split(":");
66
+ if (parts.some(part => part.length === 0)) throw new Error(`Invalid GDR "${uri}": id parts must be non-empty (no leading, trailing, or doubled ":").`);
67
+ if (scheme === "dataset") {
68
+ if (parts.length !== 3) throw new Error(`Invalid GDR "${uri}": dataset scheme requires <projectId>:<dataset>:<documentId> (3 parts after scheme); got ${parts.length}.`);
69
+ return {
70
+ scheme: "dataset",
71
+ projectId: parts[0],
72
+ dataset: parts[1],
73
+ documentId: parts[2]
74
+ };
75
+ }
76
+ if (parts.length !== 2) throw new Error(`Invalid GDR "${uri}": ${scheme} scheme requires <resourceId>:<documentId> (2 parts after scheme); got ${parts.length}.`);
77
+ return {
78
+ scheme: scheme,
79
+ resourceId: parts[0],
80
+ documentId: parts[1]
81
+ };
82
+ }
83
+
84
+ function tryParseGdr(uri) {
85
+ try {
86
+ return parseGdr(uri);
87
+ } catch {
88
+ return;
89
+ }
90
+ }
91
+
92
+ function gdrUri(parts) {
93
+ return parts.scheme === "dataset" ? `dataset:${parts.projectId}:${parts.dataset}:${parts.documentId}` : `${parts.scheme}:${parts.resourceId}:${parts.documentId}`;
94
+ }
95
+
96
+ function extractDocumentId(gdrUriString) {
97
+ return parseGdr(gdrUriString).documentId;
98
+ }
99
+
100
+ const RESOURCE_ALIAS_NAME_SOURCE = "[a-z0-9][a-z0-9-]*", RESOURCE_ALIAS_NAME_RE = new RegExp(`^${RESOURCE_ALIAS_NAME_SOURCE}$`);
101
+
102
+ function validateResourceAliasName(name) {
103
+ if (!RESOURCE_ALIAS_NAME_RE.test(name)) throw new Error(`Invalid resource alias name "${name}": expected lowercase letters, digits, and dashes (no leading dash).`);
104
+ }
105
+
106
+ function toPhysicalGdr(id, home) {
107
+ return isGdrUri(id) ? id : gdrFromResource(home, id);
108
+ }
109
+
110
+ function refDataset({projectId: projectId, dataset: dataset, documentId: documentId, type: type}) {
111
+ return {
112
+ id: gdrUri({
113
+ scheme: "dataset",
114
+ projectId: projectId,
115
+ dataset: dataset,
116
+ documentId: documentId
117
+ }),
118
+ type: type
119
+ };
120
+ }
121
+
122
+ function refCanvas({resourceId: resourceId, documentId: documentId, type: type}) {
123
+ return {
124
+ id: gdrUri({
125
+ scheme: "canvas",
126
+ resourceId: resourceId,
127
+ documentId: documentId
128
+ }),
129
+ type: type
130
+ };
131
+ }
132
+
133
+ function refMediaLibrary({resourceId: resourceId, documentId: documentId, type: type}) {
134
+ return {
135
+ id: gdrUri({
136
+ scheme: "media-library",
137
+ resourceId: resourceId,
138
+ documentId: documentId
139
+ }),
140
+ type: type
141
+ };
142
+ }
143
+
144
+ function refDashboard({resourceId: resourceId, documentId: documentId, type: type}) {
145
+ return {
146
+ id: gdrUri({
147
+ scheme: "dashboard",
148
+ resourceId: resourceId,
149
+ documentId: documentId
150
+ }),
151
+ type: type
152
+ };
153
+ }
154
+
155
+ function datasetResourceParts(id) {
156
+ const dot = id.indexOf(".");
157
+ if (dot <= 0 || dot === id.length - 1) throw new Error(`Invalid dataset resource id "${id}": expected "<projectId>.<dataset>".`);
158
+ return {
159
+ projectId: id.slice(0, dot),
160
+ dataset: id.slice(dot + 1)
161
+ };
162
+ }
163
+
164
+ function clientConfigFromResource(res) {
165
+ return res.type === "dataset" ? datasetResourceParts(res.id) : {
166
+ resource: res
167
+ };
168
+ }
169
+
170
+ function gdrFromResource(res, documentId) {
171
+ if (res.type === "dataset") {
172
+ const {projectId: projectId, dataset: dataset} = datasetResourceParts(res.id);
173
+ return gdrUri({
174
+ scheme: "dataset",
175
+ projectId: projectId,
176
+ dataset: dataset,
177
+ documentId: documentId
178
+ });
179
+ }
180
+ return gdrUri({
181
+ scheme: res.type,
182
+ resourceId: res.id,
183
+ documentId: documentId
184
+ });
185
+ }
186
+
187
+ function resourceGdr(res) {
188
+ return `${res.type}:${res.id}`;
189
+ }
190
+
191
+ function parseResourceGdr(uri) {
192
+ const expected = 'expected "<type>:<id>" with no document part, e.g. "dataset:<projectId>.<dataset>" or "media-library:<resourceId>"', parts = uri.split(":");
193
+ if (parts.length !== 2 || parts.some(part => part.length === 0)) throw new Error(`Invalid resource GDR "${uri}": ${expected}.`);
194
+ const [type, id] = parts;
195
+ if (!KNOWN_SCHEMES.has(type)) throw new Error(`Invalid resource GDR "${uri}": unknown resource type "${type}". Known: ${KNOWN_SCHEMES_TEXT}.`);
196
+ return type === "dataset" && datasetResourceParts(id), {
197
+ type: type,
198
+ id: id
199
+ };
200
+ }
201
+
202
+ function gdrResourcePrefix(res) {
203
+ const full = gdrFromResource(res, "x");
204
+ return full.slice(0, full.length - 1);
205
+ }
206
+
207
+ function resourceFromParsed(parsed) {
208
+ return parsed.scheme === "dataset" ? {
209
+ type: "dataset",
210
+ id: `${parsed.projectId}.${parsed.dataset}`
211
+ } : {
212
+ type: parsed.scheme,
213
+ id: parsed.resourceId ?? ""
214
+ };
215
+ }
216
+
217
+ function resourceFromGdrUri(uri) {
218
+ const parsed = tryParseGdr(uri);
219
+ return parsed === void 0 ? void 0 : resourceFromParsed(parsed);
220
+ }
221
+
222
+ function sameResource(a, b) {
223
+ return a.type === b.type && a.id === b.id;
224
+ }
225
+
226
+ function selfGdr(doc) {
227
+ return gdrFromResource(doc.workflowResource, doc._id);
228
+ }
229
+
230
+ function definitionDocId({tag: tag, definition: definition, version: version}) {
231
+ return `${tag}.${definition}.v${version}`;
232
+ }
233
+
234
+ function isGdrUri(value) {
235
+ return typeof value == "string" && tryParseGdr(value) !== void 0;
236
+ }
237
+
238
+ function toBareId(id) {
239
+ return isGdrUri(id) ? extractDocumentId(id) : id;
240
+ }
241
+
242
+ function gdrRef({res: res, documentId: documentId, type: type}) {
243
+ return {
244
+ id: gdrFromResource(res, documentId),
245
+ type: type
246
+ };
247
+ }
248
+
249
+ function isGdr(value) {
250
+ return typeof value == "object" && value !== null && typeof value.id == "string" && typeof value.type == "string";
251
+ }
252
+
253
+ class WorkflowError extends Error {
254
+ kind;
255
+ constructor(kind, message, options) {
256
+ super(message, options), this.kind = kind;
257
+ }
258
+ }
259
+
260
+ class ContractViolationError extends WorkflowError {
261
+ constructor(message) {
262
+ super("contract-violation", message), this.name = "ContractViolationError";
263
+ }
264
+ }
265
+
266
+ class InstanceNotFoundError extends WorkflowError {
267
+ instanceId;
268
+ constructor(args) {
269
+ super("instance-not-found", `Workflow instance ${args.instanceId} not found${args.detail ? ` (${args.detail})` : ""}`),
270
+ this.name = "InstanceNotFoundError", this.instanceId = args.instanceId;
271
+ }
272
+ }
273
+
274
+ class DefinitionNotFoundError extends WorkflowError {
275
+ definition;
276
+ version;
277
+ constructor(args) {
278
+ super("definition-not-found", args.version !== void 0 ? `Workflow definition ${args.definition} v${args.version} not deployed` : `Workflow definition ${args.definition} has no deployed versions`),
279
+ this.name = "DefinitionNotFoundError", this.definition = args.definition, args.version !== void 0 && (this.version = args.version);
280
+ }
281
+ }
282
+
283
+ class DefinitionInUseError extends WorkflowError {
284
+ definition;
285
+ blockedBy;
286
+ constructor(args) {
287
+ super("definition-in-use", definitionInUseMessage(args.definition, args.blockedBy)),
288
+ this.name = "DefinitionInUseError", this.definition = args.definition, this.blockedBy = args.blockedBy;
289
+ }
290
+ }
291
+
292
+ function definitionInUseMessage(definition, blockedBy) {
293
+ if (blockedBy.reason === "non-terminal-instances") {
294
+ const head = blockedBy.instanceIds.slice(0, 3).join(", "), preview = blockedBy.instanceIds.length > 3 ? `${head}, …` : head;
295
+ return `Cannot delete ${definition}: ${blockedBy.instanceIds.length} non-terminal instance(s) exist (${preview}). Pass cascade to abort them first — instances are aborted in place, never deleted.`;
296
+ }
297
+ const names = blockedBy.referrers.map(r => `${r.definition} v${r.version}`).join(", ");
298
+ return `Cannot delete ${definition}: still spawn-referenced by deployed definition(s) ${names}. Delete or redeploy the referrer(s) first.`;
299
+ }
300
+
301
+ class EffectNotFoundError extends WorkflowError {
302
+ instanceId;
303
+ effectKey;
304
+ settled;
305
+ constructor(args) {
306
+ super("effect-not-found", effectNotFoundMessage(args)), this.name = "EffectNotFoundError",
307
+ this.instanceId = args.instanceId, this.effectKey = args.effectKey, args.settled !== void 0 && (this.settled = args.settled);
308
+ }
309
+ }
310
+
311
+ function effectNotFoundMessage(args) {
312
+ const base = `Pending effect "${args.effectKey}" not found on instance ${args.instanceId}`;
313
+ if (args.settled === void 0) return base;
314
+ const cause = args.settled.detail !== void 0 ? ` (${args.settled.detail})` : "";
315
+ return args.settled.status === "cancelled" ? `${base} — it was cancelled at ${args.settled.ranAt}${cause}` : `${base} — it already settled "${args.settled.status}" at ${args.settled.ranAt}${cause}`;
316
+ }
317
+
318
+ const TAG_RE = /^[a-z0-9][a-z0-9-]*$/;
319
+
320
+ function validateTag(tag) {
321
+ if (!TAG_RE.test(tag)) throw new ContractViolationError(`tag: invalid tag "${tag}" — must match ${TAG_RE.source} (ASCII lowercase + digits + dashes, no leading dash, no dots)`);
322
+ }
323
+
324
+ function tagScopeFilter() {
325
+ return "tag == $tag";
326
+ }
327
+
328
+ const NonEmptyString = v__namespace.pipe(v__namespace.string(), v__namespace.nonEmpty("must not be empty"));
329
+
330
+ function asPredicate(validate) {
331
+ return value => {
332
+ try {
333
+ return validate(value), !0;
334
+ } catch {
335
+ return !1;
336
+ }
337
+ };
338
+ }
339
+
340
+ const isValidTag = asPredicate(validateTag), isValidAliasName = asPredicate(validateResourceAliasName), isValidDatasetId = asPredicate(datasetResourceParts), WorkflowResourceSchema = v__namespace.variant("type", [ v__namespace.object({
341
+ type: v__namespace.literal("dataset"),
342
+ id: v__namespace.pipe(NonEmptyString, v__namespace.check(isValidDatasetId, 'invalid dataset resource id — expected "<projectId>.<dataset>"'))
343
+ }), v__namespace.object({
344
+ type: v__namespace.literal("canvas"),
345
+ id: NonEmptyString
346
+ }), v__namespace.object({
347
+ type: v__namespace.literal("media-library"),
348
+ id: NonEmptyString
349
+ }), v__namespace.object({
350
+ type: v__namespace.literal("dashboard"),
351
+ id: NonEmptyString
352
+ }) ]), ResourceBindingSchema = v__namespace.object({
353
+ name: v__namespace.pipe(NonEmptyString, v__namespace.check(isValidAliasName, "invalid resource handle name — lowercase letters, digits and dashes only, no leading dash")),
354
+ resource: WorkflowResourceSchema
355
+ }), DefinitionSchema = v__namespace.custom(input => typeof input == "object" && input !== null && typeof input.name == "string", "expected a workflow definition (an object with a string `name`)"), DeploymentSchema = v__namespace.object({
356
+ name: NonEmptyString,
357
+ tag: v__namespace.pipe(v__namespace.string(), v__namespace.nonEmpty(), v__namespace.check(isValidTag, "invalid tag — lowercase letters, digits and dashes only, no leading dash, no dots")),
358
+ workflowResource: WorkflowResourceSchema,
359
+ resourceAliases: v__namespace.optional(v__namespace.pipe(v__namespace.array(ResourceBindingSchema), v__namespace.check(bindings => new Set(bindings.map(binding => binding.name)).size === bindings.length, "duplicate resource handle name — each binding name must be unique within a deployment"))),
360
+ definitions: v__namespace.pipe(v__namespace.array(DefinitionSchema), v__namespace.minLength(1, "a deployment needs at least one definition"))
361
+ }), TelemetryLoggerSchema = v__namespace.custom(input => typeof input == "object" && input !== null && typeof input.log == "function", "expected a telemetry logger (an object with a `log` function)"), WorkflowConfigSchema = v__namespace.object({
362
+ deployments: v__namespace.pipe(v__namespace.array(DeploymentSchema), v__namespace.minLength(1, "a config needs at least one deployment"), v__namespace.check(deployments => new Set(deployments.map(deployment => deployment.tag)).size === deployments.length, "duplicate deployment tag — each deployment must use a unique tag")),
363
+ telemetry: v__namespace.optional(TelemetryLoggerSchema)
364
+ });
365
+
366
+ function resourceAliasesToMap(resourceAliases) {
367
+ return Object.fromEntries((resourceAliases ?? []).map(binding => [ binding.name, binding.resource ]));
368
+ }
369
+
370
+ const FIELD_READ = /^\$fields\.(\w+)(?:\.(.+))?$/, EFFECTS_READ = /^\$effects\['([^']+)'\](?:\.(.+))?$/;
371
+
372
+ function isGuardReadExpr(expr) {
373
+ return expr === "$self" || expr === "$now" || FIELD_READ.test(expr) || EFFECTS_READ.test(expr);
374
+ }
375
+
376
+ function printGuardRead(read) {
377
+ switch (read.type) {
378
+ case "self":
379
+ return "$self";
380
+
381
+ case "now":
382
+ return "$now";
383
+
384
+ case "fieldRead":
385
+ return read.path === void 0 ? `$fields.${read.field}` : `$fields.${read.field}.${read.path}`;
386
+
387
+ case "effectsRead":
388
+ return read.path === void 0 ? `$effects['${read.effect}']` : `$effects['${read.effect}'].${read.path}`;
389
+ }
390
+ }
391
+
392
+ const UNIVERSAL_ROLE_ALIAS_KEY = "$all";
393
+
394
+ function normalizeRoleAliases(aliases) {
395
+ if (aliases === void 0 || !("*" in aliases)) return aliases;
396
+ const {["*"]: universal, ...perRole} = aliases;
397
+ return {
398
+ ...perRole,
399
+ [UNIVERSAL_ROLE_ALIAS_KEY]: universal
400
+ };
401
+ }
402
+
403
+ function expandRequiredRoles(required, aliases) {
404
+ if (aliases === void 0) return [ ...required ];
405
+ const out = /* @__PURE__ */ new Set;
406
+ for (const role of required) {
407
+ out.add(role);
408
+ for (const fulfiller of aliases[role] ?? []) out.add(fulfiller);
409
+ }
410
+ for (const fulfiller of aliases[UNIVERSAL_ROLE_ALIAS_KEY] ?? []) out.add(fulfiller);
411
+ return [ ...out ];
412
+ }
413
+
414
+ function actorFulfillsRole({actorRoles: actorRoles, required: required, aliases: aliases}) {
415
+ if (actorRoles === void 0 || actorRoles.length === 0) return !1;
416
+ const accepted = new Set(expandRequiredRoles([ required ], aliases));
417
+ return actorRoles.some(role => accepted.has(role));
418
+ }
419
+
420
+ function groq(strings, ...values) {
421
+ return strings.reduce((out, part, i) => i === 0 ? part : `${out}${serializeGroqValue(values[i - 1])}${part}`, "");
422
+ }
423
+
424
+ function serializeGroqValue(value) {
425
+ const serialized = JSON.stringify(value);
426
+ if (serialized === void 0) throw new Error(`groq tag cannot serialize ${typeof value} — interpolate JSON-representable values only`);
427
+ return serialized;
428
+ }
429
+
430
+ function desugarWorkflow(authoring) {
431
+ const issues = [];
432
+ checkReservedRoleAliasKeys(authoring.roleAliases, issues);
433
+ const roleAliases = normalizeRoleAliases(authoring.roleAliases), ctx = {
434
+ issues: issues,
435
+ claimFields: /* @__PURE__ */ new Map,
436
+ claimedFields: /* @__PURE__ */ new Set,
437
+ roleAliases: roleAliases
438
+ }, workflowFields2 = desugarFieldEntries({
439
+ entries: authoring.fields,
440
+ path: [ "fields" ],
441
+ ctx: ctx
442
+ }), workflowLayer = layerOf(workflowFields2), stages = authoring.stages.map((stage, i) => {
443
+ const path = [ "stages", i ], stageFields2 = desugarFieldEntries({
444
+ entries: stage.fields,
445
+ path: [ ...path, "fields" ],
446
+ ctx: ctx
447
+ }), stageEnv = {
448
+ layers: [ {
449
+ scope: "stage",
450
+ entries: layerOf(stageFields2)
451
+ }, {
452
+ scope: "workflow",
453
+ entries: workflowLayer
454
+ } ]
455
+ }, activities = (stage.activities ?? []).map((activity, j) => desugarActivity({
456
+ activity: activity,
457
+ path: [ ...path, "activities", j ],
458
+ stageEnv: stageEnv,
459
+ ctx: ctx
460
+ })), transitions = (stage.transitions ?? []).map(transition => desugarTransition({
461
+ transition: transition
462
+ })), editable = desugarStageEditable({
463
+ overrides: stage.editable,
464
+ inScope: editableFieldNames({
465
+ workflowFields: workflowFields2,
466
+ stageFields: stageFields2,
467
+ activities: activities
468
+ }),
469
+ path: [ ...path, "editable" ],
470
+ ctx: ctx
471
+ });
472
+ return {
473
+ ...stripUndefined({
474
+ name: stage.name,
475
+ title: stage.title,
476
+ description: stage.description,
477
+ groups: stage.groups,
478
+ guards: stage.guards?.map(desugarGuard)
479
+ }),
480
+ ...stageFields2 ? {
481
+ fields: stageFields2
482
+ } : {},
483
+ ...activities.length > 0 ? {
484
+ activities: activities
485
+ } : {},
486
+ ...transitions.length > 0 ? {
487
+ transitions: transitions
488
+ } : {},
489
+ ...editable ? {
490
+ editable: editable
491
+ } : {}
492
+ };
493
+ });
494
+ return checkUnclaimedClaimFields(ctx), {
495
+ definition: {
496
+ ...stripUndefined({
497
+ name: authoring.name,
498
+ title: authoring.title,
499
+ description: authoring.description,
500
+ groups: authoring.groups,
501
+ lifecycle: authoring.lifecycle,
502
+ start: desugarStart(authoring.start),
503
+ initialStage: authoring.initialStage,
504
+ predicates: authoring.predicates,
505
+ roleAliases: roleAliases
506
+ }),
507
+ ...workflowFields2 ? {
508
+ fields: workflowFields2
509
+ } : {},
510
+ stages: stages
511
+ },
512
+ issues: ctx.issues
513
+ };
514
+ }
515
+
516
+ function desugarStart(start) {
517
+ if (start !== void 0) return {
518
+ kind: start.kind ?? "interactive",
519
+ ...start.filter !== void 0 ? {
520
+ filter: start.filter
521
+ } : {}
522
+ };
523
+ }
524
+
525
+ function checkReservedRoleAliasKeys(aliases, issues) {
526
+ for (const key of Object.keys(aliases ?? {})) key.startsWith("$") && issues.push({
527
+ path: [ "roleAliases", key ],
528
+ message: `role alias key "${key}" uses the reserved "$" prefix — that namespace is the engine's stored spelling for the universal fulfiller. Use "*" to mean "fulfills any gate", or rename the role.`
529
+ });
530
+ }
531
+
532
+ const TODOLIST_OF = [ {
533
+ type: "string",
534
+ name: "label",
535
+ title: "Label"
536
+ }, {
537
+ type: "string",
538
+ name: "status",
539
+ title: "Status"
540
+ }, {
541
+ type: "assignee",
542
+ name: "assignee",
543
+ title: "Assignee"
544
+ }, {
545
+ type: "date",
546
+ name: "dueDate",
547
+ title: "Due date"
548
+ } ], NOTES_OF = [ {
549
+ type: "text",
550
+ name: "body",
551
+ title: "Body"
552
+ }, {
553
+ type: "actor",
554
+ name: "actor",
555
+ title: "Actor"
556
+ }, {
557
+ type: "datetime",
558
+ name: "at",
559
+ title: "At"
560
+ } ];
561
+
562
+ function desugarFieldEntries({entries: entries, path: path, ctx: ctx}) {
563
+ return !entries || entries.length === 0 ? entries === void 0 ? void 0 : [] : entries.map((entry, i) => desugarFieldEntry({
564
+ entry: entry,
565
+ path: [ ...path, i ],
566
+ ctx: ctx
567
+ }));
568
+ }
569
+
570
+ function desugarFieldEntry({entry: entry, path: path, ctx: ctx}) {
571
+ if (entry.type === "claim") return desugarClaimField({
572
+ entry: entry,
573
+ path: path,
574
+ ctx: ctx
575
+ });
576
+ if (entry.type === "todoList" || entry.type === "notes") return desugarListField({
577
+ entry: entry,
578
+ path: path,
579
+ ctx: ctx
580
+ });
581
+ const editable = normalizeEditable({
582
+ editable: entry.editable,
583
+ path: [ ...path, "editable" ],
584
+ ctx: ctx
585
+ });
586
+ return {
587
+ ...stripUndefined({
588
+ type: entry.type,
589
+ name: entry.name,
590
+ title: entry.title,
591
+ description: entry.description,
592
+ group: normalizeGroup(entry.group),
593
+ required: entry.required,
594
+ initialValue: entry.initialValue,
595
+ editable: editable,
596
+ types: entry.types,
597
+ fields: entry.fields,
598
+ of: entry.of
599
+ })
600
+ };
601
+ }
602
+
603
+ function desugarClaimField({entry: entry, path: path, ctx: ctx}) {
604
+ const desugared = {
605
+ ...stripUndefined({
606
+ name: entry.name,
607
+ title: entry.title,
608
+ description: entry.description,
609
+ group: normalizeGroup(entry.group)
610
+ }),
611
+ type: "actor"
612
+ };
613
+ return ctx.claimFields.set(desugared, {
614
+ name: entry.name,
615
+ path: path
616
+ }), desugared;
617
+ }
618
+
619
+ function desugarListField({entry: entry, path: path, ctx: ctx}) {
620
+ const editable = normalizeEditable({
621
+ editable: entry.editable,
622
+ path: [ ...path, "editable" ],
623
+ ctx: ctx
624
+ });
625
+ return {
626
+ ...stripUndefined({
627
+ name: entry.name,
628
+ title: entry.title,
629
+ description: entry.description,
630
+ group: normalizeGroup(entry.group),
631
+ required: entry.required,
632
+ initialValue: entry.initialValue,
633
+ editable: editable
634
+ }),
635
+ type: "array",
636
+ of: entry.type === "todoList" ? TODOLIST_OF : NOTES_OF
637
+ };
638
+ }
639
+
640
+ function normalizeEditable({editable: editable, path: path, ctx: ctx}) {
641
+ if (editable === void 0 || editable === !0) return editable;
642
+ if (Array.isArray(editable)) {
643
+ const condition = rolesCondition(editable, ctx.roleAliases);
644
+ if (condition === void 0) {
645
+ ctx.issues.push({
646
+ path: path,
647
+ message: "editable: [] names no roles — use `true` to open the field to anyone in its scope, or list at least one role"
648
+ });
649
+ return;
650
+ }
651
+ return condition;
652
+ }
653
+ return editable;
654
+ }
655
+
656
+ function editableFieldNames({workflowFields: workflowFields2, stageFields: stageFields2, activities: activities}) {
657
+ const names = /* @__PURE__ */ new Set, collect = entries => {
658
+ for (const entry of entries ?? []) entry.editable !== void 0 && names.add(entry.name);
659
+ };
660
+ collect(workflowFields2), collect(stageFields2);
661
+ for (const activity of activities) collect(activity.fields);
662
+ return names;
663
+ }
664
+
665
+ function desugarStageEditable({overrides: overrides, inScope: inScope, path: path, ctx: ctx}) {
666
+ if (overrides === void 0) return;
667
+ const out = {};
668
+ for (const [name, value] of Object.entries(overrides)) {
669
+ if (!inScope.has(name)) {
670
+ ctx.issues.push({
671
+ path: [ ...path, name ],
672
+ message: `stage editable override "${name}" does not narrow an editable field in scope — name a workflow/stage/activity field of this stage that declares \`editable\``
673
+ });
674
+ continue;
675
+ }
676
+ const normalized = normalizeEditable({
677
+ editable: value,
678
+ path: [ ...path, name ],
679
+ ctx: ctx
680
+ });
681
+ normalized !== void 0 && (out[name] = normalized);
682
+ }
683
+ return Object.keys(out).length > 0 ? out : void 0;
684
+ }
685
+
686
+ function layerOf(entries) {
687
+ return new Map((entries ?? []).map(entry => [ entry.name, entry ]));
688
+ }
689
+
690
+ function normalizeGroup(group) {
691
+ return group === void 0 || Array.isArray(group) ? group : [ group ];
692
+ }
693
+
694
+ function desugarActivity({activity: activity, path: path, stageEnv: stageEnv, ctx: ctx}) {
695
+ const activityFields2 = desugarFieldEntries({
696
+ entries: activity.fields,
697
+ path: [ ...path, "fields" ],
698
+ ctx: ctx
699
+ }), env = {
700
+ layers: [ {
701
+ scope: "activity",
702
+ entries: layerOf(activityFields2)
703
+ }, ...stageEnv.layers ]
704
+ }, actions = (activity.actions ?? []).map((action, a) => desugarAction({
705
+ action: action,
706
+ path: [ ...path, "actions", a ],
707
+ env: env,
708
+ activityName: activity.name,
709
+ ctx: ctx
710
+ })), target = desugarTarget({
711
+ target: activity.target,
712
+ env: env,
713
+ path: [ ...path, "target" ],
714
+ ctx: ctx
715
+ });
716
+ return {
717
+ ...stripUndefined({
718
+ name: activity.name,
719
+ title: activity.title,
720
+ description: activity.description,
721
+ groups: activity.groups,
722
+ group: normalizeGroup(activity.group),
723
+ filter: activity.filter,
724
+ requirements: activity.requirements
725
+ }),
726
+ ...target ? {
727
+ target: target
728
+ } : {},
729
+ ...activityFields2 ? {
730
+ fields: activityFields2
731
+ } : {},
732
+ ...actions.length > 0 ? {
733
+ actions: actions
734
+ } : {}
735
+ };
736
+ }
737
+
738
+ const TARGET_DOC_KINDS = [ "doc.ref", "doc.refs", "release.ref" ];
739
+
740
+ function desugarTarget({target: target, env: env, path: path, ctx: ctx}) {
741
+ if (target === void 0 || target.type === "url") return target;
742
+ const ref = typeof target.field == "string" ? {
743
+ field: target.field
744
+ } : target.field, field = resolveRef({
745
+ ref: ref,
746
+ env: env,
747
+ path: [ ...path, "field" ],
748
+ ctx: ctx
749
+ }) ?? fallbackRef(ref), entry = entryAt(env, field);
750
+ return entry && !TARGET_DOC_KINDS.includes(entry.type) && ctx.issues.push({
751
+ path: [ ...path, "field" ],
752
+ message: `manual activity target references "${field.field}" of kind "${entry.type}" — a deep-link target needs a document-valued entry (${TARGET_DOC_KINDS.join(", ")})`
753
+ }), {
754
+ type: "field",
755
+ field: field
756
+ };
757
+ }
758
+
759
+ function desugarAction({action: action, path: path, env: env, activityName: activityName, ctx: ctx}) {
760
+ if ("type" in action) return desugarClaimAction({
761
+ action: action,
762
+ path: path,
763
+ env: env,
764
+ ctx: ctx
765
+ });
766
+ action.roles !== void 0 && action.roles.length === 0 && ctx.issues.push({
767
+ path: [ ...path, "roles" ],
768
+ message: "roles: [] names no roles — omit it to allow any identity, or list at least one role"
769
+ });
770
+ const cascadeFired = isCascadeFired(action), filter = cascadeFired ? action.filter : andConditions([ rolesCondition(action.roles, ctx.roleAliases), action.filter ]), ops = desugarOps({
771
+ ops: action.ops,
772
+ path: [ ...path, "ops" ],
773
+ env: env,
774
+ firingActivity: activityName,
775
+ ctx: ctx
776
+ }) ?? [];
777
+ return action.status !== void 0 && ops.push({
778
+ type: "status.set",
779
+ activity: activityName,
780
+ status: action.status
781
+ }), {
782
+ ...stripUndefined({
783
+ name: action.name,
784
+ title: action.title,
785
+ description: action.description,
786
+ group: normalizeGroup(action.group),
787
+ when: action.when,
788
+ params: action.params,
789
+ effects: action.effects,
790
+ spawn: action.spawn
791
+ }),
792
+ ...cascadeFired && action.roles !== void 0 && action.roles.length > 0 ? {
793
+ roles: action.roles
794
+ } : {},
795
+ ...filter ? {
796
+ filter: filter
797
+ } : {},
798
+ ...ops.length > 0 ? {
799
+ ops: ops
800
+ } : {}
801
+ };
802
+ }
803
+
804
+ function desugarClaimAction({action: action, path: path, env: env, ctx: ctx}) {
805
+ const ref = typeof action.field == "string" ? {
806
+ field: action.field
807
+ } : action.field, resolved = resolveRef({
808
+ ref: ref,
809
+ env: env,
810
+ path: [ ...path, "field" ],
811
+ ctx: ctx
812
+ });
813
+ if (resolved) {
814
+ const entry = entryAt(env, resolved);
815
+ entry && entry.type !== "actor" && ctx.issues.push({
816
+ path: [ ...path, "field" ],
817
+ message: `claim action "${action.name}" references "${resolved.field}" of kind "${entry.type}" — a claim pair needs an actor-valued entry (a "claim" or "actor" field declaration)`
818
+ }), checkShadowedClaimTarget({
819
+ actionName: action.name,
820
+ field: ref.field,
821
+ resolved: resolved,
822
+ env: env,
823
+ path: [ ...path, "field" ],
824
+ ctx: ctx
825
+ }), entry && ctx.claimedFields.add(entry);
826
+ }
827
+ const noSteal = `!defined($fields.${ref.field})`, filter = andConditions([ noSteal, rolesCondition(action.roles, ctx.roleAliases), action.filter ]), ops = resolved ? [ {
828
+ type: "field.set",
829
+ target: resolved,
830
+ value: {
831
+ type: "actor"
832
+ }
833
+ } ] : [];
834
+ return {
835
+ ...stripUndefined({
836
+ name: action.name,
837
+ title: action.title,
838
+ description: action.description,
839
+ group: normalizeGroup(action.group),
840
+ params: action.params,
841
+ effects: action.effects
842
+ }),
843
+ filter: filter,
844
+ ...ops.length > 0 ? {
845
+ ops: ops
846
+ } : {}
847
+ };
848
+ }
849
+
850
+ function checkShadowedClaimTarget({actionName: actionName, field: field, resolved: resolved, env: env, path: path, ctx: ctx}) {
851
+ const nearest = env.layers.find(l => l.entries.has(field));
852
+ nearest === void 0 || nearest.scope === resolved.scope || ctx.issues.push({
853
+ path: path,
854
+ message: `claim action "${actionName}" targets "${field}" at scope "${resolved.scope}", but a nearer ${nearest.scope}-scope entry of the same name shadows it in $fields — the no-steal filter would read the shadowing entry while the claim writes the "${resolved.scope}" one. Rename one of the entries or claim the nearer one`
855
+ });
856
+ }
857
+
858
+ function checkUnclaimedClaimFields(ctx) {
859
+ for (const [entry, {name: name, path: path}] of ctx.claimFields) ctx.claimedFields.has(entry) || ctx.issues.push({
860
+ path: path,
861
+ message: `claim field "${name}" is never referenced by a claim action — you announced the pattern and wrote half of it. Declare a defineAction({ type: "claim", field: "${name}" }) or use a raw actor entry`
862
+ });
863
+ }
864
+
865
+ const DEFAULT_TRANSITION_WHEN = "$allActivitiesDone";
866
+
867
+ function desugarTransition({transition: transition}) {
868
+ return {
869
+ ...stripUndefined({
870
+ name: transition.name,
871
+ title: transition.title,
872
+ description: transition.description,
873
+ to: transition.to
874
+ }),
875
+ when: transition.when ?? DEFAULT_TRANSITION_WHEN
876
+ };
877
+ }
878
+
879
+ function desugarGuard(guard) {
880
+ const {match: match, metadata: metadata, ...rest} = guard, {idRefs: idRefs, ...matchRest} = match;
881
+ return {
882
+ ...rest,
883
+ match: {
884
+ ...matchRest,
885
+ ...idRefs !== void 0 ? {
886
+ idRefs: idRefs.map(printGuardRead)
887
+ } : {}
888
+ },
889
+ ...metadata !== void 0 ? {
890
+ metadata: Object.fromEntries(Object.entries(metadata).map(([key, read]) => [ key, printGuardRead(read) ]))
891
+ } : {}
892
+ };
893
+ }
894
+
895
+ function desugarOps({ops: ops, path: path, env: env, firingActivity: firingActivity, ctx: ctx}) {
896
+ if (ops) return ops.map((op, i) => desugarOp({
897
+ op: op,
898
+ path: [ ...path, i ],
899
+ env: env,
900
+ firingActivity: firingActivity,
901
+ ctx: ctx
902
+ }));
903
+ }
904
+
905
+ function desugarOp({op: op, path: path, env: env, firingActivity: firingActivity, ctx: ctx}) {
906
+ if (op.type === "status.set") return {
907
+ type: "status.set",
908
+ activity: op.activity ?? firingActivity,
909
+ status: op.status
910
+ };
911
+ if (op.type === "audit") return desugarAuditOp({
912
+ op: op,
913
+ path: path,
914
+ env: env,
915
+ ctx: ctx
916
+ });
917
+ const target = resolveRef({
918
+ ref: op.target,
919
+ env: env,
920
+ path: [ ...path, "target" ],
921
+ ctx: ctx
922
+ }) ?? fallbackRef(op.target);
923
+ return {
924
+ ...op,
925
+ target: target
926
+ };
927
+ }
928
+
929
+ const AUDIT_STAMPS = {
930
+ actor: {
931
+ type: "actor"
932
+ },
933
+ at: {
934
+ type: "now"
935
+ }
936
+ };
937
+
938
+ function desugarAuditOp({op: op, path: path, env: env, ctx: ctx}) {
939
+ const target = resolveRef({
940
+ ref: op.target,
941
+ env: env,
942
+ path: [ ...path, "target" ],
943
+ ctx: ctx
944
+ }) ?? fallbackRef(op.target);
945
+ if (op.value.type !== "object") return ctx.issues.push({
946
+ path: [ ...path, "value" ],
947
+ message: `audit value must be an object source carrying the domain fields (got "${op.value.type}")`
948
+ }), {
949
+ type: "field.append",
950
+ target: target,
951
+ value: op.value
952
+ };
953
+ const actorField = op.stampFields?.actor ?? "actor", atField = op.stampFields?.at ?? "at", fields = {
954
+ ...op.value.fields
955
+ };
956
+ for (const [stamp, source] of [ [ actorField, AUDIT_STAMPS.actor ], [ atField, AUDIT_STAMPS.at ] ]) {
957
+ if (stamp in fields) {
958
+ ctx.issues.push({
959
+ path: [ ...path, "value", "fields", stamp ],
960
+ message: `audit stamp field "${stamp}" collides with an authored domain field — rename yours or remap the stamp via stampFields`
961
+ });
962
+ continue;
963
+ }
964
+ fields[stamp] = source;
965
+ }
966
+ return {
967
+ type: "field.append",
968
+ target: target,
969
+ value: {
970
+ type: "object",
971
+ fields: fields
972
+ }
973
+ };
974
+ }
975
+
976
+ function resolveRef({ref: ref, env: env, path: path, ctx: ctx}) {
977
+ const layers = ref.scope === void 0 ? env.layers : env.layers.filter(l => l.scope === ref.scope);
978
+ for (const layer of layers) if (layer.entries.has(ref.field)) return {
979
+ scope: layer.scope,
980
+ field: ref.field
981
+ };
982
+ const reachable = env.layers.flatMap(l => [ ...l.entries.keys() ].map(n => `${l.scope}:${n}`));
983
+ ctx.issues.push({
984
+ path: path,
985
+ message: `field reference "${ref.field}"${ref.scope ? ` (scope "${ref.scope}")` : ""} does not resolve to a declared entry. Reachable: ${reachable.join(", ") || "(none)"}`
986
+ });
987
+ }
988
+
989
+ function entryAt(env, ref) {
990
+ return env.layers.find(l => l.scope === ref.scope)?.entries.get(ref.field);
991
+ }
992
+
993
+ function fallbackRef(ref) {
994
+ return {
995
+ scope: ref.scope ?? "workflow",
996
+ field: ref.field
997
+ };
998
+ }
999
+
1000
+ function rolesCondition(roles, aliases) {
1001
+ if (!(!roles || roles.length === 0)) return groq`count($actor.roles[@ in ${expandRequiredRoles(roles, aliases)}]) > 0`;
1002
+ }
1003
+
1004
+ function stripUndefined(obj) {
1005
+ const out = {};
1006
+ for (const [k, value] of Object.entries(obj)) value !== void 0 && (out[k] = value);
1007
+ return out;
1008
+ }
1009
+
1010
+ function isUnevaluable(result) {
1011
+ return result == null;
1012
+ }
1013
+
1014
+ async function evaluateConditionOutcome(args) {
1015
+ const {condition: condition, snapshot: snapshot, params: params} = args;
1016
+ return groqConditionDescribe.evaluateConditionOutcome({
1017
+ condition: condition,
1018
+ params: params,
1019
+ dataset: snapshot.docs
1020
+ });
1021
+ }
1022
+
1023
+ async function evaluateCondition(args) {
1024
+ return await evaluateConditionOutcome(args) === "satisfied";
1025
+ }
1026
+
1027
+ async function evaluatePredicates(args) {
1028
+ const out = {};
1029
+ for (const [name, groq2] of Object.entries(args.predicates ?? {})) {
1030
+ const result = await runGroq({
1031
+ groq: groq2,
1032
+ params: args.params,
1033
+ snapshot: args.snapshot
1034
+ }), outcome = groqConditionDescribe.conditionOutcome(result);
1035
+ out[name] = outcome === "unevaluable" ? null : outcome === "satisfied";
1036
+ }
1037
+ return out;
1038
+ }
1039
+
1040
+ async function runGroq({groq: groq2, params: params, snapshot: snapshot}) {
1041
+ return groqConditionDescribe.runGroq({
1042
+ groq: groq2,
1043
+ params: params,
1044
+ dataset: snapshot.docs
1045
+ });
1046
+ }
1047
+
1048
+ function conditionSyntaxIssues(groq2, boundVars) {
1049
+ const issues = [];
1050
+ let tree;
1051
+ try {
1052
+ tree = groqJs.parse(groq2);
1053
+ } catch (err) {
1054
+ issues.push(errorMessage(err));
1055
+ }
1056
+ if (/\*\s*\[\s*_type\b/.test(groq2) && issues.push("condition scans by `_type` — that's a discovery query, not a predicate. Conditions evaluate against the in-memory snapshot (instance + ancestors + field-declared docs). To bring extra docs in scope, declare a `doc.ref` (or `doc.refs`) field entry on the workflow or this stage. For lake scans like \"all articles in this release\", use a spawn action's `forEach` instead."),
1057
+ boundVars !== void 0 && tree !== void 0) for (const name of conditionParameterNames(groq2)) boundVars.includes(name) || issues.push(`reads $${name}, which this scope does not bind — an unbound variable evaluates to GROQ null, so the condition silently never matches. Bound here: ` + boundVars.map(n => `$${n}`).join(", "));
1058
+ return issues;
1059
+ }
1060
+
1061
+ function conditionParameterNames(groq2) {
1062
+ const read = /* @__PURE__ */ new Set;
1063
+ return walkAstNodes(tryParseGroq(groq2), node => {
1064
+ node.type === "Parameter" && typeof node.name == "string" && read.add(node.name);
1065
+ }), read;
1066
+ }
1067
+
1068
+ function conditionFieldReadNames(groq2) {
1069
+ const read = /* @__PURE__ */ new Set;
1070
+ return walkAstNodes(tryParseGroq(groq2), node => {
1071
+ if (node.type !== "AccessAttribute" || typeof node.name != "string") return;
1072
+ const base = node.base;
1073
+ base?.type === "Parameter" && base.name === "fields" && read.add(node.name);
1074
+ }), read;
1075
+ }
1076
+
1077
+ function conditionEffectReads(groq2) {
1078
+ const reads = [];
1079
+ return walkAstNodes(tryParseGroq(groq2), node => {
1080
+ if (node.type !== "AccessAttribute" || typeof node.name != "string") return;
1081
+ const base = node.base;
1082
+ base?.type !== "AccessAttribute" || typeof base.name != "string" || base.base?.type === "Parameter" && base.base.name === "effects" && reads.push({
1083
+ effect: base.name,
1084
+ key: node.name
1085
+ });
1086
+ }), reads;
1087
+ }
1088
+
1089
+ function readsRootDocument(groq2) {
1090
+ return nodeReadsRoot(tryParseGroq(groq2), 0);
1091
+ }
1092
+
1093
+ const SCOPED_CHILD = {
1094
+ Filter: "expr",
1095
+ Projection: "expr",
1096
+ Map: "expr",
1097
+ FlatMap: "expr",
1098
+ PipeFuncCall: "args"
1099
+ };
1100
+
1101
+ function nodeReadsRoot(node, depth) {
1102
+ if (Array.isArray(node)) return node.some(item => nodeReadsRoot(item, depth));
1103
+ if (typeof node != "object" || node === null) return !1;
1104
+ const typed = node;
1105
+ if (depth === 0 && typed.type === "This" || depth === 0 && typed.type === "AccessAttribute" && typed.base === void 0) return !0;
1106
+ if (typed.type === "Parent") {
1107
+ const climb = typeof typed.n == "number" ? typed.n : 1;
1108
+ return depth - climb <= 0;
1109
+ }
1110
+ const scopedChild = typeof typed.type == "string" ? SCOPED_CHILD[typed.type] : void 0;
1111
+ return scopedChild !== void 0 ? Object.entries(node).some(([key, value]) => nodeReadsRoot(value, key === scopedChild ? depth + 1 : depth)) : Object.values(node).some(value => nodeReadsRoot(value, depth));
1112
+ }
1113
+
1114
+ function tryParseGroq(groq2) {
1115
+ try {
1116
+ return groqJs.parse(groq2);
1117
+ } catch {
1118
+ return;
1119
+ }
1120
+ }
1121
+
1122
+ function walkAstNodes(node, visit) {
1123
+ if (Array.isArray(node)) {
1124
+ for (const item of node) walkAstNodes(item, visit);
1125
+ return;
1126
+ }
1127
+ if (!(typeof node != "object" || node === null)) {
1128
+ visit(node);
1129
+ for (const value of Object.values(node)) walkAstNodes(value, visit);
1130
+ }
1131
+ }
1132
+
1133
+ const ACTIVITY_STATUSES = [ "active", "done", "skipped", "failed" ], TERMINAL_ACTIVITY_STATUSES = [ "done", "skipped", "failed" ];
1134
+
1135
+ function isTerminalActivityStatus(status) {
1136
+ return TERMINAL_ACTIVITY_STATUSES.includes(status);
1137
+ }
1138
+
1139
+ const FIELD_SCOPES = [ "workflow", "stage", "activity" ], DOCUMENT_VALUE_PERMISSIONS = [ "create", "read", "update" ], MUTATION_GUARD_ACTIONS = [ "create", "update", "delete", "publish", "unpublish" ], ACTIVITY_KINDS = [ "user", "service", "script", "manual", "receive" ], EXECUTOR_CLASSIFICATIONS = [ "autonomous", "interactive", "off-system", "hybrid" ], GROUP_KINDS = [ "core", "details" ], DRIVER_KINDS = [ "person", "agent", "service", "engine" ], CONDITION_VARS = [ {
1140
+ name: "self",
1141
+ binding: "always",
1142
+ label: "this workflow instance",
1143
+ description: "GDR URI of the instance document itself — `*[_id == $self][0]` reads the instance in the snapshot."
1144
+ }, {
1145
+ name: "fields",
1146
+ binding: "always",
1147
+ label: "the workflow's fields",
1148
+ description: "Declared field entries rendered by name (`$fields.<name>` is the value, no envelope). Stage/activity scopes overlay lexically; `doc.ref` values render the hydrated document when the snapshot holds it."
1149
+ }, {
1150
+ name: "parent",
1151
+ binding: "always",
1152
+ label: "the parent workflow",
1153
+ description: "The parent instance's GDR URI, `null` on a root instance."
1154
+ }, {
1155
+ name: "ancestors",
1156
+ binding: "always",
1157
+ label: "the workflow's ancestors",
1158
+ description: "GDR URIs of the ancestor chain, root first."
1159
+ }, {
1160
+ name: "stage",
1161
+ binding: "always",
1162
+ label: "the current stage",
1163
+ description: "The current stage's name."
1164
+ }, {
1165
+ name: "now",
1166
+ binding: "always",
1167
+ label: "the current time",
1168
+ description: "The ISO clock reading shared by every condition in one evaluation pass, so they agree on the time."
1169
+ }, {
1170
+ name: "context",
1171
+ binding: "always",
1172
+ label: "start-time context",
1173
+ description: "The instance's `context` bag: values seeded at `startInstance` plus a parent's `spawn.context` handoff, by entry name. Written only at start — never mutated afterwards."
1174
+ }, {
1175
+ name: "effects",
1176
+ binding: "always",
1177
+ label: "automation outputs",
1178
+ description: "Completed effects' outputs, namespaced by effect name (`$effects['<effect>'].<output>`) — each effect's latest completed run wins. Handler fuel — transition triggers should read instance/lake state (or $effectStatus) instead."
1179
+ }, {
1180
+ name: "effectStatus",
1181
+ binding: "always",
1182
+ label: "automation status",
1183
+ description: "Effect name → `'done'` | `'failed'` of the latest completed run queued during the current stage entry; absent until the effect drains for this entry. Re-entry-safe: runs queued under a prior entry never count, so `$effectStatus['<effect>'] == 'done'` (or `defined($effectStatus['<effect>'])` for settled-either-way) waits for the fresh run."
1184
+ }, {
1185
+ name: "activities",
1186
+ binding: "always",
1187
+ label: "this stage's activities",
1188
+ description: "The current stage's activity rows, statuses included."
1189
+ }, {
1190
+ name: "allActivitiesDone",
1191
+ binding: "always",
1192
+ label: "all activities in this stage are finished",
1193
+ description: "Every current-stage activity is `done` or `skipped` — the default transition gate."
1194
+ }, {
1195
+ name: "anyActivityFailed",
1196
+ binding: "always",
1197
+ label: "an activity in this stage has failed",
1198
+ description: "Some current-stage activity is `failed`."
1199
+ }, {
1200
+ name: "actor",
1201
+ binding: "caller",
1202
+ label: "you",
1203
+ description: "The acting identity (id + roles); `undefined` when no caller rides the evaluation. Never holds a value in the cascade gates (deploy-rejected there)."
1204
+ }, {
1205
+ name: "assigned",
1206
+ binding: "caller",
1207
+ label: "you are assigned to this activity",
1208
+ description: "Whether the caller matches the activity's assignees-kind field entry (by user id, or by a role under the definition's `roleAliases`); `false` outside an activity context. Constant `false` in the cascade gates (deploy-rejected there)."
1209
+ }, {
1210
+ name: "can",
1211
+ binding: "caller",
1212
+ label: "your permissions",
1213
+ description: "Advisory per-permission booleans computed from the caller's grants; `undefined` without grants. Bound wherever grants ride the evaluation: the projection's rendered scope (fireAction-action filters, activity requirements, editability predicates) and the fireAction/editField commit gates. Deploy rejects it at every site that evaluates without grants: transition `when`s, activity filters, cascade-fired actions' `when`/`filter`, effect bindings, where-op `where`s, and the spawn `forEach`/`with`/`context` sites."
1214
+ }, {
1215
+ name: "row",
1216
+ binding: "spawn",
1217
+ label: "the spawned row",
1218
+ description: "One `spawn.forEach` result row, bound while its `with` map evaluates — and the row under test while a where-op `where` evaluates (per row)."
1219
+ }, {
1220
+ name: "params",
1221
+ binding: "caller",
1222
+ label: "the action's arguments",
1223
+ description: "The firing action's args — they hold values only while a fireAction-fired action's effect bindings and where-op `where`s evaluate (spawn sites don't bind it at all, and a cascade-fired action has no caller to supply args). Deploy rejects a read in the cascade gates, in a cascade-fired action's payload, and in the caller-bound projection (action filters, requirements, editable predicates): args exist only once the caller fires the action, after those sites have evaluated."
1224
+ }, {
1225
+ name: "subworkflows",
1226
+ binding: "always",
1227
+ label: "the spawned subworkflows",
1228
+ description: "Every row of the instance's subworkflow registry, faceted by `activity`/`action`/`definition`/`rowKey`/`status` (`'active'|'done'|'aborted'`) with `current` marking the open stage entry's cohort and `stage` the child's current stage. Usable anywhere — transition `when`s, requirements, any stage's gates; the settled gate is `count($subworkflows[activity == <name> && current && status == 'active']) == 0`."
1229
+ } ], RESERVED_CONDITION_VARS = CONDITION_VARS.map(v2 => v2.name), FILTER_SCOPE_VARS = CONDITION_VARS.filter(v2 => v2.binding === "always").map(v2 => v2.name), CALLER_BOUND_VARS = CONDITION_VARS.filter(v2 => v2.binding === "caller").map(v2 => v2.name), START_FILTER_VARS = [ {
1230
+ name: "tag",
1231
+ description: "The engine's tag partition — scope `*[...]` instance scans with `tag == $tag`."
1232
+ }, {
1233
+ name: "definition",
1234
+ description: "The `name` of the definition under evaluation (its own start.filter binds it)."
1235
+ }, {
1236
+ name: "now",
1237
+ description: "The ISO clock reading of the evaluating engine."
1238
+ }, {
1239
+ name: "fields",
1240
+ description: "The candidate initialFields by entry name, when the read surface has them (the Studio start control: yes; a per-doc picker: no). Document references bind as GDR envelopes — `$fields.<entry>.id` is the GDR URI, never a string authors assemble."
1241
+ } ], GUARD_PREDICATE_VARS = [ {
1242
+ name: "guard",
1243
+ description: "The guard document itself (its `metadata` carries deploy-time resolved values)."
1244
+ }, {
1245
+ name: "mutation",
1246
+ description: "The attempted mutation — `mutation.action` is the write kind being gated."
1247
+ } ], ACTOR_KINDS = [ "person", "agent", "system" ];
1248
+
1249
+ function releaseDocId(releaseName) {
1250
+ return `_.releases.${releaseName}`;
1251
+ }
1252
+
1253
+ function releaseRef({res: res, releaseName: releaseName}) {
1254
+ if (releaseName.length === 0) throw new ContractViolationError("releaseRef: releaseName must be a non-empty release name");
1255
+ return {
1256
+ id: gdrFromResource(res, releaseDocId(releaseName)),
1257
+ type: "system.release",
1258
+ releaseName: releaseName
1259
+ };
1260
+ }
1261
+
1262
+ function isTodoListItem(row) {
1263
+ if (typeof row != "object" || row === null) return !1;
1264
+ const candidate = row, status = candidate.status;
1265
+ return typeof candidate._key == "string" && typeof candidate.label == "string" && (status == null || typeof status == "string");
1266
+ }
1267
+
1268
+ function declaredRowColumns(entry) {
1269
+ if (("_type" in entry ? entry._type : entry.type) === "array") return new Set((("of" in entry ? entry.of : void 0) ?? []).map(shape => shape.name));
1270
+ }
1271
+
1272
+ function isTodoListEntry(entry) {
1273
+ const columns = declaredRowColumns(entry);
1274
+ return columns !== void 0 && columns.has("label") && columns.has("status");
1275
+ }
1276
+
1277
+ function isNotesEntry(entry) {
1278
+ const columns = declaredRowColumns(entry);
1279
+ return columns !== void 0 && columns.has("body") && columns.has("actor") && columns.has("at");
1280
+ }
1281
+
1282
+ class FieldValueShapeError extends WorkflowError {
1283
+ entryType;
1284
+ entryName;
1285
+ issues;
1286
+ constructor(args) {
1287
+ const issueText = args.issues.join("; ");
1288
+ super("field-value-shape", `Field entry ${args.mode} shape invalid for "${args.entryName}" (${args.entryType}): ${issueText}`),
1289
+ this.name = "FieldValueShapeError", this.entryType = args.entryType, this.entryName = args.entryName,
1290
+ this.issues = args.issues;
1291
+ }
1292
+ }
1293
+
1294
+ const GdrShape = v__namespace.looseObject({
1295
+ id: v__namespace.pipe(v__namespace.string(), v__namespace.check(s => isGdrUri(s), "must be a GDR URI")),
1296
+ type: v__namespace.pipe(v__namespace.string(), v__namespace.minLength(1))
1297
+ }), ReleaseRefShape = v__namespace.pipe(v__namespace.looseObject({
1298
+ id: v__namespace.pipe(v__namespace.string(), v__namespace.check(s => isGdrUri(s), "must be a GDR URI")),
1299
+ type: v__namespace.literal("system.release"),
1300
+ releaseName: v__namespace.pipe(v__namespace.string(), v__namespace.minLength(1))
1301
+ }), v__namespace.check(ref => !isGdrUri(ref.id) || extractDocumentId(ref.id) === releaseDocId(ref.releaseName), "id must point at the `_.releases.<releaseName>` doc named by releaseName")), ActorShape = v__namespace.looseObject({
1302
+ kind: v__namespace.picklist(ACTOR_KINDS),
1303
+ id: v__namespace.pipe(v__namespace.string(), v__namespace.minLength(1)),
1304
+ roles: v__namespace.optional(v__namespace.array(v__namespace.string())),
1305
+ onBehalfOf: v__namespace.optional(v__namespace.string())
1306
+ }), AssigneeShape = v__namespace.union([ v__namespace.looseObject({
1307
+ type: v__namespace.literal("user"),
1308
+ id: v__namespace.pipe(v__namespace.string(), v__namespace.minLength(1))
1309
+ }), v__namespace.looseObject({
1310
+ type: v__namespace.literal("role"),
1311
+ role: v__namespace.pipe(v__namespace.string(), v__namespace.minLength(1))
1312
+ }) ]), NullableString = v__namespace.union([ v__namespace.null(), v__namespace.string() ]), NullableNumber = v__namespace.union([ v__namespace.null(), v__namespace.number() ]), NullableBoolean = v__namespace.union([ v__namespace.null(), v__namespace.boolean() ]), NullableDateTime = v__namespace.union([ v__namespace.null(), v__namespace.pipe(v__namespace.string(), v__namespace.check(s => !Number.isNaN(Date.parse(s)), "must be an ISO-8601 datetime string")) ]), NullableDate = v__namespace.union([ v__namespace.null(), v__namespace.pipe(v__namespace.string(), v__namespace.regex(/^\d{4}-\d{2}-\d{2}$/, "must be a `YYYY-MM-DD` date")) ]), NullableUrl = NullableString, valueSchemas = {
1313
+ "doc.ref": v__namespace.union([ v__namespace.null(), GdrShape ]),
1314
+ "doc.refs": v__namespace.array(GdrShape),
1315
+ "release.ref": v__namespace.union([ v__namespace.null(), ReleaseRefShape ]),
1316
+ query: v__namespace.any(),
1317
+ string: NullableString,
1318
+ text: NullableString,
1319
+ number: NullableNumber,
1320
+ boolean: NullableBoolean,
1321
+ date: NullableDate,
1322
+ datetime: NullableDateTime,
1323
+ url: NullableUrl,
1324
+ actor: v__namespace.union([ v__namespace.null(), ActorShape ]),
1325
+ assignee: v__namespace.union([ v__namespace.null(), AssigneeShape ]),
1326
+ assignees: v__namespace.array(AssigneeShape)
1327
+ };
1328
+
1329
+ function shapeValueSchema(shape, leaf) {
1330
+ return shape.type === "object" ? objectSchema(shape.fields ?? [], leaf) : shape.type === "array" ? v__namespace.array(objectSchema(shape.of ?? [], leaf)) : leaf[shape.type] ?? v__namespace.any();
1331
+ }
1332
+
1333
+ function objectSchema(fields, leaf) {
1334
+ const entries = /* @__PURE__ */ Object.create(null);
1335
+ for (const f of fields) entries[f.name] = v__namespace.optional(shapeValueSchema(f, leaf));
1336
+ return v__namespace.looseObject(entries);
1337
+ }
1338
+
1339
+ function wholeValueSchema(args) {
1340
+ const {entryType: entryType, shape: shape, leaf: leaf} = args;
1341
+ return entryType === "object" ? v__namespace.union([ v__namespace.null(), objectSchema(shape.fields ?? [], leaf) ]) : entryType === "array" ? v__namespace.array(objectSchema(shape.of ?? [], leaf)) : leaf[entryType];
1342
+ }
1343
+
1344
+ function appendItemSchema(entryType, shape) {
1345
+ if (entryType === "array") return objectSchema(shape.of ?? [], valueSchemas);
1346
+ if (entryType === "doc.refs") return GdrShape;
1347
+ if (entryType === "assignees") return AssigneeShape;
1348
+ }
1349
+
1350
+ function rejectedRefTypes(args) {
1351
+ const {entryType: entryType, types: types, value: value} = args;
1352
+ if (types === void 0 || value === null || value === void 0) return [];
1353
+ if (entryType !== "doc.ref" && entryType !== "doc.refs") return [];
1354
+ let items = [ value ];
1355
+ return entryType === "doc.refs" && (items = Array.isArray(value) ? value : []),
1356
+ [ ...new Set(items.map(gdrTypeOf).filter(t => t !== void 0 && !types.includes(t))) ];
1357
+ }
1358
+
1359
+ function refTypeIssues(args) {
1360
+ const rejected = rejectedRefTypes(args);
1361
+ if (rejected.length === 0) return;
1362
+ const accepts = (args.types ?? []).map(t => `"${t}"`).join(", ");
1363
+ return rejected.map(t => `document type "${t}" is not accepted — this entry accepts ${accepts} (a GDR's \`type\` names the target document's schema type)`);
1364
+ }
1365
+
1366
+ function gdrTypeOf(item) {
1367
+ if (typeof item != "object" || item === null) return;
1368
+ const t = item.type;
1369
+ return typeof t == "string" ? t : void 0;
1370
+ }
1371
+
1372
+ function checkFieldValue(args) {
1373
+ return checkValueAgainst(args, valueSchemas);
1374
+ }
1375
+
1376
+ function checkValueAgainst(args, leaf) {
1377
+ const schema = wholeValueSchema({
1378
+ entryType: args.entryType,
1379
+ shape: args,
1380
+ leaf: leaf
1381
+ });
1382
+ if (schema === void 0) return [ `unknown field entry type ${args.entryType}` ];
1383
+ const result = v__namespace.safeParse(schema, args.value);
1384
+ return result.success ? refTypeIssues({
1385
+ entryType: args.entryType,
1386
+ types: args.types,
1387
+ value: args.value
1388
+ }) : formatIssues(result.issues);
1389
+ }
1390
+
1391
+ function validateFieldValue(args) {
1392
+ const issues = checkFieldValue(args);
1393
+ if (issues !== void 0) throw new FieldValueShapeError({
1394
+ entryType: args.entryType,
1395
+ entryName: args.entryName,
1396
+ issues: issues,
1397
+ mode: "value"
1398
+ });
1399
+ }
1400
+
1401
+ const BARE_ID_SOURCE = "[A-Za-z0-9_][A-Za-z0-9._-]*", BARE_ID_RE = new RegExp(`^${BARE_ID_SOURCE}$`), ALIAS_REF_RE = new RegExp(`^@${RESOURCE_ALIAS_NAME_SOURCE}:${BARE_ID_SOURCE}$`);
1402
+
1403
+ function isAuthoringRefId(id) {
1404
+ return id.startsWith("@") ? ALIAS_REF_RE.test(id) : id.includes(":") ? isGdrUri(id) : BARE_ID_RE.test(id);
1405
+ }
1406
+
1407
+ function isBareSeedId(id) {
1408
+ return BARE_ID_RE.test(id);
1409
+ }
1410
+
1411
+ const AuthoringRefId = v__namespace.pipe(v__namespace.string(), v__namespace.check(isAuthoringRefId, "must be a bare document id, a GDR URI, or a portable `@<alias>:<id>` reference")), AuthoringGdrShape = v__namespace.looseObject({
1412
+ id: AuthoringRefId,
1413
+ type: v__namespace.pipe(v__namespace.string(), v__namespace.minLength(1))
1414
+ }), seedValueSchemas = {
1415
+ ...valueSchemas,
1416
+ "doc.ref": v__namespace.union([ v__namespace.null(), AuthoringGdrShape ]),
1417
+ "doc.refs": v__namespace.array(AuthoringGdrShape),
1418
+ "release.ref": v__namespace.union([ v__namespace.null(), v__namespace.looseObject({
1419
+ id: AuthoringRefId,
1420
+ type: v__namespace.literal("system.release"),
1421
+ releaseName: v__namespace.pipe(v__namespace.string(), v__namespace.minLength(1))
1422
+ }) ])
1423
+ };
1424
+
1425
+ function checkLiteralSeed(args) {
1426
+ return args.value === null ? [ "a literal seed cannot be null — omit `initialValue` to start the field empty" ] : checkValueAgainst(args, seedValueSchemas);
1427
+ }
1428
+
1429
+ function validateFieldAppendItem(args) {
1430
+ const schema = appendItemSchema(args.entryType, args);
1431
+ if (schema === void 0) throw new FieldValueShapeError({
1432
+ entryType: args.entryType,
1433
+ entryName: args.entryName,
1434
+ issues: [ `field entry type ${args.entryType} does not support append` ],
1435
+ mode: "item"
1436
+ });
1437
+ const result = v__namespace.safeParse(schema, args.item);
1438
+ if (!result.success) throw new FieldValueShapeError({
1439
+ entryType: args.entryType,
1440
+ entryName: args.entryName,
1441
+ issues: formatIssues(result.issues),
1442
+ mode: "item"
1443
+ });
1444
+ const typeIssues = refTypeIssues({
1445
+ entryType: args.entryType,
1446
+ types: args.types,
1447
+ value: [ args.item ]
1448
+ });
1449
+ if (typeIssues !== void 0) throw new FieldValueShapeError({
1450
+ entryType: args.entryType,
1451
+ entryName: args.entryName,
1452
+ issues: typeIssues,
1453
+ mode: "item"
1454
+ });
1455
+ }
1456
+
1457
+ function formatIssues(issues) {
1458
+ return issues.map(i => {
1459
+ const keys = i.path?.map(p => p.key) ?? [];
1460
+ return `${keys.length > 0 ? `at ${keys.join(".")}: ` : ""}${i.message}`;
1461
+ });
1462
+ }
1463
+
1464
+ const NonEmpty = v__namespace.pipe(v__namespace.string(), v__namespace.minLength(1, "must be a non-empty string")), PositiveInt = v__namespace.pipe(v__namespace.number(), v__namespace.integer(), v__namespace.minValue(1)), GROQ_IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
1465
+
1466
+ function groqIdentifier(referencedAs) {
1467
+ return v__namespace.pipe(v__namespace.string(), v__namespace.regex(GROQ_IDENTIFIER, `must be a GROQ-safe identifier (letters, digits, underscore; not starting with a digit) because it is referenced as ${referencedAs} in GROQ conditions`));
1468
+ }
1469
+
1470
+ function picklist(options) {
1471
+ return v__namespace.picklist(options, `Invalid option: expected one of ${options.map(o => `"${o}"`).join("|")}`);
1472
+ }
1473
+
1474
+ function pinned() {
1475
+ return (schema, ..._exact) => schema;
1476
+ }
1477
+
1478
+ const LiteralSchema = v__namespace.strictObject({
1479
+ type: v__namespace.literal("literal"),
1480
+ value: v__namespace.unknown()
1481
+ }), FieldReadSchema = v__namespace.strictObject({
1482
+ type: v__namespace.literal("fieldRead"),
1483
+ scope: v__namespace.optional(v__namespace.union([ v__namespace.literal("workflow"), v__namespace.literal("stage") ])),
1484
+ field: NonEmpty,
1485
+ path: v__namespace.optional(v__namespace.string())
1486
+ }), FieldSourceSchema = v__namespace.union([ v__namespace.strictObject({
1487
+ type: v__namespace.literal("input")
1488
+ }), v__namespace.strictObject({
1489
+ type: v__namespace.literal("query"),
1490
+ query: NonEmpty
1491
+ }), LiteralSchema, FieldReadSchema ]), ValueExprSchema = v__namespace.lazy(() => v__namespace.union([ LiteralSchema, FieldReadSchema, v__namespace.strictObject({
1492
+ type: v__namespace.literal("param"),
1493
+ param: NonEmpty
1494
+ }), v__namespace.strictObject({
1495
+ type: v__namespace.literal("actor")
1496
+ }), v__namespace.strictObject({
1497
+ type: v__namespace.literal("now")
1498
+ }), v__namespace.strictObject({
1499
+ type: v__namespace.literal("self")
1500
+ }), v__namespace.strictObject({
1501
+ type: v__namespace.literal("stage")
1502
+ }), v__namespace.strictObject({
1503
+ type: v__namespace.literal("object"),
1504
+ fields: v__namespace.record(NonEmpty, ValueExprSchema)
1505
+ }) ])), StoredFieldRefSchema = v__namespace.strictObject({
1506
+ scope: picklist(FIELD_SCOPES),
1507
+ field: NonEmpty
1508
+ }), AuthoringFieldRefSchema = v__namespace.strictObject({
1509
+ scope: v__namespace.optional(picklist(FIELD_SCOPES)),
1510
+ field: NonEmpty
1511
+ }), HREF_SCHEMES = [ "http:", "https:" ];
1512
+
1513
+ function isHttpUrl(value) {
1514
+ try {
1515
+ return HREF_SCHEMES.includes(new URL(value).protocol);
1516
+ } catch {
1517
+ return !1;
1518
+ }
1519
+ }
1520
+
1521
+ const UrlString = v__namespace.pipe(v__namespace.string(), v__namespace.url("must be a valid URL"), v__namespace.check(isHttpUrl, "must be an http(s) URL"));
1522
+
1523
+ function manualTargetSchema(ref) {
1524
+ return v__namespace.variant("type", [ v__namespace.strictObject({
1525
+ type: v__namespace.literal("url"),
1526
+ url: UrlString
1527
+ }), v__namespace.strictObject({
1528
+ type: v__namespace.literal("field"),
1529
+ field: ref
1530
+ }) ]);
1531
+ }
1532
+
1533
+ const StoredManualTargetSchema = manualTargetSchema(StoredFieldRefSchema), AuthoringManualTargetSchema = manualTargetSchema(v__namespace.union([ NonEmpty, AuthoringFieldRefSchema ])), ConditionSchema = NonEmpty;
1534
+
1535
+ function opSchemas(targetSchema) {
1536
+ return [ v__namespace.strictObject({
1537
+ type: v__namespace.literal("field.set"),
1538
+ target: targetSchema,
1539
+ value: ValueExprSchema
1540
+ }), v__namespace.strictObject({
1541
+ type: v__namespace.literal("field.unset"),
1542
+ target: targetSchema
1543
+ }), v__namespace.strictObject({
1544
+ type: v__namespace.literal("field.append"),
1545
+ target: targetSchema,
1546
+ value: ValueExprSchema
1547
+ }), v__namespace.strictObject({
1548
+ type: v__namespace.literal("field.updateWhere"),
1549
+ target: targetSchema,
1550
+ where: ConditionSchema,
1551
+ value: ValueExprSchema
1552
+ }), v__namespace.strictObject({
1553
+ type: v__namespace.literal("field.removeWhere"),
1554
+ target: targetSchema,
1555
+ where: ConditionSchema
1556
+ }) ];
1557
+ }
1558
+
1559
+ const StoredFieldOpSchema = v__namespace.variant("type", [ ...opSchemas(StoredFieldRefSchema) ]), StoredOpSchema = v__namespace.variant("type", [ ...opSchemas(StoredFieldRefSchema), v__namespace.strictObject({
1560
+ type: v__namespace.literal("status.set"),
1561
+ activity: NonEmpty,
1562
+ status: picklist(ACTIVITY_STATUSES)
1563
+ }) ]), AuditOpSchema = v__namespace.strictObject({
1564
+ type: v__namespace.literal("audit"),
1565
+ target: AuthoringFieldRefSchema,
1566
+ value: ValueExprSchema,
1567
+ stampFields: v__namespace.optional(v__namespace.strictObject({
1568
+ actor: v__namespace.optional(NonEmpty),
1569
+ at: v__namespace.optional(NonEmpty)
1570
+ }))
1571
+ }), AuthoringOpSchema = v__namespace.variant("type", [ ...opSchemas(AuthoringFieldRefSchema), v__namespace.strictObject({
1572
+ type: v__namespace.literal("status.set"),
1573
+ activity: v__namespace.optional(NonEmpty),
1574
+ status: picklist(ACTIVITY_STATUSES)
1575
+ }), AuditOpSchema ]), GroupName = v__namespace.pipe(v__namespace.string(), v__namespace.regex(GROQ_IDENTIFIER, "must be an identifier (letters, digits, underscore; not starting with a digit)")), GroupSchema = pinned()(v__namespace.strictObject({
1576
+ name: GroupName,
1577
+ title: v__namespace.optional(v__namespace.string()),
1578
+ description: v__namespace.optional(v__namespace.string()),
1579
+ kind: v__namespace.optional(picklist(GROUP_KINDS))
1580
+ })), GroupNameList = v__namespace.pipe(v__namespace.array(GroupName), v__namespace.minLength(1, "name at least one group, or omit `group`"), v__namespace.check(names => new Set(names).size === names.length, "a group is listed more than once — list each group once")), StoredGroupMembershipSchema = GroupNameList, AuthoringGroupMembershipSchema = v__namespace.union([ GroupName, GroupNameList ]);
1581
+
1582
+ function groupMembershipNames(group) {
1583
+ return group === void 0 ? [] : typeof group == "string" ? [ group ] : [ ...group ];
1584
+ }
1585
+
1586
+ const FIELD_VALUE_KINDS = [ "doc.ref", "doc.refs", "release.ref", "string", "text", "number", "boolean", "date", "datetime", "url", "actor", "assignee", "assignees", "object", "array" ], FieldValueKindSchema = picklist(FIELD_VALUE_KINDS), FieldKindSchema = picklist(FIELD_VALUE_KINDS), FieldEntryName = groqIdentifier("`$fields.<name>`");
1587
+
1588
+ function asShape(input) {
1589
+ return typeof input == "object" && input !== null ? input : {};
1590
+ }
1591
+
1592
+ function compositeShapeOk(input) {
1593
+ const shape = asShape(input);
1594
+ return shape.type === "object" ? Array.isArray(shape.fields) && shape.fields.length > 0 && shape.of === void 0 : shape.type === "array" ? Array.isArray(shape.of) && shape.of.length > 0 && shape.fields === void 0 : shape.fields === void 0 && shape.of === void 0;
1595
+ }
1596
+
1597
+ function compositeShapeMessage(input) {
1598
+ const shape = asShape(input);
1599
+ return shape.type === "object" ? shape.of !== void 0 ? "an `object` kind declares its sub-fields with `fields`, not `of`" : "an `object` kind needs a non-empty `fields` list of sub-field shapes" : shape.type === "array" ? shape.fields !== void 0 ? "an `array` kind declares its item shape with `of`, not `fields`" : "an `array` kind needs a non-empty `of` list of sub-field shapes" : `\`fields\` / \`of\` are only valid on the \`object\` / \`array\` kinds, not "${String(shape.type)}"`;
1600
+ }
1601
+
1602
+ function duplicateSubfieldName(input) {
1603
+ const shape = asShape(input);
1604
+ let list = [];
1605
+ Array.isArray(shape.fields) ? list = shape.fields : Array.isArray(shape.of) && (list = shape.of);
1606
+ const seen = /* @__PURE__ */ new Set;
1607
+ for (const item of list) {
1608
+ const name = asShape(item).name;
1609
+ if (typeof name == "string") {
1610
+ if (seen.has(name)) return name;
1611
+ seen.add(name);
1612
+ }
1613
+ }
1614
+ }
1615
+
1616
+ function compositeChecked(entries) {
1617
+ return v__namespace.pipe(v__namespace.strictObject(entries), v__namespace.check(input => compositeShapeOk(input), issue => compositeShapeMessage(issue.input)), v__namespace.check(input => duplicateSubfieldName(input) === void 0, issue => `duplicate sub-field name "${duplicateSubfieldName(issue.input)}" — sub-field names must be unique within \`fields\` / \`of\``));
1618
+ }
1619
+
1620
+ const FieldShapeSchema = v__namespace.lazy(() => compositeChecked({
1621
+ type: FieldValueKindSchema,
1622
+ name: FieldEntryName,
1623
+ title: v__namespace.optional(v__namespace.string()),
1624
+ description: v__namespace.optional(v__namespace.string()),
1625
+ fields: v__namespace.optional(v__namespace.array(FieldShapeSchema)),
1626
+ of: v__namespace.optional(v__namespace.array(FieldShapeSchema))
1627
+ })), StoredEditableSchema = v__namespace.union([ v__namespace.literal(!0), NonEmpty ]), AuthoringEditableSchema = v__namespace.union([ v__namespace.literal(!0), v__namespace.array(NonEmpty), NonEmpty ]);
1628
+
1629
+ function fieldBase(editable, group) {
1630
+ return {
1631
+ name: FieldEntryName,
1632
+ title: v__namespace.optional(v__namespace.string()),
1633
+ description: v__namespace.optional(v__namespace.string()),
1634
+ group: v__namespace.optional(group),
1635
+ required: v__namespace.optional(v__namespace.boolean()),
1636
+ initialValue: v__namespace.optional(FieldSourceSchema),
1637
+ editable: v__namespace.optional(editable)
1638
+ };
1639
+ }
1640
+
1641
+ function fieldEntryFields(editable, group) {
1642
+ return {
1643
+ type: FieldKindSchema,
1644
+ ...fieldBase(editable, group),
1645
+ types: v__namespace.optional(v__namespace.pipe(v__namespace.array(NonEmpty), v__namespace.minLength(1, "declare at least one accepted type, or omit `types` to accept any"))),
1646
+ fields: v__namespace.optional(v__namespace.array(FieldShapeSchema)),
1647
+ of: v__namespace.optional(v__namespace.array(FieldShapeSchema))
1648
+ };
1649
+ }
1650
+
1651
+ function literalSeedIssues(entry) {
1652
+ if (entry.initialValue?.type === "literal") return checkLiteralSeed({
1653
+ entryType: entry.type,
1654
+ value: entry.initialValue.value,
1655
+ types: entry.types,
1656
+ fields: entry.fields,
1657
+ of: entry.of
1658
+ });
1659
+ }
1660
+
1661
+ function literalSeedCheck() {
1662
+ return v__namespace.check(entry => literalSeedIssues(entry) === void 0, issue => `initialValue literal does not fit the declared kind: ${(literalSeedIssues(issue.input) ?? []).join("; ")}`);
1663
+ }
1664
+
1665
+ function refTypesCheck() {
1666
+ return v__namespace.check(entry => entry.types === void 0 || entry.type === "doc.ref" || entry.type === "doc.refs", issue => `\`types\` is only valid on \`doc.ref\` / \`doc.refs\` entries, not "${issue.input.type}"`);
1667
+ }
1668
+
1669
+ const FieldEntrySchema = pinned()(v__namespace.pipe(compositeChecked(fieldEntryFields(StoredEditableSchema, StoredGroupMembershipSchema)), refTypesCheck(), literalSeedCheck())), RawAuthoringFieldEntrySchema = pinned()(v__namespace.pipe(compositeChecked(fieldEntryFields(AuthoringEditableSchema, AuthoringGroupMembershipSchema)), refTypesCheck(), literalSeedCheck())), ClaimFieldSchema = pinned()(v__namespace.strictObject({
1670
+ type: v__namespace.literal("claim"),
1671
+ name: FieldEntryName,
1672
+ title: v__namespace.optional(v__namespace.string()),
1673
+ description: v__namespace.optional(v__namespace.string()),
1674
+ group: v__namespace.optional(AuthoringGroupMembershipSchema)
1675
+ }));
1676
+
1677
+ function listSugarFields(type) {
1678
+ return {
1679
+ type: v__namespace.literal(type),
1680
+ ...fieldBase(AuthoringEditableSchema, AuthoringGroupMembershipSchema)
1681
+ };
1682
+ }
1683
+
1684
+ const TodoListFieldSchema = pinned()(v__namespace.strictObject(listSugarFields("todoList"))), NotesFieldSchema = pinned()(v__namespace.strictObject(listSugarFields("notes"))), AuthoringFieldEntrySchema = pinned()(v__namespace.union([ RawAuthoringFieldEntrySchema, ClaimFieldSchema, TodoListFieldSchema, NotesFieldSchema ])), EffectSchema = v__namespace.strictObject({
1685
+ name: NonEmpty,
1686
+ title: v__namespace.optional(v__namespace.string()),
1687
+ description: v__namespace.optional(v__namespace.string()),
1688
+ bindings: v__namespace.optional(v__namespace.record(v__namespace.string(), ConditionSchema)),
1689
+ input: v__namespace.optional(v__namespace.record(v__namespace.string(), v__namespace.unknown())),
1690
+ outputs: v__namespace.optional(v__namespace.array(FieldShapeSchema))
1691
+ }), DefinitionRefSchema = v__namespace.strictObject({
1692
+ name: NonEmpty,
1693
+ version: v__namespace.optional(v__namespace.union([ PositiveInt, v__namespace.literal("latest") ]))
1694
+ }), SubworkflowsSchema = v__namespace.strictObject({
1695
+ forEach: NonEmpty,
1696
+ definition: DefinitionRefSchema,
1697
+ with: v__namespace.optional(v__namespace.record(NonEmpty, ConditionSchema)),
1698
+ context: v__namespace.optional(v__namespace.record(NonEmpty, ConditionSchema)),
1699
+ onExit: v__namespace.optional(picklist([ "detach", "abort" ]))
1700
+ }), ActionParamSchema = v__namespace.strictObject({
1701
+ type: picklist([ "string", "number", "boolean", "url", "dateTime", "actor", "doc.ref", "doc.refs", "json" ]),
1702
+ name: NonEmpty,
1703
+ title: v__namespace.optional(v__namespace.string()),
1704
+ description: v__namespace.optional(v__namespace.string()),
1705
+ required: v__namespace.optional(v__namespace.boolean())
1706
+ });
1707
+
1708
+ function actionFields(op, group) {
1709
+ return {
1710
+ name: NonEmpty,
1711
+ title: v__namespace.optional(v__namespace.string()),
1712
+ description: v__namespace.optional(v__namespace.string()),
1713
+ group: v__namespace.optional(group),
1714
+ when: v__namespace.optional(ConditionSchema),
1715
+ filter: v__namespace.optional(ConditionSchema),
1716
+ params: v__namespace.optional(v__namespace.array(ActionParamSchema)),
1717
+ ops: v__namespace.optional(v__namespace.array(op)),
1718
+ effects: v__namespace.optional(v__namespace.array(EffectSchema)),
1719
+ spawn: v__namespace.optional(SubworkflowsSchema)
1720
+ };
1721
+ }
1722
+
1723
+ const StoredActionSchema = pinned()(v__namespace.strictObject({
1724
+ ...actionFields(StoredOpSchema, StoredGroupMembershipSchema),
1725
+ roles: v__namespace.optional(v__namespace.array(NonEmpty))
1726
+ })), TerminalActivityStatus = picklist(TERMINAL_ACTIVITY_STATUSES), RawAuthoringActionSchema = pinned()(v__namespace.strictObject({
1727
+ ...actionFields(AuthoringOpSchema, AuthoringGroupMembershipSchema),
1728
+ roles: v__namespace.optional(v__namespace.array(NonEmpty)),
1729
+ status: v__namespace.optional(TerminalActivityStatus)
1730
+ })), ClaimActionSchema = pinned()(v__namespace.strictObject({
1731
+ type: v__namespace.literal("claim"),
1732
+ name: NonEmpty,
1733
+ title: v__namespace.optional(v__namespace.string()),
1734
+ description: v__namespace.optional(v__namespace.string()),
1735
+ group: v__namespace.optional(AuthoringGroupMembershipSchema),
1736
+ field: v__namespace.union([ NonEmpty, AuthoringFieldRefSchema ]),
1737
+ roles: v__namespace.optional(v__namespace.array(NonEmpty)),
1738
+ filter: v__namespace.optional(ConditionSchema),
1739
+ params: v__namespace.optional(v__namespace.array(ActionParamSchema)),
1740
+ effects: v__namespace.optional(v__namespace.array(EffectSchema))
1741
+ })), AuthoringActionSchema = pinned()(v__namespace.lazy(input => typeof input == "object" && input !== null && "type" in input ? ClaimActionSchema : RawAuthoringActionSchema));
1742
+
1743
+ function activityFields({field: field, action: action, target: target, group: group}) {
1744
+ return {
1745
+ name: NonEmpty,
1746
+ title: v__namespace.optional(v__namespace.string()),
1747
+ description: v__namespace.optional(v__namespace.string()),
1748
+ groups: v__namespace.optional(v__namespace.array(GroupSchema)),
1749
+ group: v__namespace.optional(group),
1750
+ target: v__namespace.optional(target),
1751
+ filter: v__namespace.optional(ConditionSchema),
1752
+ requirements: v__namespace.optional(v__namespace.record(NonEmpty, ConditionSchema)),
1753
+ actions: v__namespace.optional(v__namespace.array(action)),
1754
+ fields: v__namespace.optional(v__namespace.array(field))
1755
+ };
1756
+ }
1757
+
1758
+ const StoredActivitySchema = pinned()(v__namespace.strictObject(activityFields({
1759
+ field: FieldEntrySchema,
1760
+ action: StoredActionSchema,
1761
+ target: StoredManualTargetSchema,
1762
+ group: StoredGroupMembershipSchema
1763
+ }))), AuthoringActivitySchema = pinned()(v__namespace.strictObject(activityFields({
1764
+ field: AuthoringFieldEntrySchema,
1765
+ action: AuthoringActionSchema,
1766
+ target: AuthoringManualTargetSchema,
1767
+ group: AuthoringGroupMembershipSchema
1768
+ })));
1769
+
1770
+ function transitionFields(when) {
1771
+ return {
1772
+ name: NonEmpty,
1773
+ title: v__namespace.optional(v__namespace.string()),
1774
+ description: v__namespace.optional(v__namespace.string()),
1775
+ to: NonEmpty,
1776
+ when: when
1777
+ };
1778
+ }
1779
+
1780
+ const StoredTransitionSchema = pinned()(v__namespace.strictObject(transitionFields(ConditionSchema))), AuthoringTransitionSchema = pinned()(v__namespace.strictObject(transitionFields(v__namespace.optional(ConditionSchema)))), GuardActionSchema = picklist(MUTATION_GUARD_ACTIONS), GuardReadPath = v__namespace.pipe(NonEmpty, v__namespace.check(path => !/[\r\n\u2028\u2029]/.test(path), "a guard read path cannot contain a line break")), GuardReadSchema = v__namespace.variant("type", [ v__namespace.strictObject({
1781
+ type: v__namespace.literal("self")
1782
+ }), v__namespace.strictObject({
1783
+ type: v__namespace.literal("now")
1784
+ }), v__namespace.strictObject({
1785
+ type: v__namespace.literal("fieldRead"),
1786
+ field: FieldEntryName,
1787
+ path: v__namespace.optional(GuardReadPath)
1788
+ }), v__namespace.strictObject({
1789
+ type: v__namespace.literal("effectsRead"),
1790
+ effect: v__namespace.pipe(NonEmpty, v__namespace.check(name => !name.includes("'"), "an effect name cannot contain `'`")),
1791
+ path: v__namespace.optional(GuardReadPath)
1792
+ }) ]);
1793
+
1794
+ function guardMatchFields(read) {
1795
+ return {
1796
+ types: v__namespace.optional(v__namespace.array(NonEmpty)),
1797
+ idRefs: v__namespace.optional(v__namespace.array(read)),
1798
+ idPatterns: v__namespace.optional(v__namespace.array(NonEmpty)),
1799
+ actions: v__namespace.pipe(v__namespace.array(GuardActionSchema), v__namespace.minLength(1, "a guard must match at least one action"))
1800
+ };
1801
+ }
1802
+
1803
+ function guardFields(read) {
1804
+ return {
1805
+ name: NonEmpty,
1806
+ title: v__namespace.optional(v__namespace.string()),
1807
+ description: v__namespace.optional(v__namespace.string()),
1808
+ match: v__namespace.strictObject(guardMatchFields(read)),
1809
+ predicate: v__namespace.optional(v__namespace.string()),
1810
+ metadata: v__namespace.optional(v__namespace.record(NonEmpty, read))
1811
+ };
1812
+ }
1813
+
1814
+ const GuardSchema = v__namespace.strictObject(guardFields(NonEmpty)), AuthoringGuardSchema = v__namespace.strictObject(guardFields(GuardReadSchema));
1815
+
1816
+ function stageFields({field: field, activity: activity, transition: transition, guard: guard, editable: editable}) {
1817
+ return {
1818
+ name: NonEmpty,
1819
+ title: v__namespace.optional(v__namespace.string()),
1820
+ description: v__namespace.optional(v__namespace.string()),
1821
+ groups: v__namespace.optional(v__namespace.array(GroupSchema)),
1822
+ activities: v__namespace.optional(v__namespace.array(activity)),
1823
+ transitions: v__namespace.optional(v__namespace.array(transition)),
1824
+ guards: v__namespace.optional(v__namespace.array(guard)),
1825
+ fields: v__namespace.optional(v__namespace.array(field)),
1826
+ editable: v__namespace.optional(v__namespace.record(FieldEntryName, editable))
1827
+ };
1828
+ }
1829
+
1830
+ const StoredStageSchema = pinned()(v__namespace.strictObject(stageFields({
1831
+ field: FieldEntrySchema,
1832
+ activity: StoredActivitySchema,
1833
+ transition: StoredTransitionSchema,
1834
+ guard: GuardSchema,
1835
+ editable: StoredEditableSchema
1836
+ }))), AuthoringStageSchema = pinned()(v__namespace.strictObject(stageFields({
1837
+ field: AuthoringFieldEntrySchema,
1838
+ activity: AuthoringActivitySchema,
1839
+ transition: AuthoringTransitionSchema,
1840
+ guard: AuthoringGuardSchema,
1841
+ editable: AuthoringEditableSchema
1842
+ }))), RoleAliasesSchema = v__namespace.record(NonEmpty, v__namespace.pipe(v__namespace.array(NonEmpty), v__namespace.minLength(1, "a role alias must list at least one fulfilling role"))), WORKFLOW_LIFECYCLES = [ "standalone", "child" ], START_KINDS = [ "interactive", "autonomous" ];
1843
+
1844
+ function startFields(kind) {
1845
+ return {
1846
+ kind: kind,
1847
+ filter: v__namespace.optional(ConditionSchema)
1848
+ };
1849
+ }
1850
+
1851
+ const StoredStartSchema = pinned()(v__namespace.strictObject(startFields(picklist(START_KINDS)))), AuthoringStartSchema = pinned()(v__namespace.strictObject(startFields(v__namespace.optional(picklist(START_KINDS)))));
1852
+
1853
+ function workflowFields({field: field, stage: stage, start: start}) {
1854
+ return {
1855
+ name: NonEmpty,
1856
+ title: NonEmpty,
1857
+ description: v__namespace.optional(v__namespace.string()),
1858
+ groups: v__namespace.optional(v__namespace.array(GroupSchema)),
1859
+ lifecycle: v__namespace.optional(picklist(WORKFLOW_LIFECYCLES)),
1860
+ start: v__namespace.optional(start),
1861
+ initialStage: NonEmpty,
1862
+ fields: v__namespace.optional(v__namespace.array(field)),
1863
+ stages: v__namespace.pipe(v__namespace.array(stage), v__namespace.minLength(1, "must declare at least one stage")),
1864
+ predicates: v__namespace.optional(v__namespace.record(groqIdentifier("`$<name>`"), ConditionSchema)),
1865
+ roleAliases: v__namespace.optional(RoleAliasesSchema)
1866
+ };
1867
+ }
1868
+
1869
+ const WorkflowDefinitionSchema = pinned()(v__namespace.strictObject(workflowFields({
1870
+ field: FieldEntrySchema,
1871
+ stage: StoredStageSchema,
1872
+ start: StoredStartSchema
1873
+ })));
1874
+
1875
+ function parseStoredDefinition(input, label) {
1876
+ return parseOrThrow({
1877
+ schema: WorkflowDefinitionSchema,
1878
+ input: input,
1879
+ label: label
1880
+ });
1881
+ }
1882
+
1883
+ const AuthoringWorkflowSchema = pinned()(v__namespace.strictObject(workflowFields({
1884
+ field: AuthoringFieldEntrySchema,
1885
+ stage: AuthoringStageSchema,
1886
+ start: AuthoringStartSchema
1887
+ }))), WORKFLOW_DEFINITION_TYPE = "sanity.workflow.definition";
1888
+
1889
+ function isStartableDefinition(definition) {
1890
+ return definition.lifecycle !== "child";
1891
+ }
1892
+
1893
+ function startKindOf(definition) {
1894
+ return definition.start?.kind ?? "interactive";
1895
+ }
1896
+
1897
+ function isSubjectEntry(entry) {
1898
+ return entry.required === !0 && (entry.type === "doc.ref" || entry.type === "doc.refs");
1899
+ }
1900
+
1901
+ function isInputSourced(entry) {
1902
+ return entry.initialValue?.type === "input";
1903
+ }
1904
+
1905
+ function formatValidationError(label, issues) {
1906
+ const lines = issues.map(issue => ` - ${issue.path.length === 0 ? "(root)" : formatIssuePath(issue.path)}: ${issue.message}`);
1907
+ return `${label} failed validation (${issues.length} issue${issues.length === 1 ? "" : "s"}):\n${lines.join(`\n`)}`;
1908
+ }
1909
+
1910
+ function issuesFromValibot(issues) {
1911
+ return issues.map(issue => ({
1912
+ path: issue.path ? issue.path.map(item => item.key) : [],
1913
+ message: issue.message
1914
+ }));
1915
+ }
1916
+
1917
+ function formatIssuePath(path) {
1918
+ let out = "";
1919
+ for (const seg of path) typeof seg == "number" ? out += `[${seg}]` : out += out.length === 0 ? String(seg) : `.${String(seg)}`;
1920
+ return out;
1921
+ }
1922
+
1923
+ function parseOrThrow({schema: schema, input: input, label: label}) {
1924
+ const result = v__namespace.safeParse(schema, input);
1925
+ if (!result.success) throw new Error(formatValidationError(label, issuesFromValibot(result.issues)));
1926
+ return result.output;
1927
+ }
1928
+
1929
+ function labelFor(fn, value) {
1930
+ if (value && typeof value == "object" && "name" in value) {
1931
+ const raw = value.name;
1932
+ if (typeof raw == "string") return `${fn}("${raw}")`;
1933
+ }
1934
+ return fn;
1935
+ }
1936
+
1937
+ function knownList(ids) {
1938
+ const quoted = [ ...ids ].map(s => `"${s}"`);
1939
+ return quoted.length > 0 ? quoted.join(", ") : "(none)";
1940
+ }
1941
+
1942
+ function checkDuplicates({names: names, what: what, issues: issues}) {
1943
+ const seen = /* @__PURE__ */ new Set;
1944
+ for (const {name: name, path: path} of names) seen.has(name) && issues.push({
1945
+ path: path,
1946
+ message: `duplicate ${what} "${name}" (already declared earlier)`
1947
+ }), seen.add(name);
1948
+ return seen;
1949
+ }
1950
+
1951
+ function checkStages(def, issues) {
1952
+ const stageNames = checkDuplicates({
1953
+ names: def.stages.map((stage, i) => ({
1954
+ name: stage.name,
1955
+ path: [ "stages", i, "name" ]
1956
+ })),
1957
+ what: "stage name",
1958
+ issues: issues
1959
+ });
1960
+ for (const [i, stage] of def.stages.entries()) {
1961
+ const activityNames = checkDuplicates({
1962
+ names: (stage.activities ?? []).map((activity, j) => ({
1963
+ name: activity.name,
1964
+ path: [ "stages", i, "activities", j, "name" ]
1965
+ })),
1966
+ what: `activity name in stage "${stage.name}"`,
1967
+ issues: issues
1968
+ });
1969
+ checkDuplicates({
1970
+ names: (stage.transitions ?? []).map((t, k) => ({
1971
+ name: t.name,
1972
+ path: [ "stages", i, "transitions", k, "name" ]
1973
+ })),
1974
+ what: `transition name in stage "${stage.name}"`,
1975
+ issues: issues
1976
+ }), checkActivities({
1977
+ def: def,
1978
+ i: i,
1979
+ activityNames: activityNames,
1980
+ issues: issues
1981
+ });
1982
+ }
1983
+ return stageNames;
1984
+ }
1985
+
1986
+ function checkActivities({def: def, i: i, activityNames: activityNames, issues: issues}) {
1987
+ const stage = def.stages[i];
1988
+ for (const [j, activity] of (stage.activities ?? []).entries()) {
1989
+ const path = [ "stages", i, "activities", j ];
1990
+ checkDuplicates({
1991
+ names: (activity.actions ?? []).map((action, a) => ({
1992
+ name: action.name,
1993
+ path: [ ...path, "actions", a, "name" ]
1994
+ })),
1995
+ what: `action name in activity "${activity.name}"`,
1996
+ issues: issues
1997
+ }), checkStatusSetTargets({
1998
+ activity: activity,
1999
+ activityNames: activityNames,
2000
+ path: path,
2001
+ issues: issues
2002
+ });
2003
+ }
2004
+ }
2005
+
2006
+ function checkStatusSetTargets({activity: activity, activityNames: activityNames, path: path, issues: issues}) {
2007
+ for (const [a, action] of (activity.actions ?? []).entries()) checkStatusSetOps({
2008
+ ops: action.ops,
2009
+ activityNames: activityNames,
2010
+ path: [ ...path, "actions", a, "ops" ],
2011
+ issues: issues
2012
+ });
2013
+ }
2014
+
2015
+ function checkStatusSetOps({ops: ops, activityNames: activityNames, path: path, issues: issues}) {
2016
+ for (const [o, op] of (ops ?? []).entries()) op.type !== "status.set" || activityNames.has(op.activity) || issues.push({
2017
+ path: [ ...path, o, "activity" ],
2018
+ message: `status.set targets activity "${op.activity}" which is not declared in this stage. Known activities: ${knownList(activityNames)}`
2019
+ });
2020
+ }
2021
+
2022
+ function checkInitialStage({def: def, stageNames: stageNames, issues: issues}) {
2023
+ stageNames.has(def.initialStage) || issues.push({
2024
+ path: [ "initialStage" ],
2025
+ message: `initialStage "${def.initialStage}" is not a declared stage. Known stages: ${knownList(stageNames)}`
2026
+ });
2027
+ }
2028
+
2029
+ function checkTransitionTargets({def: def, stageNames: stageNames, issues: issues}) {
2030
+ for (const [i, stage] of def.stages.entries()) for (const [k, t] of (stage.transitions ?? []).entries()) stageNames.has(t.to) || issues.push({
2031
+ path: [ "stages", i, "transitions", k, "to" ],
2032
+ message: `transition target "${t.to}" is not a declared stage. Known stages: ${knownList(stageNames)}`
2033
+ });
2034
+ }
2035
+
2036
+ function checkStageReachability({def: def, stageNames: stageNames, issues: issues}) {
2037
+ if (!stageNames.has(def.initialStage)) return;
2038
+ const stagesByName = /* @__PURE__ */ new Map;
2039
+ for (const stage of def.stages) stagesByName.has(stage.name) || stagesByName.set(stage.name, stage);
2040
+ const reached = /* @__PURE__ */ new Set([ def.initialStage ]), frontier = [ def.initialStage ];
2041
+ for (;frontier.length > 0; ) {
2042
+ const stage = stagesByName.get(frontier.pop());
2043
+ for (const t of stage?.transitions ?? []) reached.has(t.to) || !stageNames.has(t.to) || (reached.add(t.to),
2044
+ frontier.push(t.to));
2045
+ }
2046
+ for (const [i, stage] of def.stages.entries()) reached.has(stage.name) || issues.push({
2047
+ path: [ "stages", i, "name" ],
2048
+ message: `stage "${stage.name}" is unreachable — no transition path leads from initialStage "${def.initialStage}" to it, so no instance can ever enter it. Add a transition targeting it, or remove the stage`
2049
+ });
2050
+ }
2051
+
2052
+ function effectNameSites(def) {
2053
+ const sites = [];
2054
+ for (const [i, stage] of def.stages.entries()) for (const [j, activity] of (stage.activities ?? []).entries()) for (const [a, action] of (activity.actions ?? []).entries()) collectEffects({
2055
+ effects: action.effects,
2056
+ path: [ "stages", i, "activities", j, "actions", a, "effects" ],
2057
+ sites: sites
2058
+ });
2059
+ return sites;
2060
+ }
2061
+
2062
+ function checkEffectNames(def, issues) {
2063
+ checkDuplicates({
2064
+ names: effectNameSites(def),
2065
+ what: "effect name (registry key — unique per definition)",
2066
+ issues: issues
2067
+ });
2068
+ }
2069
+
2070
+ function collectEffects({effects: effects, path: path, sites: sites}) {
2071
+ for (const [e, effect] of (effects ?? []).entries()) sites.push({
2072
+ name: effect.name,
2073
+ path: [ ...path, e, "name" ]
2074
+ });
2075
+ }
2076
+
2077
+ function checkGuardNames(def, issues) {
2078
+ const sites = def.stages.flatMap((stage, i) => (stage.guards ?? []).map((guard, g) => ({
2079
+ name: guard.name,
2080
+ path: [ "stages", i, "guards", g, "name" ]
2081
+ })));
2082
+ checkDuplicates({
2083
+ names: sites,
2084
+ what: "guard name (the guard lake _id derives from it — unique per definition)",
2085
+ issues: issues
2086
+ });
2087
+ }
2088
+
2089
+ function fieldScopes(def) {
2090
+ const scopes = [ {
2091
+ entries: def.fields,
2092
+ scope: "workflow",
2093
+ path: [ "fields" ],
2094
+ label: "workflow fields"
2095
+ } ];
2096
+ for (const [i, stage] of def.stages.entries()) {
2097
+ scopes.push({
2098
+ entries: stage.fields,
2099
+ scope: "stage",
2100
+ path: [ "stages", i, "fields" ],
2101
+ label: `stage "${stage.name}" fields`
2102
+ });
2103
+ for (const [j, activity] of (stage.activities ?? []).entries()) scopes.push({
2104
+ entries: activity.fields,
2105
+ scope: "activity",
2106
+ path: [ "stages", i, "activities", j, "fields" ],
2107
+ label: `activity "${activity.name}" fields`
2108
+ });
2109
+ }
2110
+ return scopes;
2111
+ }
2112
+
2113
+ function checkRequiredField(def, issues) {
2114
+ for (const {entries: entries, scope: scope, path: path, label: label} of fieldScopes(def)) for (const [n, entry] of (entries ?? []).entries()) if (entry.required === !0) {
2115
+ if (scope !== "workflow") issues.push({
2116
+ path: [ ...path, n, "required" ],
2117
+ message: `${label} entry "${entry.name}" is \`required\`, but \`required\` applies only to workflow-scope \`input\` entries — only those are caller-supplied at start/spawn`
2118
+ }); else if (entry.initialValue?.type !== "input") {
2119
+ const seed = entry.initialValue?.type ?? "working memory (no initialValue)";
2120
+ issues.push({
2121
+ path: [ ...path, n, "required" ],
2122
+ message: `field entry "${entry.name}" has initialValue "${seed}" — \`required\` applies only to \`input\`-sourced entries (the caller fills them at start/spawn)`
2123
+ });
2124
+ }
2125
+ }
2126
+ }
2127
+
2128
+ function checkFieldEntryNames(def, issues) {
2129
+ for (const {entries: entries, path: path, label: label} of fieldScopes(def)) checkDuplicates({
2130
+ names: (entries ?? []).map((entry, n) => ({
2131
+ name: entry.name,
2132
+ path: [ ...path, n, "name" ]
2133
+ })),
2134
+ what: `field entry name in ${label}`,
2135
+ issues: issues
2136
+ });
2137
+ }
2138
+
2139
+ function checkPredicates(def, issues) {
2140
+ const reserved = new Set(RESERVED_CONDITION_VARS), names = Object.keys(def.predicates ?? {});
2141
+ for (const name of names) reserved.has(name) && issues.push({
2142
+ path: [ "predicates", name ],
2143
+ message: `predicate "${name}" shadows the built-in $${name} — pick another name`
2144
+ });
2145
+ for (const [name, groq2] of Object.entries(def.predicates ?? {})) checkPredicateBody({
2146
+ name: name,
2147
+ groq: groq2,
2148
+ names: names,
2149
+ issues: issues
2150
+ });
2151
+ }
2152
+
2153
+ function checkPredicateBody({name: name, groq: groq2, names: names, issues: issues}) {
2154
+ const reads = conditionParameterNames(groq2);
2155
+ for (const caller of CALLER_BOUND_VARS) reads.has(caller) && issues.push({
2156
+ path: [ "predicates", name ],
2157
+ message: `predicate "${name}" reads $${caller} — predicates are instance-level (pre-evaluated once, without a caller); compose caller gates at the condition site instead`
2158
+ });
2159
+ for (const other of names) other === name || !reads.has(other) || issues.push({
2160
+ path: [ "predicates", name ],
2161
+ message: `predicate "${name}" references predicate $${other} — predicates may read built-in vars only (no cross-references; compose at the condition site instead)`
2162
+ });
2163
+ }
2164
+
2165
+ function entryNames(entries) {
2166
+ return new Set((entries ?? []).map(entry => entry.name));
2167
+ }
2168
+
2169
+ function checkUnboundCallerVars(def, issues) {
2170
+ for (const site of conditionSites(def)) {
2171
+ const reads = conditionParameterNames(site.groq);
2172
+ for (const name of unboundCallerVars(site.policy)) reads.has(name) && issues.push({
2173
+ path: site.path,
2174
+ message: callerVarMessage(site, name)
2175
+ });
2176
+ }
2177
+ }
2178
+
2179
+ function unboundCallerVars(policy) {
2180
+ return policy === "cascade" ? CALLER_BOUND_VARS : policy === "caller-bound" ? [ "params" ] : policy === "triggered-payload" ? [ "can", "params" ] : [ "can" ];
2181
+ }
2182
+
2183
+ function callerVarMessage(site, name) {
2184
+ return site.policy === "cascade" ? `${site.label} reads $${name} — cascade gates (transition \`when\`s, activity \`filter\`s, a cascade-fired action's \`when\`/\`filter\`) must resolve identically no matter whose token drives the cascade ($assigned is constant false, the other caller vars hold no value); gate on instance state (e.g. a field an action wrote), or pin executing identities with \`roles\`` : site.policy === "caller-bound" ? `${site.label} reads $${name} — $params (the firing action's args) is bound only while the action's effect bindings and where-op \`where\`s evaluate; this site never binds it, so the condition could never pass. Bind $params in an effect binding or a where-op instead, or gate on a field an action wrote` : site.policy === "triggered-payload" && name === "params" ? `${site.label} reads $params — a cascade-fired action has no caller to supply args, so $params never holds a value in its payload; read fields or effect outputs instead` : `${site.label} reads $can — $can (the caller's grants) is bound only in the caller-bound projection (action filters, requirements, editable predicates) and never holds a value in cascade conditions; move the check to one of those sites or drop $can`;
2185
+ }
2186
+
2187
+ function conditionSites(def) {
2188
+ const sites = [], workflowLayer = {
2189
+ scope: "workflow",
2190
+ names: entryNames(def.fields)
2191
+ };
2192
+ collectEditableSites({
2193
+ entries: def.fields,
2194
+ path: [ "fields" ],
2195
+ labelPrefix: "workflow",
2196
+ fields: "context-dependent",
2197
+ sites: sites
2198
+ });
2199
+ for (const [i, stage] of def.stages.entries()) collectStageConditionSites({
2200
+ stage: stage,
2201
+ i: i,
2202
+ workflowLayer: workflowLayer,
2203
+ sites: sites
2204
+ });
2205
+ return sites;
2206
+ }
2207
+
2208
+ function collectStageConditionSites({stage: stage, i: i, workflowLayer: workflowLayer, sites: sites}) {
2209
+ const stageLayer = {
2210
+ scope: "stage",
2211
+ names: entryNames(stage.fields)
2212
+ }, stageFields2 = [ stageLayer, workflowLayer ], editableWindow = [ stageLayer, workflowLayer, ...(stage.activities ?? []).map(activity => ({
2213
+ scope: "activity",
2214
+ names: entryNames(activity.fields)
2215
+ })) ];
2216
+ for (const [k, t] of (stage.transitions ?? []).entries()) sites.push({
2217
+ groq: t.when,
2218
+ path: [ "stages", i, "transitions", k, "when" ],
2219
+ label: `transition "${t.name}" when`,
2220
+ policy: "cascade",
2221
+ fields: stageFields2
2222
+ });
2223
+ collectEditableSites({
2224
+ entries: stage.fields,
2225
+ path: [ "stages", i, "fields" ],
2226
+ labelPrefix: `stage "${stage.name}"`,
2227
+ fields: editableWindow,
2228
+ sites: sites
2229
+ });
2230
+ for (const [name, editable] of Object.entries(stage.editable ?? {})) typeof editable == "string" && sites.push({
2231
+ groq: editable,
2232
+ path: [ "stages", i, "editable", name ],
2233
+ label: `stage "${stage.name}" editable override "${name}"`,
2234
+ policy: "caller-bound",
2235
+ fields: editableWindow
2236
+ });
2237
+ for (const [j, activity] of (stage.activities ?? []).entries()) collectActivityConditionSites({
2238
+ activity: activity,
2239
+ path: [ "stages", i, "activities", j ],
2240
+ stageFields: stageFields2,
2241
+ sites: sites
2242
+ });
2243
+ }
2244
+
2245
+ function collectEditableSites({entries: entries, path: path, labelPrefix: labelPrefix, fields: fields, sites: sites}) {
2246
+ for (const [n, entry] of (entries ?? []).entries()) typeof entry.editable == "string" && sites.push({
2247
+ groq: entry.editable,
2248
+ path: [ ...path, n, "editable" ],
2249
+ label: `${labelPrefix} field "${entry.name}" editable`,
2250
+ policy: "caller-bound",
2251
+ fields: fields
2252
+ });
2253
+ }
2254
+
2255
+ function collectOpWhereSites({ops: ops, path: path, label: label, policy: policy, fields: fields, sites: sites}) {
2256
+ for (const [o, op] of (ops ?? []).entries()) op.where !== void 0 && sites.push({
2257
+ groq: op.where,
2258
+ path: [ ...path, o, "where" ],
2259
+ label: `${label} ${op.type} where`,
2260
+ ...policy !== void 0 ? {
2261
+ policy: policy
2262
+ } : {},
2263
+ fields: fields
2264
+ });
2265
+ }
2266
+
2267
+ function collectActivityConditionSites({activity: activity, path: path, stageFields: stageFields2, sites: sites}) {
2268
+ const fields = [ {
2269
+ scope: "activity",
2270
+ names: entryNames(activity.fields)
2271
+ }, ...stageFields2 ];
2272
+ activity.filter !== void 0 && sites.push({
2273
+ groq: activity.filter,
2274
+ path: [ ...path, "filter" ],
2275
+ label: `activity "${activity.name}".filter`,
2276
+ policy: "cascade",
2277
+ fields: fields
2278
+ });
2279
+ for (const [name, groq2] of Object.entries(activity.requirements ?? {})) sites.push({
2280
+ groq: groq2,
2281
+ path: [ ...path, "requirements", name ],
2282
+ label: `activity "${activity.name}" requirement "${name}"`,
2283
+ policy: "caller-bound",
2284
+ fields: fields
2285
+ });
2286
+ collectEditableSites({
2287
+ entries: activity.fields,
2288
+ path: [ ...path, "fields" ],
2289
+ labelPrefix: `activity "${activity.name}"`,
2290
+ fields: fields,
2291
+ sites: sites
2292
+ }), collectActionConditionSites({
2293
+ activity: activity,
2294
+ path: path,
2295
+ fields: fields,
2296
+ sites: sites
2297
+ });
2298
+ }
2299
+
2300
+ function collectActionConditionSites({activity: activity, path: path, fields: fields, sites: sites}) {
2301
+ for (const [a, action] of (activity.actions ?? []).entries()) {
2302
+ const actionPath = [ ...path, "actions", a ], cascadeFired = action.when !== void 0;
2303
+ action.when !== void 0 && sites.push({
2304
+ groq: action.when,
2305
+ path: [ ...actionPath, "when" ],
2306
+ label: `action "${action.name}" when`,
2307
+ policy: "cascade",
2308
+ fields: fields
2309
+ }), action.filter !== void 0 && sites.push({
2310
+ groq: action.filter,
2311
+ path: [ ...actionPath, "filter" ],
2312
+ label: `action "${action.name}" filter`,
2313
+ policy: cascadeFired ? "cascade" : "caller-bound",
2314
+ fields: fields
2315
+ });
2316
+ const payloadPolicy = cascadeFired ? "triggered-payload" : void 0;
2317
+ collectBindingSites({
2318
+ effects: action.effects,
2319
+ path: [ ...actionPath, "effects" ],
2320
+ label: `action "${action.name}"`,
2321
+ policy: payloadPolicy,
2322
+ fields: fields,
2323
+ sites: sites
2324
+ }), collectOpWhereSites({
2325
+ ops: action.ops,
2326
+ path: [ ...actionPath, "ops" ],
2327
+ label: `action "${action.name}"`,
2328
+ policy: payloadPolicy,
2329
+ fields: fields,
2330
+ sites: sites
2331
+ }), collectSpawnSites({
2332
+ action: action,
2333
+ path: actionPath,
2334
+ fields: fields,
2335
+ sites: sites
2336
+ });
2337
+ }
2338
+ }
2339
+
2340
+ function collectSpawnSites({action: action, path: path, fields: fields, sites: sites}) {
2341
+ const spawn = action.spawn;
2342
+ if (spawn === void 0) return;
2343
+ const spawnPath = [ ...path, "spawn" ];
2344
+ sites.push({
2345
+ groq: spawn.forEach,
2346
+ path: [ ...spawnPath, "forEach" ],
2347
+ label: `action "${action.name}".spawn.forEach`,
2348
+ policy: "cascade",
2349
+ fields: fields
2350
+ });
2351
+ for (const [group, record] of [ [ "with", spawn.with ], [ "context", spawn.context ] ]) for (const [key, groq2] of Object.entries(record ?? {})) sites.push({
2352
+ groq: groq2,
2353
+ path: [ ...spawnPath, group, key ],
2354
+ label: `action "${action.name}".spawn.${group} "${key}"`,
2355
+ policy: "triggered-payload",
2356
+ fields: fields
2357
+ });
2358
+ }
2359
+
2360
+ function collectBindingSites({effects: effects, path: path, label: label, policy: policy, fields: fields, sites: sites}) {
2361
+ for (const [e, effect] of (effects ?? []).entries()) for (const [key, groq2] of Object.entries(effect.bindings ?? {})) sites.push({
2362
+ groq: groq2,
2363
+ path: [ ...path, e, "bindings", key ],
2364
+ label: `${label} effect "${effect.name}" binding "${key}"`,
2365
+ ...policy !== void 0 ? {
2366
+ policy: policy
2367
+ } : {},
2368
+ fields: fields
2369
+ });
2370
+ }
2371
+
2372
+ function checkAssigneesEntries(def, issues) {
2373
+ for (const {entries: entries, path: path} of fieldScopes(def)) (entries ?? []).filter(e => e.type === "assignees").length <= 1 || issues.push({
2374
+ path: path,
2375
+ message: "at most one assignees-kind field entry per scope — the inbox reads it by kind"
2376
+ });
2377
+ }
2378
+
2379
+ function checkActivityTerminalPaths(def, issues) {
2380
+ for (const [i, stage] of def.stages.entries()) {
2381
+ const resolvable = terminallyResolvableActivities(stage);
2382
+ for (const [j, activity] of (stage.activities ?? []).entries()) resolvable.has(activity.name) || issues.push({
2383
+ path: [ "stages", i, "activities", j ],
2384
+ message: `activity "${activity.name}" has no path to a terminal status — no action in stage "${stage.name}" resolves it (\`status: 'done'\` on one of its actions, a \`{when: <condition>, status: 'done'}\` trigger, or a sibling's \`status.set\`), so it can never finish and the stage's \`$allActivitiesDone\` gate would wedge`
2385
+ });
2386
+ }
2387
+ }
2388
+
2389
+ function terminallyResolvableActivities(stage) {
2390
+ const terminalSets = (stage.activities ?? []).flatMap(activity => activity.actions ?? []).flatMap(action => action.ops ?? []).filter(op => op.type === "status.set").filter(op => TERMINAL_ACTIVITY_STATUSES.includes(op.status));
2391
+ return new Set(terminalSets.map(op => op.activity));
2392
+ }
2393
+
2394
+ function checkTerminalStageActivities(def, issues) {
2395
+ for (const [i, stage] of def.stages.entries()) (stage.transitions ?? []).length > 0 || (stage.activities ?? []).length === 0 || issues.push({
2396
+ path: [ "stages", i, "activities" ],
2397
+ message: `stage "${stage.name}" is terminal (no transitions) but declares activities — entering a terminal stage completes the instance, so they can never run. Move the work to a \`when\` action in the source stage (it commits in the hop that moves here), or give the stage a transition out`
2398
+ });
2399
+ }
2400
+
2401
+ function storedRolesIssue(action) {
2402
+ if (action.roles !== void 0) {
2403
+ if (action.roles.length === 0) return `action "${action.name}" stores \`roles: []\`, which names no roles — the runtime reads an empty pin as "anyone may execute", the opposite of a pin. Omit \`roles\` to allow any identity, or list at least one role`;
2404
+ if (action.when === void 0) return `action "${action.name}" stores \`roles\` but is fireAction-fired (no \`when\`) — the stored pin is only read on cascade fires. Author \`roles\` normally (it desugars into the action's \`filter\`), or add \`when\` to make it a trigger`;
2405
+ }
2406
+ }
2407
+
2408
+ function checkStoredRolesPlacement(def, issues) {
2409
+ for (const [i, stage] of def.stages.entries()) for (const [j, activity] of (stage.activities ?? []).entries()) for (const [a, action] of (activity.actions ?? []).entries()) {
2410
+ const message = storedRolesIssue(action);
2411
+ message !== void 0 && issues.push({
2412
+ path: [ "stages", i, "activities", j, "actions", a, "roles" ],
2413
+ message: message
2414
+ });
2415
+ }
2416
+ }
2417
+
2418
+ function checkTriggeredActionParams(def, issues) {
2419
+ for (const [i, stage] of def.stages.entries()) for (const [j, activity] of (stage.activities ?? []).entries()) for (const [a, action] of (activity.actions ?? []).entries()) action.when === void 0 || (action.params ?? []).length === 0 || issues.push({
2420
+ path: [ "stages", i, "activities", j, "actions", a, "params" ],
2421
+ message: `action "${action.name}" declares params but is cascade-fired (\`when\`) — no caller ever supplies args to a trigger. Drop the params, or drop \`when\` to make it a fireAction-fired action`
2422
+ });
2423
+ }
2424
+
2425
+ function checkStart(def, issues) {
2426
+ if (def.start !== void 0 && (def.lifecycle === "child" && issues.push({
2427
+ path: [ "start" ],
2428
+ message: "a spawn-only (lifecycle 'child') definition declares `start` — children are instantiated by a parent's `spawn`, never started standalone, so the block would never apply. Remove `start`, or drop `lifecycle: 'child'`"
2429
+ }), checkStartFilterReads(def, issues), def.start.kind === "autonomous")) for (const [n, entry] of (def.fields ?? []).entries()) entry.required !== !0 || isSubjectEntry(entry) || issues.push({
2430
+ path: [ "fields", n, "required" ],
2431
+ message: `start.kind 'autonomous' means runs are initiated by a system reacting to a document, so every required input must be derivable from that triggering document — required entry "${entry.name}" (kind "${entry.type}") is not a document reference. Make it optional, seed it another way (query/literal), or declare it doc.ref / doc.refs`
2432
+ });
2433
+ }
2434
+
2435
+ function checkStartFilterReads(def, issues) {
2436
+ const filter = def.start?.filter;
2437
+ if (filter === void 0) return;
2438
+ const bindable = new Set((def.fields ?? []).filter(isInputSourced).map(entry => entry.name)), declared = new Set((def.fields ?? []).map(entry => entry.name));
2439
+ for (const name of conditionFieldReadNames(filter)) bindable.has(name) || issues.push({
2440
+ path: [ "start", "filter" ],
2441
+ message: declared.has(name) ? `start.filter reads $fields.${name}, but "${name}" is not an \`input\`-sourced entry — $fields binds only the caller's input entries (query/literal/fieldRead entries resolve at materialisation, after any read-side evaluation), so the read is GROQ null and the filter silently never passes. Bindable (input) fields: ${knownList(bindable)}` : `start.filter reads $fields.${name}, but no workflow-scope field entry named "${name}" is declared — $fields binds only the caller's input entries, so the read is GROQ null and the filter silently never passes. Bindable (input) fields: ${knownList(bindable)}`
2442
+ });
2443
+ readsRootDocument(filter) && !(def.fields ?? []).some(isSubjectEntry) && issues.push({
2444
+ path: [ "start", "filter" ],
2445
+ message: "start.filter reads the candidate document (its root), but the definition declares no required subject entry (a required doc.ref / doc.refs input) — a read surface would never have a document to bind as root, so the filter could never pass. Declare a required subject entry, or gate on $fields / the dataset instead"
2446
+ });
2447
+ }
2448
+
2449
+ function checkGroups(def, issues) {
2450
+ const workflowGroups = checkGroupDeclarations({
2451
+ groups: def.groups,
2452
+ path: [ "groups" ],
2453
+ what: "group name",
2454
+ issues: issues
2455
+ });
2456
+ checkFieldMemberships({
2457
+ entries: def.fields,
2458
+ reachable: [ workflowGroups ],
2459
+ path: [ "fields" ],
2460
+ label: "workflow fields",
2461
+ issues: issues
2462
+ });
2463
+ for (const [i, stage] of def.stages.entries()) checkStageGroups({
2464
+ stage: stage,
2465
+ i: i,
2466
+ workflowGroups: workflowGroups,
2467
+ issues: issues
2468
+ });
2469
+ }
2470
+
2471
+ function checkGroupDeclarations({groups: groups, path: path, what: what, issues: issues}) {
2472
+ return checkDuplicates({
2473
+ names: (groups ?? []).map((group, n) => ({
2474
+ name: group.name,
2475
+ path: [ ...path, n, "name" ]
2476
+ })),
2477
+ what: what,
2478
+ issues: issues
2479
+ });
2480
+ }
2481
+
2482
+ function checkStageGroups({stage: stage, i: i, workflowGroups: workflowGroups, issues: issues}) {
2483
+ const path = [ "stages", i ], enclosing = [ checkGroupDeclarations({
2484
+ groups: stage.groups,
2485
+ path: [ ...path, "groups" ],
2486
+ what: `group name in stage "${stage.name}"`,
2487
+ issues: issues
2488
+ }), workflowGroups ];
2489
+ checkFieldMemberships({
2490
+ entries: stage.fields,
2491
+ reachable: enclosing,
2492
+ path: [ ...path, "fields" ],
2493
+ label: `stage "${stage.name}" fields`,
2494
+ issues: issues
2495
+ });
2496
+ for (const [j, activity] of (stage.activities ?? []).entries()) checkActivityGroups({
2497
+ activity: activity,
2498
+ path: [ ...path, "activities", j ],
2499
+ enclosing: enclosing,
2500
+ issues: issues
2501
+ });
2502
+ }
2503
+
2504
+ function checkActivityGroups({activity: activity, path: path, enclosing: enclosing, issues: issues}) {
2505
+ checkGroupMembership({
2506
+ group: activity.group,
2507
+ reachable: enclosing,
2508
+ path: [ ...path, "group" ],
2509
+ label: `activity "${activity.name}"`,
2510
+ issues: issues
2511
+ });
2512
+ const inner = [ checkGroupDeclarations({
2513
+ groups: activity.groups,
2514
+ path: [ ...path, "groups" ],
2515
+ what: `group name in activity "${activity.name}"`,
2516
+ issues: issues
2517
+ }), ...enclosing ];
2518
+ checkFieldMemberships({
2519
+ entries: activity.fields,
2520
+ reachable: inner,
2521
+ path: [ ...path, "fields" ],
2522
+ label: `activity "${activity.name}" fields`,
2523
+ issues: issues
2524
+ });
2525
+ for (const [a, action] of (activity.actions ?? []).entries()) checkGroupMembership({
2526
+ group: action.group,
2527
+ reachable: inner,
2528
+ path: [ ...path, "actions", a, "group" ],
2529
+ label: `action "${action.name}"`,
2530
+ issues: issues
2531
+ });
2532
+ }
2533
+
2534
+ function checkFieldMemberships({entries: entries, reachable: reachable, path: path, label: label, issues: issues}) {
2535
+ for (const [n, entry] of (entries ?? []).entries()) checkGroupMembership({
2536
+ group: entry.group,
2537
+ reachable: reachable,
2538
+ path: [ ...path, n, "group" ],
2539
+ label: `${label} entry "${entry.name}"`,
2540
+ issues: issues
2541
+ });
2542
+ }
2543
+
2544
+ function checkGroupMembership({group: group, reachable: reachable, path: path, label: label, issues: issues}) {
2545
+ for (const name of groupMembershipNames(group)) {
2546
+ if (reachable.some(names => names.has(name))) continue;
2547
+ const declared = new Set(reachable.flatMap(names => [ ...names ]));
2548
+ issues.push({
2549
+ path: path,
2550
+ message: `${label} names group "${name}", which is not declared on its enclosing chain. Declared: ${knownList(declared)}`
2551
+ });
2552
+ }
2553
+ }
2554
+
2555
+ function checkConditionFieldReads(def, issues) {
2556
+ const everywhere = allDeclaredFieldNames(def);
2557
+ for (const site of conditionSites(def)) for (const name of conditionFieldReadNames(site.groq)) fieldVisibleAtSite({
2558
+ site: site,
2559
+ name: name,
2560
+ everywhere: everywhere
2561
+ }) || issues.push({
2562
+ path: site.path,
2563
+ message: undeclaredFieldReadMessage(site, name)
2564
+ });
2565
+ for (const [name, groq2] of Object.entries(def.predicates ?? {})) for (const read of conditionFieldReadNames(groq2)) everywhere.has(read) || issues.push({
2566
+ path: [ "predicates", name ],
2567
+ message: noFieldAnywhereMessage(`predicate "${name}"`, read)
2568
+ });
2569
+ }
2570
+
2571
+ function allDeclaredFieldNames(def) {
2572
+ const names = /* @__PURE__ */ new Set;
2573
+ for (const {entries: entries} of fieldScopes(def)) for (const entry of entries ?? []) names.add(entry.name);
2574
+ return names;
2575
+ }
2576
+
2577
+ function fieldVisibleAtSite({site: site, name: name, everywhere: everywhere}) {
2578
+ return site.fields === "context-dependent" ? everywhere.has(name) : site.fields.some(layer => layer.names.has(name));
2579
+ }
2580
+
2581
+ function noFieldAnywhereMessage(label, name) {
2582
+ return `${label} reads $fields.${name}, but no field entry named "${name}" is declared anywhere in the definition — the read evaluates to GROQ null, so the condition silently fails closed`;
2583
+ }
2584
+
2585
+ function undeclaredFieldReadMessage(site, name) {
2586
+ if (site.fields === "context-dependent") return noFieldAnywhereMessage(site.label, name);
2587
+ const visible = site.fields.flatMap(layer => [ ...layer.names ].map(n => `${layer.scope}:${n}`));
2588
+ return `${site.label} reads $fields.${name}, but no field entry named "${name}" is lexically visible here — the read evaluates to GROQ null, so the condition silently fails closed. Visible: ${visible.join(", ") || "(none)"}`;
2589
+ }
2590
+
2591
+ function checkFieldReadSeeds(def, issues) {
2592
+ const workflowEntries = def.fields ?? [];
2593
+ for (const {entries: entries, scope: scope, path: path, label: label} of fieldScopes(def)) for (const [n, entry] of (entries ?? []).entries()) {
2594
+ const read = entry.initialValue;
2595
+ read?.type === "fieldRead" && checkSeedRead({
2596
+ read: read,
2597
+ where: `${label} entry "${entry.name}"`,
2598
+ entryName: entry.name,
2599
+ scope: scope,
2600
+ siblings: entries ?? [],
2601
+ index: n,
2602
+ workflowEntries: workflowEntries,
2603
+ path: [ ...path, n, "initialValue" ],
2604
+ issues: issues
2605
+ });
2606
+ }
2607
+ }
2608
+
2609
+ function checkSeedRead(args) {
2610
+ const {read: read, where: where, path: path, issues: issues} = args, misspelled = seedScopeSpellingIssue(args);
2611
+ if (misspelled !== void 0) {
2612
+ issues.push({
2613
+ path: path,
2614
+ message: `${where} ${misspelled}`
2615
+ });
2616
+ return;
2617
+ }
2618
+ const target = read.scope === "workflow" ? seedWorkflowTarget(args) : seedEarlierSiblingTarget(args);
2619
+ target !== void 0 && pushFieldReadPathIssue({
2620
+ where: where,
2621
+ read: read,
2622
+ target: target,
2623
+ path: path,
2624
+ issues: issues
2625
+ });
2626
+ }
2627
+
2628
+ function seedScopeSpellingIssue({read: read, scope: scope}) {
2629
+ if (read.scope === "workflow" && scope === "workflow") return 'seeds from a fieldRead with scope "workflow", but workflow-scope seeds resolve against earlier same-scope siblings only (no workflow layer exists yet at start/spawn) — omit `scope`';
2630
+ if (read.scope === "stage") {
2631
+ if (scope === "workflow") return 'seeds from a fieldRead with scope "stage", but a workflow-scope entry has no stage in scope — omit `scope` to read an earlier workflow-scope sibling';
2632
+ if (scope === "activity") return 'seeds from a fieldRead with scope "stage", but a seed resolves non-workflow reads against the scope being seeded, so an activity-scope entry cannot read stage fields — omit `scope` to read an earlier activity-scope sibling, or use scope "workflow"';
2633
+ }
2634
+ }
2635
+
2636
+ function seedWorkflowTarget({read: read, where: where, workflowEntries: workflowEntries, path: path, issues: issues}) {
2637
+ const target = workflowEntries.find(entry => entry.name === read.field);
2638
+ if (target !== void 0) return target;
2639
+ issues.push({
2640
+ path: path,
2641
+ message: `${where} seeds from workflow-scope field "${read.field}", which is not declared — the read would resolve to the default value. Known workflow fields: ${knownList(workflowEntries.map(entry => entry.name))}`
2642
+ });
2643
+ }
2644
+
2645
+ function seedEarlierSiblingTarget(args) {
2646
+ const {read: read, where: where, entryName: entryName, scope: scope, siblings: siblings, index: index, workflowEntries: workflowEntries, path: path, issues: issues} = args, targetIndex = siblings.findIndex(entry => entry.name === read.field);
2647
+ if (targetIndex !== -1 && targetIndex < index) return siblings[targetIndex];
2648
+ if (targetIndex === index) {
2649
+ issues.push({
2650
+ path: path,
2651
+ message: `${where} seeds from itself — a fieldRead cannot read the entry it seeds`
2652
+ });
2653
+ return;
2654
+ }
2655
+ if (targetIndex > index) {
2656
+ issues.push({
2657
+ path: path,
2658
+ message: `${where} seeds from "${read.field}", which is declared later in the same scope — entries resolve in declaration order, so the read would see its default. Move "${read.field}" before "${entryName}"`
2659
+ });
2660
+ return;
2661
+ }
2662
+ const workflowHint = scope !== "workflow" && workflowEntries.some(entry => entry.name === read.field) ? ' — a workflow-scope entry of that name exists; add `scope: "workflow"` to read it (an omitted scope reads same-scope siblings only)' : "";
2663
+ issues.push({
2664
+ path: path,
2665
+ message: `${where} seeds from "${read.field}", which is not an earlier ${scope}-scope sibling — the read would resolve to the default value${workflowHint}. Earlier siblings: ${knownList(siblings.slice(0, index).map(entry => entry.name))}`
2666
+ });
2667
+ }
2668
+
2669
+ function opSites(def) {
2670
+ const sites = [];
2671
+ for (const [i, stage] of def.stages.entries()) for (const [j, activity] of (stage.activities ?? []).entries()) for (const [a, action] of (activity.actions ?? []).entries()) sites.push({
2672
+ ops: action.ops,
2673
+ path: [ "stages", i, "activities", j, "actions", a, "ops" ],
2674
+ label: `action "${action.name}"`,
2675
+ stage: stage,
2676
+ activity: activity
2677
+ });
2678
+ return sites;
2679
+ }
2680
+
2681
+ function checkFieldReadOpValues(def, issues) {
2682
+ const workflowEntries = def.fields ?? [];
2683
+ for (const site of opSites(def)) checkOpsFieldReads({
2684
+ workflowEntries: workflowEntries,
2685
+ stageEntries: site.stage.fields ?? [],
2686
+ issues: issues,
2687
+ ops: site.ops,
2688
+ path: site.path,
2689
+ label: site.label,
2690
+ activityNames: entryNames(site.activity.fields)
2691
+ });
2692
+ }
2693
+
2694
+ function checkOpsFieldReads({ops: ops, path: path, label: label, ...ctx}) {
2695
+ for (const [o, op] of (ops ?? []).entries()) if ("value" in op) for (const {read: read, path: readPath} of fieldReadsIn(op.value, [ ...path, o, "value" ])) checkOpFieldRead({
2696
+ ...ctx,
2697
+ read: read,
2698
+ path: readPath,
2699
+ where: `${label} ${op.type} value`
2700
+ });
2701
+ }
2702
+
2703
+ function fieldReadsIn(value, path) {
2704
+ return value.type === "fieldRead" ? [ {
2705
+ read: value,
2706
+ path: path
2707
+ } ] : value.type !== "object" ? [] : Object.entries(value.fields).flatMap(([key, sub]) => fieldReadsIn(sub, [ ...path, "fields", key ]));
2708
+ }
2709
+
2710
+ function checkOpFieldRead({read: read, where: where, path: path, workflowEntries: workflowEntries, stageEntries: stageEntries, activityNames: activityNames, issues: issues}) {
2711
+ const hosts = [ {
2712
+ scope: "stage",
2713
+ entries: stageEntries
2714
+ }, {
2715
+ scope: "workflow",
2716
+ entries: workflowEntries
2717
+ } ].filter(host => read.scope === void 0 || host.scope === read.scope), target = hosts.flatMap(host => host.entries).find(entry => entry.name === read.field);
2718
+ if (target === void 0) {
2719
+ issues.push({
2720
+ path: path,
2721
+ message: opFieldReadMissMessage({
2722
+ read: read,
2723
+ where: where,
2724
+ hosts: hosts,
2725
+ activityNames: activityNames
2726
+ })
2727
+ });
2728
+ return;
2729
+ }
2730
+ pushFieldReadPathIssue({
2731
+ where: where,
2732
+ read: read,
2733
+ target: target,
2734
+ path: path,
2735
+ issues: issues
2736
+ });
2737
+ }
2738
+
2739
+ function opFieldReadMissMessage({read: read, where: where, hosts: hosts, activityNames: activityNames}) {
2740
+ const searched = read.scope === void 0 ? "stage or workflow scope" : `${read.scope} scope`, activityHint = activityNames?.has(read.field) ? ` "${read.field}" is an activity-scope field, and op-time field reads resolve against stage and workflow scopes only — declare it at stage scope to read it from an op.` : "", known = hosts.flatMap(host => host.entries.map(entry => `${host.scope}:${entry.name}`));
2741
+ return `${where} reads field "${read.field}", which is not declared at ${searched} — the read resolves to undefined at op time, so the write silently lands empty.${activityHint} Known: ${known.join(", ") || "(none)"}`;
2742
+ }
2743
+
2744
+ function checkUpdateWhereOps(def, issues) {
2745
+ const workflow = def.fields ?? [];
2746
+ for (const site of opSites(def)) for (const [o, op] of (site.ops ?? []).entries()) {
2747
+ if (op.type !== "field.updateWhere") continue;
2748
+ const scopes = {
2749
+ workflow: workflow,
2750
+ stage: site.stage.fields ?? [],
2751
+ activity: site.activity.fields ?? []
2752
+ };
2753
+ checkUpdateWhereTargetKind({
2754
+ op: op,
2755
+ path: [ ...site.path, o ],
2756
+ label: site.label,
2757
+ scopes: scopes,
2758
+ issues: issues
2759
+ }), checkUpdateWhereMergeKeys({
2760
+ op: op,
2761
+ path: [ ...site.path, o ],
2762
+ label: site.label,
2763
+ issues: issues
2764
+ });
2765
+ }
2766
+ }
2767
+
2768
+ function checkUpdateWhereTargetKind({op: op, path: path, label: label, scopes: scopes, issues: issues}) {
2769
+ const target = scopes[op.target.scope]?.find(entry => entry.name === op.target.field);
2770
+ target === void 0 || target.type === "array" || issues.push({
2771
+ path: [ ...path, "target" ],
2772
+ message: `${label} field.updateWhere targets ${op.target.scope}-scope "${op.target.field}" (${target.type}) — updateWhere merges declared row sub-fields, so its target must be an \`array\` entry${rowOpsHint(target.type)}`
2773
+ });
2774
+ }
2775
+
2776
+ function rowOpsHint(type) {
2777
+ return type === "doc.refs" || type === "assignees" ? `; to add or drop ${type} rows use field.append / field.removeWhere` : "";
2778
+ }
2779
+
2780
+ function checkUpdateWhereMergeKeys({op: op, path: path, label: label, issues: issues}) {
2781
+ if (op.value.type === "object") for (const key of Object.keys(op.value.fields)) key !== "_key" && key !== "_type" || issues.push({
2782
+ path: [ ...path, "value", "fields", key ],
2783
+ message: `${label} field.updateWhere merge writes the reserved row key "${key}" — row identity and bookkeeping are engine-stamped, and a merge would restamp every matched row with the same literal`
2784
+ });
2785
+ }
2786
+
2787
+ function checkGuardFieldReads(def, issues) {
2788
+ const reads = {
2789
+ workflowEntries: def.fields ?? [],
2790
+ effectNames: new Set(effectNameSites(def).map(site => site.name)),
2791
+ issues: issues
2792
+ };
2793
+ for (const [i, stage] of def.stages.entries()) for (const [g, guard] of (stage.guards ?? []).entries()) {
2794
+ const where = `stage "${stage.name}" guard "${guard.name}"`, guardPath = [ "stages", i, "guards", g ];
2795
+ for (const [k, expr] of (guard.match.idRefs ?? []).entries()) checkGuardRead({
2796
+ ...reads,
2797
+ expr: expr,
2798
+ where: `${where} match.idRefs`,
2799
+ path: [ ...guardPath, "match", "idRefs", k ]
2800
+ });
2801
+ for (const [key, expr] of Object.entries(guard.metadata ?? {})) checkGuardRead({
2802
+ ...reads,
2803
+ expr: expr,
2804
+ where: `${where} metadata "${key}"`,
2805
+ path: [ ...guardPath, "metadata", key ]
2806
+ });
2807
+ }
2808
+ }
2809
+
2810
+ function checkGuardRead(args) {
2811
+ const fieldRead = FIELD_READ.exec(args.expr);
2812
+ if (fieldRead !== null) {
2813
+ checkGuardFieldRead({
2814
+ ...args,
2815
+ name: fieldRead[1],
2816
+ valuePath: fieldRead[2]
2817
+ });
2818
+ return;
2819
+ }
2820
+ const effectName = EFFECTS_READ.exec(args.expr)?.[1];
2821
+ effectName === void 0 || args.effectNames.has(effectName) || args.issues.push({
2822
+ path: args.path,
2823
+ message: `${args.where} reads "${args.expr}", but no effect named "${effectName}" is declared — the read resolves to undefined and the guard deploys with a hole. Known effects: ${knownList(args.effectNames)}`
2824
+ });
2825
+ }
2826
+
2827
+ function checkGuardFieldRead({name: name, valuePath: valuePath, where: where, path: path, workflowEntries: workflowEntries, issues: issues}) {
2828
+ const target = workflowEntries.find(entry => entry.name === name);
2829
+ if (target === void 0) {
2830
+ issues.push({
2831
+ path: path,
2832
+ message: `${where} reads "$fields.${name}", but "${name}" is not a workflow-scope field entry — guard reads resolve against workflow-scope fields only. Known workflow fields: ${knownList(workflowEntries.map(entry => entry.name))}`
2833
+ });
2834
+ return;
2835
+ }
2836
+ pushFieldReadPathIssue({
2837
+ where: where,
2838
+ read: {
2839
+ field: name,
2840
+ path: valuePath
2841
+ },
2842
+ target: target,
2843
+ path: path,
2844
+ issues: issues
2845
+ });
2846
+ }
2847
+
2848
+ function pushFieldReadPathIssue({where: where, read: read, target: target, path: path, issues: issues}) {
2849
+ if (read.path === void 0) return;
2850
+ const pathIssue = fieldValuePathIssue({
2851
+ target: target,
2852
+ path: read.path
2853
+ });
2854
+ pathIssue !== void 0 && issues.push({
2855
+ path: path,
2856
+ message: `${where} reads "${read.field}" with path "${read.path}", which does not fit the declared shape of "${target.name ?? read.field}" (${target.type}) — ${pathIssue}`
2857
+ });
2858
+ }
2859
+
2860
+ const SCALAR = {
2861
+ kind: "scalar"
2862
+ }, DOC_CONTENT = {
2863
+ kind: "opaque"
2864
+ }, GDR_VALUE = {
2865
+ kind: "record",
2866
+ keys: {
2867
+ id: SCALAR,
2868
+ type: SCALAR
2869
+ }
2870
+ }, RELEASE_VALUE = {
2871
+ kind: "record",
2872
+ keys: {
2873
+ id: SCALAR,
2874
+ type: SCALAR,
2875
+ releaseName: SCALAR
2876
+ }
2877
+ }, ACTOR_VALUE = {
2878
+ kind: "record",
2879
+ keys: {
2880
+ kind: SCALAR,
2881
+ id: SCALAR,
2882
+ roles: {
2883
+ kind: "list",
2884
+ item: SCALAR
2885
+ },
2886
+ onBehalfOf: SCALAR
2887
+ }
2888
+ }, ASSIGNEE_VALUE = {
2889
+ kind: "record",
2890
+ keys: {
2891
+ type: SCALAR,
2892
+ id: SCALAR,
2893
+ role: SCALAR
2894
+ }
2895
+ }, VALUE_NODES = {
2896
+ "doc.ref": () => DOC_CONTENT,
2897
+ "doc.refs": () => ({
2898
+ kind: "list",
2899
+ item: GDR_VALUE
2900
+ }),
2901
+ "release.ref": () => RELEASE_VALUE,
2902
+ string: () => SCALAR,
2903
+ text: () => SCALAR,
2904
+ number: () => SCALAR,
2905
+ boolean: () => SCALAR,
2906
+ date: () => SCALAR,
2907
+ datetime: () => SCALAR,
2908
+ url: () => SCALAR,
2909
+ actor: () => ACTOR_VALUE,
2910
+ assignee: () => ASSIGNEE_VALUE,
2911
+ assignees: () => ({
2912
+ kind: "list",
2913
+ item: ASSIGNEE_VALUE
2914
+ }),
2915
+ object: shape => ({
2916
+ kind: "object",
2917
+ fields: shape.fields ?? []
2918
+ }),
2919
+ array: shape => ({
2920
+ kind: "rows",
2921
+ of: shape.of ?? []
2922
+ })
2923
+ };
2924
+
2925
+ function valueNodeFor(shape) {
2926
+ return Object.hasOwn(VALUE_NODES, shape.type) ? VALUE_NODES[shape.type](shape) : void 0;
2927
+ }
2928
+
2929
+ const INDEX_SEGMENT = /^\d+$/;
2930
+
2931
+ function stepIntoValueNode(node, segment) {
2932
+ switch (node.kind) {
2933
+ case "scalar":
2934
+ return "the value is a scalar with no sub-paths";
2935
+
2936
+ case "object":
2937
+ {
2938
+ const sub = node.fields.find(f => f.name === segment), next = sub === void 0 ? void 0 : valueNodeFor(sub);
2939
+ return next !== void 0 ? next : `an object value has sub-fields ${knownList(node.fields.map(f => f.name))} — "${segment}" is not one`;
2940
+ }
2941
+
2942
+ case "rows":
2943
+ return INDEX_SEGMENT.test(segment) ? {
2944
+ kind: "object",
2945
+ fields: node.of
2946
+ } : `an array value is addressed by numeric row index first (e.g. "0.<subField>") — "${segment}" is not an index`;
2947
+
2948
+ case "list":
2949
+ return INDEX_SEGMENT.test(segment) ? node.item : `an array value is addressed by numeric item index (e.g. "0") — "${segment}" is not an index`;
2950
+
2951
+ case "record":
2952
+ return Object.hasOwn(node.keys, segment) ? node.keys[segment] : `the value carries ${knownList(Object.keys(node.keys))} — "${segment}" is not one of them`;
2953
+
2954
+ case "opaque":
2955
+ return node;
2956
+ }
2957
+ }
2958
+
2959
+ function fieldValuePathIssue({target: target, path: path}) {
2960
+ let node = valueNodeFor(target);
2961
+ if (node === void 0) return `"${String(target.type)}" is not a known field kind`;
2962
+ for (const segment of path.split(".")) {
2963
+ const next = stepIntoValueNode(node, segment);
2964
+ if (typeof next == "string") return `at segment "${segment}": ${next}`;
2965
+ node = next;
2966
+ }
2967
+ }
2968
+
2969
+ function checkWorkflowInvariants(def) {
2970
+ const issues = [], stageNames = checkStages(def, issues);
2971
+ return checkInitialStage({
2972
+ def: def,
2973
+ stageNames: stageNames,
2974
+ issues: issues
2975
+ }), checkTransitionTargets({
2976
+ def: def,
2977
+ stageNames: stageNames,
2978
+ issues: issues
2979
+ }), checkStageReachability({
2980
+ def: def,
2981
+ stageNames: stageNames,
2982
+ issues: issues
2983
+ }), checkEffectNames(def, issues), checkGuardNames(def, issues), checkFieldEntryNames(def, issues),
2984
+ checkRequiredField(def, issues), checkStart(def, issues), checkPredicates(def, issues),
2985
+ checkUnboundCallerVars(def, issues), checkConditionFieldReads(def, issues), checkFieldReadSeeds(def, issues),
2986
+ checkFieldReadOpValues(def, issues), checkUpdateWhereOps(def, issues), checkGuardFieldReads(def, issues),
2987
+ checkAssigneesEntries(def, issues), checkActivityTerminalPaths(def, issues), checkTerminalStageActivities(def, issues),
2988
+ checkTriggeredActionParams(def, issues), checkStoredRolesPlacement(def, issues),
2989
+ checkGroups(def, issues), issues;
2990
+ }
2991
+
2992
+ exports.ACTIVITY_KINDS = ACTIVITY_KINDS;
2993
+
2994
+ exports.ACTOR_KINDS = ACTOR_KINDS;
2995
+
2996
+ exports.AuthoringActionSchema = AuthoringActionSchema;
2997
+
2998
+ exports.AuthoringActivitySchema = AuthoringActivitySchema;
2999
+
3000
+ exports.AuthoringFieldEntrySchema = AuthoringFieldEntrySchema;
3001
+
3002
+ exports.AuthoringGuardSchema = AuthoringGuardSchema;
3003
+
3004
+ exports.AuthoringOpSchema = AuthoringOpSchema;
3005
+
3006
+ exports.AuthoringStageSchema = AuthoringStageSchema;
3007
+
3008
+ exports.AuthoringTransitionSchema = AuthoringTransitionSchema;
3009
+
3010
+ exports.AuthoringWorkflowSchema = AuthoringWorkflowSchema;
3011
+
3012
+ exports.CALLER_BOUND_VARS = CALLER_BOUND_VARS;
3013
+
3014
+ exports.CONDITION_VARS = CONDITION_VARS;
3015
+
3016
+ exports.ContractViolationError = ContractViolationError;
3017
+
3018
+ exports.DEFAULT_TRANSITION_WHEN = DEFAULT_TRANSITION_WHEN;
3019
+
3020
+ exports.DOCUMENT_VALUE_PERMISSIONS = DOCUMENT_VALUE_PERMISSIONS;
3021
+
3022
+ exports.DRIVER_KINDS = DRIVER_KINDS;
3023
+
3024
+ exports.DefinitionInUseError = DefinitionInUseError;
3025
+
3026
+ exports.DefinitionNotFoundError = DefinitionNotFoundError;
3027
+
3028
+ exports.EFFECTS_READ = EFFECTS_READ;
3029
+
3030
+ exports.EXECUTOR_CLASSIFICATIONS = EXECUTOR_CLASSIFICATIONS;
3031
+
3032
+ exports.EffectNotFoundError = EffectNotFoundError;
3033
+
3034
+ exports.EffectSchema = EffectSchema;
3035
+
3036
+ exports.FIELD_READ = FIELD_READ;
3037
+
3038
+ exports.FILTER_SCOPE_VARS = FILTER_SCOPE_VARS;
3039
+
3040
+ exports.FieldValueShapeError = FieldValueShapeError;
3041
+
3042
+ exports.GROUP_KINDS = GROUP_KINDS;
3043
+
3044
+ exports.GUARD_PREDICATE_VARS = GUARD_PREDICATE_VARS;
3045
+
3046
+ exports.GroupSchema = GroupSchema;
3047
+
3048
+ exports.InstanceNotFoundError = InstanceNotFoundError;
3049
+
3050
+ exports.RESERVED_CONDITION_VARS = RESERVED_CONDITION_VARS;
3051
+
3052
+ exports.RESOURCE_ALIAS_NAME_SOURCE = RESOURCE_ALIAS_NAME_SOURCE;
3053
+
3054
+ exports.START_FILTER_VARS = START_FILTER_VARS;
3055
+
3056
+ exports.StoredFieldOpSchema = StoredFieldOpSchema;
3057
+
3058
+ exports.WORKFLOW_DEFINITION_TYPE = WORKFLOW_DEFINITION_TYPE;
3059
+
3060
+ exports.WorkflowConfigSchema = WorkflowConfigSchema;
3061
+
3062
+ exports.WorkflowError = WorkflowError;
3063
+
3064
+ exports.actorFulfillsRole = actorFulfillsRole;
3065
+
3066
+ exports.andConditions = andConditions;
3067
+
3068
+ exports.checkFieldValue = checkFieldValue;
3069
+
3070
+ exports.checkWorkflowInvariants = checkWorkflowInvariants;
3071
+
3072
+ exports.clientConfigFromResource = clientConfigFromResource;
3073
+
3074
+ exports.conditionEffectReads = conditionEffectReads;
3075
+
3076
+ exports.conditionFieldReadNames = conditionFieldReadNames;
3077
+
3078
+ exports.conditionParameterNames = conditionParameterNames;
3079
+
3080
+ exports.conditionSyntaxIssues = conditionSyntaxIssues;
3081
+
3082
+ exports.datasetResourceParts = datasetResourceParts;
3083
+
3084
+ exports.definitionDocId = definitionDocId;
3085
+
3086
+ exports.deriveActivityKind = deriveActivityKind;
3087
+
3088
+ exports.deriveExecutorClassification = deriveExecutorClassification;
3089
+
3090
+ exports.desugarWorkflow = desugarWorkflow;
3091
+
3092
+ exports.driverKind = driverKind;
3093
+
3094
+ exports.errorMessage = errorMessage;
3095
+
3096
+ exports.evaluateCondition = evaluateCondition;
3097
+
3098
+ exports.evaluateConditionOutcome = evaluateConditionOutcome;
3099
+
3100
+ exports.evaluatePredicates = evaluatePredicates;
3101
+
3102
+ exports.extractDocumentId = extractDocumentId;
3103
+
3104
+ exports.formatIssuePath = formatIssuePath;
3105
+
3106
+ exports.formatIssues = formatIssues;
3107
+
3108
+ exports.formatValidationError = formatValidationError;
3109
+
3110
+ exports.gdrFromResource = gdrFromResource;
3111
+
3112
+ exports.gdrRef = gdrRef;
3113
+
3114
+ exports.gdrResourcePrefix = gdrResourcePrefix;
3115
+
3116
+ exports.gdrUri = gdrUri;
3117
+
3118
+ exports.groq = groq;
3119
+
3120
+ exports.groupMembershipNames = groupMembershipNames;
3121
+
3122
+ exports.isBareSeedId = isBareSeedId;
3123
+
3124
+ exports.isCascadeFired = isCascadeFired;
3125
+
3126
+ exports.isGdr = isGdr;
3127
+
3128
+ exports.isGdrUri = isGdrUri;
3129
+
3130
+ exports.isGuardReadExpr = isGuardReadExpr;
3131
+
3132
+ exports.isInputSourced = isInputSourced;
3133
+
3134
+ exports.isNotesEntry = isNotesEntry;
3135
+
3136
+ exports.isStartableDefinition = isStartableDefinition;
3137
+
3138
+ exports.isSubjectEntry = isSubjectEntry;
3139
+
3140
+ exports.isTerminalActivityStatus = isTerminalActivityStatus;
3141
+
3142
+ exports.isTodoListEntry = isTodoListEntry;
3143
+
3144
+ exports.isTodoListItem = isTodoListItem;
3145
+
3146
+ exports.isUnevaluable = isUnevaluable;
3147
+
3148
+ exports.labelFor = labelFor;
3149
+
3150
+ exports.parseGdr = parseGdr;
3151
+
3152
+ exports.parseOrThrow = parseOrThrow;
3153
+
3154
+ exports.parseResourceGdr = parseResourceGdr;
3155
+
3156
+ exports.parseStoredDefinition = parseStoredDefinition;
3157
+
3158
+ exports.readsRootDocument = readsRootDocument;
3159
+
3160
+ exports.refCanvas = refCanvas;
3161
+
3162
+ exports.refDashboard = refDashboard;
3163
+
3164
+ exports.refDataset = refDataset;
3165
+
3166
+ exports.refMediaLibrary = refMediaLibrary;
3167
+
3168
+ exports.refTypeIssues = refTypeIssues;
3169
+
3170
+ exports.rejectedRefTypes = rejectedRefTypes;
3171
+
3172
+ exports.releaseDocId = releaseDocId;
3173
+
3174
+ exports.releaseRef = releaseRef;
3175
+
3176
+ exports.resourceAliasesToMap = resourceAliasesToMap;
3177
+
3178
+ exports.resourceFromGdrUri = resourceFromGdrUri;
3179
+
3180
+ exports.resourceFromParsed = resourceFromParsed;
3181
+
3182
+ exports.resourceGdr = resourceGdr;
3183
+
3184
+ exports.rethrowWithContext = rethrowWithContext;
3185
+
3186
+ exports.runGroq = runGroq;
3187
+
3188
+ exports.sameResource = sameResource;
3189
+
3190
+ exports.selfGdr = selfGdr;
3191
+
3192
+ exports.startKindOf = startKindOf;
3193
+
3194
+ exports.tagScopeFilter = tagScopeFilter;
3195
+
3196
+ exports.toBareId = toBareId;
3197
+
3198
+ exports.toPhysicalGdr = toPhysicalGdr;
3199
+
3200
+ exports.tryParseGdr = tryParseGdr;
3201
+
3202
+ exports.validateFieldAppendItem = validateFieldAppendItem;
3203
+
3204
+ exports.validateFieldValue = validateFieldValue;
3205
+
3206
+ exports.validateResourceAliasName = validateResourceAliasName;
3207
+
3208
+ exports.validateTag = validateTag;