@sanity/workflow-engine 0.12.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16,11 +16,325 @@ function _interopNamespaceCompat(e) {
16
16
  }), n.default = e, Object.freeze(n);
17
17
  }
18
18
  var v__namespace = /* @__PURE__ */ _interopNamespaceCompat(v);
19
+ const KNOWN_SCHEMES = /* @__PURE__ */ new Set(["dataset", "canvas", "media-library", "dashboard"]);
20
+ function parseGdr(uri) {
21
+ const colon = uri.indexOf(":");
22
+ if (colon < 0)
23
+ throw new Error(
24
+ `Invalid GDR "${uri}": must be a URI of form "<scheme>:<...id-parts>". Known schemes: dataset, canvas, media-library, dashboard.`
25
+ );
26
+ const scheme = uri.slice(0, colon), rest = uri.slice(colon + 1);
27
+ if (!KNOWN_SCHEMES.has(scheme))
28
+ throw new Error(
29
+ `Invalid GDR "${uri}": unknown scheme "${scheme}". Known: dataset, canvas, media-library, dashboard.`
30
+ );
31
+ const parts = rest.split(":");
32
+ if (parts.some((part) => part.length === 0))
33
+ throw new Error(
34
+ `Invalid GDR "${uri}": id parts must be non-empty (no leading, trailing, or doubled ":").`
35
+ );
36
+ if (scheme === "dataset") {
37
+ if (parts.length !== 3)
38
+ throw new Error(
39
+ `Invalid GDR "${uri}": dataset scheme requires <projectId>:<dataset>:<documentId> (3 parts after scheme); got ${parts.length}.`
40
+ );
41
+ return {
42
+ scheme: "dataset",
43
+ projectId: parts[0],
44
+ dataset: parts[1],
45
+ documentId: parts[2]
46
+ };
47
+ }
48
+ if (parts.length !== 2)
49
+ throw new Error(
50
+ `Invalid GDR "${uri}": ${scheme} scheme requires <resourceId>:<documentId> (2 parts after scheme); got ${parts.length}.`
51
+ );
52
+ return {
53
+ scheme,
54
+ resourceId: parts[0],
55
+ documentId: parts[1]
56
+ };
57
+ }
58
+ function tryParseGdr(uri) {
59
+ try {
60
+ return parseGdr(uri);
61
+ } catch {
62
+ return;
63
+ }
64
+ }
65
+ function gdrUri(parts) {
66
+ return parts.scheme === "dataset" ? `dataset:${parts.projectId}:${parts.dataset}:${parts.documentId}` : `${parts.scheme}:${parts.resourceId}:${parts.documentId}`;
67
+ }
68
+ function extractDocumentId(gdrUriString) {
69
+ return parseGdr(gdrUriString).documentId;
70
+ }
71
+ const RESOURCE_ALIAS_NAME_SOURCE = "[a-z0-9][a-z0-9-]*", RESOURCE_ALIAS_NAME_RE = new RegExp(`^${RESOURCE_ALIAS_NAME_SOURCE}$`);
72
+ function validateResourceAliasName(name) {
73
+ if (!RESOURCE_ALIAS_NAME_RE.test(name))
74
+ throw new Error(
75
+ `Invalid resource alias name "${name}": expected lowercase letters, digits, and dashes (no leading dash).`
76
+ );
77
+ }
78
+ function toPhysicalGdr(id, home) {
79
+ return isGdrUri(id) ? id : gdrFromResource(home, id);
80
+ }
81
+ function refDataset({
82
+ projectId,
83
+ dataset,
84
+ documentId,
85
+ type
86
+ }) {
87
+ return { id: gdrUri({ scheme: "dataset", projectId, dataset, documentId }), type };
88
+ }
89
+ function refCanvas({
90
+ resourceId,
91
+ documentId,
92
+ type
93
+ }) {
94
+ return { id: gdrUri({ scheme: "canvas", resourceId, documentId }), type };
95
+ }
96
+ function refMediaLibrary({
97
+ resourceId,
98
+ documentId,
99
+ type
100
+ }) {
101
+ return { id: gdrUri({ scheme: "media-library", resourceId, documentId }), type };
102
+ }
103
+ function refDashboard({
104
+ resourceId,
105
+ documentId,
106
+ type
107
+ }) {
108
+ return { id: gdrUri({ scheme: "dashboard", resourceId, documentId }), type };
109
+ }
110
+ function datasetResourceParts(id) {
111
+ const dot = id.indexOf(".");
112
+ if (dot <= 0 || dot === id.length - 1)
113
+ throw new Error(`Invalid dataset resource id "${id}": expected "<projectId>.<dataset>".`);
114
+ return { projectId: id.slice(0, dot), dataset: id.slice(dot + 1) };
115
+ }
116
+ function gdrFromResource(res, documentId) {
117
+ if (res.type === "dataset") {
118
+ const { projectId, dataset } = datasetResourceParts(res.id);
119
+ return gdrUri({ scheme: "dataset", projectId, dataset, documentId });
120
+ }
121
+ return gdrUri({ scheme: res.type, resourceId: res.id, documentId });
122
+ }
123
+ function gdrResourcePrefix(res) {
124
+ const full = gdrFromResource(res, "x");
125
+ return full.slice(0, full.length - 1);
126
+ }
127
+ function resourceFromParsed(parsed) {
128
+ return parsed.scheme === "dataset" ? { type: "dataset", id: `${parsed.projectId}.${parsed.dataset}` } : { type: parsed.scheme, id: parsed.resourceId ?? "" };
129
+ }
130
+ function resourceFromGdrUri(uri) {
131
+ const parsed = tryParseGdr(uri);
132
+ return parsed === void 0 ? void 0 : resourceFromParsed(parsed);
133
+ }
134
+ function sameResource(a, b) {
135
+ return a.type === b.type && a.id === b.id;
136
+ }
137
+ function selfGdr(doc) {
138
+ return gdrFromResource(doc.workflowResource, doc._id);
139
+ }
140
+ function definitionDocId({
141
+ tag,
142
+ definition,
143
+ version
144
+ }) {
145
+ return `${tag}.${definition}.v${version}`;
146
+ }
147
+ function isGdrUri(value) {
148
+ return typeof value == "string" && tryParseGdr(value) !== void 0;
149
+ }
150
+ function toBareId(id) {
151
+ return isGdrUri(id) ? extractDocumentId(id) : id;
152
+ }
153
+ function gdrRef({
154
+ res,
155
+ documentId,
156
+ type
157
+ }) {
158
+ return { id: gdrFromResource(res, documentId), type };
159
+ }
160
+ function isGdr(value) {
161
+ return typeof value == "object" && value !== null && typeof value.id == "string" && typeof value.type == "string";
162
+ }
163
+ class WorkflowError extends Error {
164
+ kind;
165
+ // oxlint-disable-next-line max-params -- the platform `Error(message, options)` signature with the discriminant prepended
166
+ constructor(kind, message, options) {
167
+ super(message, options), this.kind = kind;
168
+ }
169
+ }
170
+ class ContractViolationError extends WorkflowError {
171
+ constructor(message) {
172
+ super("contract-violation", message), this.name = "ContractViolationError";
173
+ }
174
+ }
175
+ class InstanceNotFoundError extends WorkflowError {
176
+ instanceId;
177
+ constructor(args) {
178
+ super(
179
+ "instance-not-found",
180
+ `Workflow instance ${args.instanceId} not found${args.detail ? ` (${args.detail})` : ""}`
181
+ ), this.name = "InstanceNotFoundError", this.instanceId = args.instanceId;
182
+ }
183
+ }
184
+ class DefinitionNotFoundError extends WorkflowError {
185
+ definition;
186
+ version;
187
+ constructor(args) {
188
+ super(
189
+ "definition-not-found",
190
+ args.version !== void 0 ? `Workflow definition ${args.definition} v${args.version} not deployed` : `Workflow definition ${args.definition} has no deployed versions`
191
+ ), this.name = "DefinitionNotFoundError", this.definition = args.definition, args.version !== void 0 && (this.version = args.version);
192
+ }
193
+ }
194
+ class DefinitionInUseError extends WorkflowError {
195
+ definition;
196
+ blockedBy;
197
+ constructor(args) {
198
+ super("definition-in-use", definitionInUseMessage(args.definition, args.blockedBy)), this.name = "DefinitionInUseError", this.definition = args.definition, this.blockedBy = args.blockedBy;
199
+ }
200
+ }
201
+ function definitionInUseMessage(definition, blockedBy) {
202
+ if (blockedBy.reason === "non-terminal-instances") {
203
+ const head = blockedBy.instanceIds.slice(0, 3).join(", "), preview = blockedBy.instanceIds.length > 3 ? `${head}, \u2026` : head;
204
+ return `Cannot delete ${definition}: ${blockedBy.instanceIds.length} non-terminal instance(s) exist (${preview}). Pass cascade to abort them first \u2014 instances are aborted in place, never deleted.`;
205
+ }
206
+ const names = blockedBy.referrers.map((r) => `${r.definition} v${r.version}`).join(", ");
207
+ return `Cannot delete ${definition}: still spawn-referenced by deployed definition(s) ${names}. Delete or redeploy the referrer(s) first.`;
208
+ }
209
+ class EffectNotFoundError extends WorkflowError {
210
+ instanceId;
211
+ effectKey;
212
+ constructor(args) {
213
+ super(
214
+ "effect-not-found",
215
+ `Pending effect "${args.effectKey}" not found on instance ${args.instanceId}`
216
+ ), this.name = "EffectNotFoundError", this.instanceId = args.instanceId, this.effectKey = args.effectKey;
217
+ }
218
+ }
219
+ const TAG_RE = /^[a-z0-9][a-z0-9-]*$/;
220
+ function validateTag(tag) {
221
+ if (!TAG_RE.test(tag))
222
+ throw new ContractViolationError(
223
+ `tag: invalid tag "${tag}" \u2014 must match ${TAG_RE.source} (ASCII lowercase + digits + dashes, no leading dash, no dots)`
224
+ );
225
+ }
226
+ function tagScopeFilter() {
227
+ return "tag == $tag";
228
+ }
229
+ const NonEmptyString = v__namespace.pipe(v__namespace.string(), v__namespace.nonEmpty("must not be empty"));
230
+ function asPredicate(validate) {
231
+ return (value) => {
232
+ try {
233
+ return validate(value), !0;
234
+ } catch {
235
+ return !1;
236
+ }
237
+ };
238
+ }
239
+ const isValidTag = asPredicate(validateTag), isValidAliasName = asPredicate(validateResourceAliasName), isValidDatasetId = asPredicate(datasetResourceParts), WorkflowResourceSchema = v__namespace.variant("type", [
240
+ v__namespace.object({
241
+ type: v__namespace.literal("dataset"),
242
+ id: v__namespace.pipe(
243
+ NonEmptyString,
244
+ v__namespace.check(isValidDatasetId, 'invalid dataset resource id \u2014 expected "<projectId>.<dataset>"')
245
+ )
246
+ }),
247
+ v__namespace.object({ type: v__namespace.literal("canvas"), id: NonEmptyString }),
248
+ v__namespace.object({ type: v__namespace.literal("media-library"), id: NonEmptyString }),
249
+ v__namespace.object({ type: v__namespace.literal("dashboard"), id: NonEmptyString })
250
+ ]), ResourceBindingSchema = v__namespace.object({
251
+ name: v__namespace.pipe(
252
+ NonEmptyString,
253
+ v__namespace.check(
254
+ isValidAliasName,
255
+ "invalid resource handle name \u2014 lowercase letters, digits and dashes only, no leading dash"
256
+ )
257
+ ),
258
+ resource: WorkflowResourceSchema
259
+ }), DefinitionSchema = v__namespace.custom(
260
+ (input) => typeof input == "object" && input !== null && typeof input.name == "string",
261
+ "expected a workflow definition (an object with a string `name`)"
262
+ ), DeploymentSchema = v__namespace.object({
263
+ name: NonEmptyString,
264
+ tag: v__namespace.pipe(
265
+ v__namespace.string(),
266
+ v__namespace.nonEmpty(),
267
+ v__namespace.check(
268
+ isValidTag,
269
+ "invalid tag \u2014 lowercase letters, digits and dashes only, no leading dash, no dots"
270
+ )
271
+ ),
272
+ workflowResource: WorkflowResourceSchema,
273
+ // Optional — omit for a workflow whose definitions reference no `@handle:`.
274
+ // No valibot default: a default makes the inferred output `resourceAliases`
275
+ // required (poor authoring ergonomics) and diverges input from output,
276
+ // which the shared `parseOrThrow` can't model. resourceAliasesToMap treats
277
+ // an absent map as none.
278
+ //
279
+ // Handle names must be unique: resourceAliasesToMap collapses bindings into a
280
+ // map, so a duplicate would silently overwrite — and the dup is gone before
281
+ // the engine sees it. Caught here, at the only layer that still can.
282
+ resourceAliases: v__namespace.optional(
283
+ v__namespace.pipe(
284
+ v__namespace.array(ResourceBindingSchema),
285
+ v__namespace.check(
286
+ (bindings) => new Set(bindings.map((binding) => binding.name)).size === bindings.length,
287
+ "duplicate resource handle name \u2014 each binding name must be unique within a deployment"
288
+ )
289
+ )
290
+ ),
291
+ definitions: v__namespace.pipe(
292
+ v__namespace.array(DefinitionSchema),
293
+ v__namespace.minLength(1, "a deployment needs at least one definition")
294
+ )
295
+ }), WorkflowConfigSchema = v__namespace.object({
296
+ // Tags must be unique: a tag is the environment key the CLI selects a
297
+ // deployment by, so two deployments sharing one make selection ambiguous.
298
+ // Enforced here so the ambiguity is a config error, not a deploy-time
299
+ // surprise — and so consumers can treat a tag as naming one deployment.
300
+ deployments: v__namespace.pipe(
301
+ v__namespace.array(DeploymentSchema),
302
+ v__namespace.minLength(1, "a config needs at least one deployment"),
303
+ v__namespace.check(
304
+ (deployments) => new Set(deployments.map((deployment) => deployment.tag)).size === deployments.length,
305
+ "duplicate deployment tag \u2014 each deployment must use a unique tag"
306
+ )
307
+ )
308
+ });
309
+ function resourceAliasesToMap(resourceAliases) {
310
+ return Object.fromEntries(
311
+ (resourceAliases ?? []).map((binding) => [
312
+ binding.name,
313
+ binding.resource
314
+ ])
315
+ );
316
+ }
19
317
  function andConditions(parts) {
20
318
  const present = parts.filter((p) => p !== void 0);
21
319
  if (present.length !== 0)
22
320
  return present.length === 1 ? present[0] : present.map((p) => `(${p})`).join(" && ");
23
321
  }
322
+ const FIELD_READ = /^\$fields\.(\w+)(?:\.(.+))?$/, EFFECTS_READ = /^\$effects\['([^']+)'\](?:\.(.+))?$/;
323
+ function isGuardReadExpr(expr) {
324
+ return expr === "$self" || expr === "$now" || FIELD_READ.test(expr) || EFFECTS_READ.test(expr);
325
+ }
326
+ function printGuardRead(read) {
327
+ switch (read.type) {
328
+ case "self":
329
+ return "$self";
330
+ case "now":
331
+ return "$now";
332
+ case "fieldRead":
333
+ return read.path === void 0 ? `$fields.${read.field}` : `$fields.${read.field}.${read.path}`;
334
+ case "effectsRead":
335
+ return read.path === void 0 ? `$effects['${read.effect}']` : `$effects['${read.effect}'].${read.path}`;
336
+ }
337
+ }
24
338
  const UNIVERSAL_ROLE_ALIAS_KEY = "$all";
25
339
  function normalizeRoleAliases(aliases) {
26
340
  if (aliases === void 0 || !("*" in aliases)) return aliases;
@@ -37,11 +351,259 @@ function expandRequiredRoles(required, aliases) {
37
351
  for (const fulfiller of aliases[UNIVERSAL_ROLE_ALIAS_KEY] ?? []) out.add(fulfiller);
38
352
  return [...out];
39
353
  }
40
- function actorFulfillsRole(actorRoles, required, aliases) {
354
+ function actorFulfillsRole({
355
+ actorRoles,
356
+ required,
357
+ aliases
358
+ }) {
41
359
  if (actorRoles === void 0 || actorRoles.length === 0) return !1;
42
360
  const accepted = new Set(expandRequiredRoles([required], aliases));
43
361
  return actorRoles.some((role) => accepted.has(role));
44
362
  }
363
+ const CONDITION_VARS = [
364
+ {
365
+ name: "self",
366
+ binding: "always",
367
+ description: "GDR URI of the instance document itself \u2014 `*[_id == $self][0]` reads the instance in the snapshot."
368
+ },
369
+ {
370
+ name: "fields",
371
+ binding: "always",
372
+ 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."
373
+ },
374
+ {
375
+ name: "parent",
376
+ binding: "always",
377
+ description: "The parent instance's GDR URI, `null` on a root instance."
378
+ },
379
+ {
380
+ name: "ancestors",
381
+ binding: "always",
382
+ description: "GDR URIs of the ancestor chain, root first."
383
+ },
384
+ { name: "stage", binding: "always", description: "The current stage's name." },
385
+ {
386
+ name: "now",
387
+ binding: "always",
388
+ description: "The ISO clock reading shared by every condition in one evaluation pass, so they agree on the time."
389
+ },
390
+ {
391
+ name: "effects",
392
+ binding: "always",
393
+ description: "The `effectsContext` map: stable handler params seeded at start plus completed effects' outputs namespaced by effect name (`$effects['<effect>'].<output>`). Handler fuel \u2014 transition filters should read instance/lake state (or $effectStatus) instead."
394
+ },
395
+ {
396
+ name: "effectStatus",
397
+ binding: "always",
398
+ description: "Effect name \u2192 `'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."
399
+ },
400
+ {
401
+ name: "activities",
402
+ binding: "always",
403
+ description: "The current stage's activity rows, statuses included."
404
+ },
405
+ {
406
+ name: "allActivitiesDone",
407
+ binding: "always",
408
+ description: "Every current-stage activity is `done` or `skipped` \u2014 the default transition gate."
409
+ },
410
+ {
411
+ name: "anyActivityFailed",
412
+ binding: "always",
413
+ description: "Some current-stage activity is `failed`."
414
+ },
415
+ {
416
+ name: "actor",
417
+ binding: "caller",
418
+ description: "The acting identity (id + roles); `undefined` when no caller rides the evaluation."
419
+ },
420
+ {
421
+ name: "assigned",
422
+ binding: "caller",
423
+ 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."
424
+ },
425
+ {
426
+ name: "can",
427
+ binding: "caller",
428
+ description: "Advisory per-permission booleans computed from the caller's grants; `undefined` without grants. Bound only while an action fires, so it may appear in action filters only (deploy-enforced)."
429
+ },
430
+ {
431
+ name: "row",
432
+ binding: "spawn",
433
+ description: "One `subworkflows.forEach` result row, bound while its `with` map evaluates \u2014 and the row under test while a where-op `where` evaluates (per row)."
434
+ },
435
+ {
436
+ name: "params",
437
+ binding: "caller",
438
+ description: "The firing action's args \u2014 bound while its effect bindings and where-op `where` conditions evaluate."
439
+ },
440
+ {
441
+ name: "subworkflows",
442
+ binding: "spawn",
443
+ description: "The spawned child rows (status included) of the spawning activity, bound for its `completeWhen`/`failWhen` \u2014 the implicit completion gate reads it."
444
+ }
445
+ ], RESERVED_CONDITION_VARS = CONDITION_VARS.map((v2) => v2.name), FILTER_SCOPE_VARS = CONDITION_VARS.filter(
446
+ (v2) => v2.binding === "always"
447
+ ).map((v2) => v2.name), GUARD_PREDICATE_VARS = [
448
+ {
449
+ name: "guard",
450
+ description: "The guard document itself (its `metadata` carries deploy-time resolved values)."
451
+ },
452
+ {
453
+ name: "mutation",
454
+ description: "The attempted mutation \u2014 `mutation.action` is the write kind being gated."
455
+ }
456
+ ], ACTOR_KINDS = ["person", "agent", "system"];
457
+ class FieldValueShapeError extends WorkflowError {
458
+ entryType;
459
+ entryName;
460
+ issues;
461
+ constructor(args) {
462
+ const issueText = args.issues.join("; ");
463
+ super(
464
+ "field-value-shape",
465
+ `Field entry ${args.mode} shape invalid for "${args.entryName}" (${args.entryType}): ${issueText}`
466
+ ), this.name = "FieldValueShapeError", this.entryType = args.entryType, this.entryName = args.entryName, this.issues = args.issues;
467
+ }
468
+ }
469
+ const GdrShape = v__namespace.looseObject({
470
+ id: v__namespace.pipe(
471
+ v__namespace.string(),
472
+ v__namespace.check((s) => isGdrUri(s), "must be a GDR URI")
473
+ ),
474
+ type: v__namespace.pipe(v__namespace.string(), v__namespace.minLength(1))
475
+ }), ReleaseRefShape = v__namespace.looseObject({
476
+ id: v__namespace.pipe(
477
+ v__namespace.string(),
478
+ v__namespace.check((s) => isGdrUri(s), "must be a GDR URI")
479
+ ),
480
+ type: v__namespace.literal("system.release"),
481
+ releaseName: v__namespace.pipe(v__namespace.string(), v__namespace.minLength(1))
482
+ }), ActorShape = v__namespace.looseObject({
483
+ kind: v__namespace.picklist(ACTOR_KINDS),
484
+ id: v__namespace.pipe(v__namespace.string(), v__namespace.minLength(1)),
485
+ roles: v__namespace.optional(v__namespace.array(v__namespace.string())),
486
+ onBehalfOf: v__namespace.optional(v__namespace.string())
487
+ }), AssigneeShape = v__namespace.union([
488
+ v__namespace.looseObject({ type: v__namespace.literal("user"), id: v__namespace.pipe(v__namespace.string(), v__namespace.minLength(1)) }),
489
+ v__namespace.looseObject({ type: v__namespace.literal("role"), role: v__namespace.pipe(v__namespace.string(), v__namespace.minLength(1)) })
490
+ ]), 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([
491
+ v__namespace.null(),
492
+ v__namespace.pipe(
493
+ v__namespace.string(),
494
+ v__namespace.check((s) => !Number.isNaN(Date.parse(s)), "must be an ISO-8601 datetime string")
495
+ )
496
+ ]), NullableDate = v__namespace.union([
497
+ v__namespace.null(),
498
+ v__namespace.pipe(v__namespace.string(), v__namespace.regex(/^\d{4}-\d{2}-\d{2}$/, "must be a `YYYY-MM-DD` date"))
499
+ ]), NullableUrl = NullableString, valueSchemas = {
500
+ "doc.ref": v__namespace.union([v__namespace.null(), GdrShape]),
501
+ "doc.refs": v__namespace.array(GdrShape),
502
+ "release.ref": v__namespace.union([v__namespace.null(), ReleaseRefShape]),
503
+ query: v__namespace.any(),
504
+ string: NullableString,
505
+ text: NullableString,
506
+ number: NullableNumber,
507
+ boolean: NullableBoolean,
508
+ date: NullableDate,
509
+ datetime: NullableDateTime,
510
+ url: NullableUrl,
511
+ actor: v__namespace.union([v__namespace.null(), ActorShape]),
512
+ assignee: v__namespace.union([v__namespace.null(), AssigneeShape]),
513
+ assignees: v__namespace.array(AssigneeShape)
514
+ };
515
+ function shapeValueSchema(shape, leaf) {
516
+ return shape.type === "object" ? objectSchema(shape.fields ?? [], leaf) : shape.type === "array" ? v__namespace.array(objectSchema(shape.of ?? [], leaf)) : leaf[shape.type] ?? v__namespace.any();
517
+ }
518
+ function objectSchema(fields, leaf) {
519
+ const entries = /* @__PURE__ */ Object.create(null);
520
+ for (const f of fields) entries[f.name] = v__namespace.optional(shapeValueSchema(f, leaf));
521
+ return v__namespace.looseObject(entries);
522
+ }
523
+ function wholeValueSchema(args) {
524
+ const { entryType, shape, leaf } = args;
525
+ return entryType === "object" ? v__namespace.union([v__namespace.null(), objectSchema(shape.fields ?? [], leaf)]) : entryType === "array" ? v__namespace.array(objectSchema(shape.of ?? [], leaf)) : leaf[entryType];
526
+ }
527
+ function appendItemSchema(entryType, shape) {
528
+ if (entryType === "array") return objectSchema(shape.of ?? [], valueSchemas);
529
+ if (entryType === "doc.refs") return GdrShape;
530
+ if (entryType === "assignees") return AssigneeShape;
531
+ }
532
+ function checkFieldValue(args) {
533
+ return checkValueAgainst(args, valueSchemas);
534
+ }
535
+ function checkValueAgainst(args, leaf) {
536
+ const schema = wholeValueSchema({ entryType: args.entryType, shape: args, leaf });
537
+ if (schema === void 0) return [`unknown field entry type ${args.entryType}`];
538
+ const result = v__namespace.safeParse(schema, args.value);
539
+ return result.success ? void 0 : formatIssues(result.issues);
540
+ }
541
+ function validateFieldValue(args) {
542
+ const issues = checkFieldValue(args);
543
+ if (issues !== void 0)
544
+ throw new FieldValueShapeError({
545
+ entryType: args.entryType,
546
+ entryName: args.entryName,
547
+ issues,
548
+ mode: "value"
549
+ });
550
+ }
551
+ 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}$`);
552
+ function isAuthoringRefId(id) {
553
+ return id.startsWith("@") ? ALIAS_REF_RE.test(id) : id.includes(":") ? isGdrUri(id) : BARE_ID_RE.test(id);
554
+ }
555
+ function isBareSeedId(id) {
556
+ return BARE_ID_RE.test(id);
557
+ }
558
+ const AuthoringRefId = v__namespace.pipe(
559
+ v__namespace.string(),
560
+ v__namespace.check(
561
+ isAuthoringRefId,
562
+ "must be a bare document id, a GDR URI, or a portable `@<alias>:<id>` reference"
563
+ )
564
+ ), AuthoringGdrShape = v__namespace.looseObject({
565
+ id: AuthoringRefId,
566
+ type: v__namespace.pipe(v__namespace.string(), v__namespace.minLength(1))
567
+ }), seedValueSchemas = {
568
+ ...valueSchemas,
569
+ "doc.ref": v__namespace.union([v__namespace.null(), AuthoringGdrShape]),
570
+ "doc.refs": v__namespace.array(AuthoringGdrShape),
571
+ "release.ref": v__namespace.union([
572
+ v__namespace.null(),
573
+ v__namespace.looseObject({
574
+ id: AuthoringRefId,
575
+ type: v__namespace.literal("system.release"),
576
+ releaseName: v__namespace.pipe(v__namespace.string(), v__namespace.minLength(1))
577
+ })
578
+ ])
579
+ };
580
+ function checkLiteralSeed(args) {
581
+ return args.value === null ? ["a literal seed cannot be null \u2014 omit `initialValue` to start the field empty"] : checkValueAgainst(args, seedValueSchemas);
582
+ }
583
+ function validateFieldAppendItem(args) {
584
+ const schema = appendItemSchema(args.entryType, args);
585
+ if (schema === void 0)
586
+ throw new FieldValueShapeError({
587
+ entryType: args.entryType,
588
+ entryName: args.entryName,
589
+ issues: [`field entry type ${args.entryType} does not support append`],
590
+ mode: "item"
591
+ });
592
+ const result = v__namespace.safeParse(schema, args.item);
593
+ if (!result.success)
594
+ throw new FieldValueShapeError({
595
+ entryType: args.entryType,
596
+ entryName: args.entryName,
597
+ issues: formatIssues(result.issues),
598
+ mode: "item"
599
+ });
600
+ }
601
+ function formatIssues(issues) {
602
+ return issues.map((i) => {
603
+ const keys = i.path?.map((p) => p.key) ?? [];
604
+ return `${keys.length > 0 ? `at ${keys.join(".")}: ` : ""}${i.message}`;
605
+ });
606
+ }
45
607
  const ACTIVITY_STATUSES = ["pending", "active", "done", "skipped", "failed"], TERMINAL_ACTIVITY_STATUSES = ["done", "skipped", "failed"];
46
608
  function isTerminalActivityStatus(status) {
47
609
  return TERMINAL_ACTIVITY_STATUSES.includes(status);
@@ -68,6 +630,9 @@ function picklist(options) {
68
630
  `Invalid option: expected one of ${options.map((o) => `"${o}"`).join("|")}`
69
631
  );
70
632
  }
633
+ function pinned() {
634
+ return (schema, ..._exact) => schema;
635
+ }
71
636
  const LiteralSchema = v__namespace.strictObject({ type: v__namespace.literal("literal"), value: v__namespace.unknown() }), FieldReadSchema = v__namespace.strictObject({
72
637
  type: v__namespace.literal("fieldRead"),
73
638
  scope: v__namespace.optional(v__namespace.union([v__namespace.literal("workflow"), v__namespace.literal("stage")])),
@@ -116,17 +681,7 @@ function manualTargetSchema(ref) {
116
681
  v__namespace.strictObject({ type: v__namespace.literal("field"), field: ref })
117
682
  ]);
118
683
  }
119
- const StoredManualTargetSchema = manualTargetSchema(StoredFieldRefSchema), AuthoringManualTargetSchema = manualTargetSchema(v__namespace.union([NonEmpty, AuthoringFieldRefSchema])), OpPredicateSchema = v__namespace.lazy(
120
- () => v__namespace.union([
121
- v__namespace.strictObject({
122
- type: v__namespace.literal("field"),
123
- field: NonEmpty,
124
- equals: ValueExprSchema
125
- }),
126
- v__namespace.strictObject({ type: v__namespace.literal("all"), of: v__namespace.array(OpPredicateSchema) }),
127
- v__namespace.strictObject({ type: v__namespace.literal("any"), of: v__namespace.array(OpPredicateSchema) })
128
- ])
129
- );
684
+ const StoredManualTargetSchema = manualTargetSchema(StoredFieldRefSchema), AuthoringManualTargetSchema = manualTargetSchema(v__namespace.union([NonEmpty, AuthoringFieldRefSchema])), ConditionSchema = NonEmpty;
130
685
  function opSchemas(targetSchema) {
131
686
  return [
132
687
  v__namespace.strictObject({
@@ -146,24 +701,24 @@ function opSchemas(targetSchema) {
146
701
  v__namespace.strictObject({
147
702
  type: v__namespace.literal("field.updateWhere"),
148
703
  target: targetSchema,
149
- where: OpPredicateSchema,
704
+ where: ConditionSchema,
150
705
  value: ValueExprSchema
151
706
  }),
152
707
  v__namespace.strictObject({
153
708
  type: v__namespace.literal("field.removeWhere"),
154
709
  target: targetSchema,
155
- where: OpPredicateSchema
710
+ where: ConditionSchema
156
711
  })
157
712
  ];
158
713
  }
159
- const StoredOpSchema = v__namespace.variant("type", [
714
+ const StoredFieldOpSchema = v__namespace.variant("type", [...opSchemas(StoredFieldRefSchema)]), StoredOpSchema = v__namespace.variant("type", [
160
715
  ...opSchemas(StoredFieldRefSchema),
161
716
  v__namespace.strictObject({
162
717
  type: v__namespace.literal("status.set"),
163
718
  activity: NonEmpty,
164
719
  status: picklist(ACTIVITY_STATUSES)
165
720
  })
166
- ]), StoredTransitionOpSchema = v__namespace.variant("type", [...opSchemas(StoredFieldRefSchema)]), AuditOpSchema = v__namespace.strictObject({
721
+ ]), StoredTransitionOpSchema = StoredFieldOpSchema, AuditOpSchema = v__namespace.strictObject({
167
722
  type: v__namespace.literal("audit"),
168
723
  target: AuthoringFieldRefSchema,
169
724
  value: ValueExprSchema,
@@ -252,7 +807,7 @@ const FieldShapeSchema = v__namespace.lazy(
252
807
  of: v__namespace.optional(v__namespace.array(FieldShapeSchema))
253
808
  })
254
809
  ), StoredEditableSchema = v__namespace.union([v__namespace.literal(!0), NonEmpty]), AuthoringEditableSchema = v__namespace.union([v__namespace.literal(!0), v__namespace.array(NonEmpty), NonEmpty]);
255
- function slotFields(editable) {
810
+ function fieldBase(editable) {
256
811
  return {
257
812
  name: FieldEntryName,
258
813
  title: v__namespace.optional(v__namespace.string()),
@@ -268,41 +823,62 @@ function slotFields(editable) {
268
823
  /**
269
824
  * How this field SEEDS its value at materialisation — see
270
825
  * {@link FieldSourceSchema}. Optional: an absent `initialValue` is working
271
- * memory (the slot starts empty and an op fills it later), the common case.
826
+ * memory (the field starts empty and an op fills it later), the common case.
272
827
  */
273
828
  initialValue: v__namespace.optional(FieldSourceSchema),
274
- /** Who-may-edit this slot via the edit seam — see {@link StoredEditableSchema}. */
829
+ /** Who-may-edit this field via the edit seam — see {@link StoredEditableSchema}. */
275
830
  editable: v__namespace.optional(editable)
276
831
  };
277
832
  }
278
833
  function fieldEntryFields(editable) {
279
834
  return {
280
835
  type: FieldKindSchema,
281
- ...slotFields(editable),
836
+ ...fieldBase(editable),
282
837
  /** Sub-field shapes for an `object` kind (see {@link compositeShapeOk}). */
283
838
  fields: v__namespace.optional(v__namespace.array(FieldShapeSchema)),
284
839
  /** Item sub-field shapes for an `array` kind (see {@link compositeShapeOk}). */
285
840
  of: v__namespace.optional(v__namespace.array(FieldShapeSchema))
286
841
  };
287
842
  }
288
- const FieldEntrySchema = compositeChecked(fieldEntryFields(StoredEditableSchema)), RawAuthoringFieldEntrySchema = compositeChecked(fieldEntryFields(AuthoringEditableSchema)), ClaimFieldSchema = v__namespace.strictObject({
289
- type: v__namespace.literal("claim"),
290
- name: FieldEntryName,
291
- title: v__namespace.optional(v__namespace.string()),
292
- description: v__namespace.optional(v__namespace.string())
293
- });
843
+ function literalSeedIssues(entry) {
844
+ if (entry.initialValue?.type === "literal")
845
+ return checkLiteralSeed({
846
+ entryType: entry.type,
847
+ value: entry.initialValue.value,
848
+ fields: entry.fields,
849
+ of: entry.of
850
+ });
851
+ }
852
+ function literalSeedCheck() {
853
+ return v__namespace.check(
854
+ (entry) => literalSeedIssues(entry) === void 0,
855
+ (issue) => `initialValue literal does not fit the declared kind: ${(literalSeedIssues(issue.input) ?? []).join("; ")}`
856
+ );
857
+ }
858
+ const FieldEntrySchema = pinned()(
859
+ v__namespace.pipe(compositeChecked(fieldEntryFields(StoredEditableSchema)), literalSeedCheck())
860
+ ), RawAuthoringFieldEntrySchema = pinned()(
861
+ v__namespace.pipe(
862
+ compositeChecked(fieldEntryFields(AuthoringEditableSchema)),
863
+ literalSeedCheck()
864
+ )
865
+ ), ClaimFieldSchema = pinned()(
866
+ v__namespace.strictObject({
867
+ type: v__namespace.literal("claim"),
868
+ name: FieldEntryName,
869
+ title: v__namespace.optional(v__namespace.string()),
870
+ description: v__namespace.optional(v__namespace.string())
871
+ })
872
+ );
294
873
  function listSugarFields(type) {
295
874
  return {
296
875
  type: v__namespace.literal(type),
297
- ...slotFields(AuthoringEditableSchema)
876
+ ...fieldBase(AuthoringEditableSchema)
298
877
  };
299
878
  }
300
- const TodoListFieldSchema = v__namespace.strictObject(listSugarFields("todoList")), NotesFieldSchema = v__namespace.strictObject(listSugarFields("notes")), AuthoringFieldEntrySchema = v__namespace.union([
301
- RawAuthoringFieldEntrySchema,
302
- ClaimFieldSchema,
303
- TodoListFieldSchema,
304
- NotesFieldSchema
305
- ]), ConditionSchema = NonEmpty, EffectSchema = v__namespace.strictObject({
879
+ const TodoListFieldSchema = pinned()(v__namespace.strictObject(listSugarFields("todoList"))), NotesFieldSchema = pinned()(v__namespace.strictObject(listSugarFields("notes"))), AuthoringFieldEntrySchema = pinned()(
880
+ v__namespace.union([RawAuthoringFieldEntrySchema, ClaimFieldSchema, TodoListFieldSchema, NotesFieldSchema])
881
+ ), EffectSchema = v__namespace.strictObject({
306
882
  name: NonEmpty,
307
883
  title: v__namespace.optional(v__namespace.string()),
308
884
  description: v__namespace.optional(v__namespace.string()),
@@ -334,8 +910,8 @@ function actionFields(op) {
334
910
  description: v__namespace.optional(v__namespace.string()),
335
911
  /**
336
912
  * The one gate mechanism: a condition over the rendered scope. Hard
337
- * enforcement lives outside the engine entirely (the lake ACL on the
338
- * documents, and guards) — every engine-evaluated gate is authoring/UX.
913
+ * enforcement lives outside the engine; every engine-evaluated gate is
914
+ * authoring/UX.
339
915
  */
340
916
  filter: v__namespace.optional(ConditionSchema),
341
917
  params: v__namespace.optional(v__namespace.array(ActionParamSchema)),
@@ -343,21 +919,27 @@ function actionFields(op) {
343
919
  effects: v__namespace.optional(v__namespace.array(EffectSchema))
344
920
  };
345
921
  }
346
- const StoredActionSchema = v__namespace.strictObject(actionFields(StoredOpSchema)), TerminalActivityStatus = picklist(TERMINAL_ACTIVITY_STATUSES), RawAuthoringActionSchema = v__namespace.strictObject({
347
- ...actionFields(AuthoringOpSchema),
348
- roles: v__namespace.optional(v__namespace.array(NonEmpty)),
349
- status: v__namespace.optional(TerminalActivityStatus)
350
- }), ClaimActionSchema = v__namespace.strictObject({
351
- type: v__namespace.literal("claim"),
352
- name: NonEmpty,
353
- title: v__namespace.optional(v__namespace.string()),
354
- description: v__namespace.optional(v__namespace.string()),
355
- field: v__namespace.union([NonEmpty, AuthoringFieldRefSchema]),
356
- roles: v__namespace.optional(v__namespace.array(NonEmpty)),
357
- filter: v__namespace.optional(ConditionSchema),
358
- params: v__namespace.optional(v__namespace.array(ActionParamSchema)),
359
- effects: v__namespace.optional(v__namespace.array(EffectSchema))
360
- }), AuthoringActionSchema = v__namespace.union([RawAuthoringActionSchema, ClaimActionSchema]), DefinitionRefSchema = v__namespace.strictObject({
922
+ const StoredActionSchema = pinned()(v__namespace.strictObject(actionFields(StoredOpSchema))), TerminalActivityStatus = picklist(TERMINAL_ACTIVITY_STATUSES), RawAuthoringActionSchema = pinned()(
923
+ v__namespace.strictObject({
924
+ ...actionFields(AuthoringOpSchema),
925
+ roles: v__namespace.optional(v__namespace.array(NonEmpty)),
926
+ status: v__namespace.optional(TerminalActivityStatus)
927
+ })
928
+ ), ClaimActionSchema = pinned()(
929
+ v__namespace.strictObject({
930
+ type: v__namespace.literal("claim"),
931
+ name: NonEmpty,
932
+ title: v__namespace.optional(v__namespace.string()),
933
+ description: v__namespace.optional(v__namespace.string()),
934
+ field: v__namespace.union([NonEmpty, AuthoringFieldRefSchema]),
935
+ roles: v__namespace.optional(v__namespace.array(NonEmpty)),
936
+ filter: v__namespace.optional(ConditionSchema),
937
+ params: v__namespace.optional(v__namespace.array(ActionParamSchema)),
938
+ effects: v__namespace.optional(v__namespace.array(EffectSchema))
939
+ })
940
+ ), AuthoringActionSchema = pinned()(
941
+ v__namespace.union([RawAuthoringActionSchema, ClaimActionSchema])
942
+ ), DefinitionRefSchema = v__namespace.strictObject({
361
943
  name: NonEmpty,
362
944
  version: v__namespace.optional(v__namespace.union([PositiveInt, v__namespace.literal("latest")]))
363
945
  }), SubworkflowsSchema = v__namespace.strictObject({
@@ -373,7 +955,7 @@ const StoredActionSchema = v__namespace.strictObject(actionFields(StoredOpSchema
373
955
  */
374
956
  context: v__namespace.optional(v__namespace.record(NonEmpty, ConditionSchema))
375
957
  });
376
- function activityFields(field, action, op, target) {
958
+ function activityFields({ field, action, op, target }) {
377
959
  return {
378
960
  name: NonEmpty,
379
961
  title: v__namespace.optional(v__namespace.string()),
@@ -396,8 +978,8 @@ function activityFields(field, action, op, target) {
396
978
  * (visibility). An unmet requirement keeps the activity visible but disables
397
979
  * its actions with a `requirements-unmet` verdict naming the unmet keys;
398
980
  * all must hold (any-of lives inside one condition). Advisory like every
399
- * engine gate — the lake still enforces. Distinct from ACL (authorization)
400
- * and guards (content-write locks).
981
+ * engine gate. Distinct from ACL (authorization) and guards
982
+ * (content-write locks).
401
983
  */
402
984
  requirements: v__namespace.optional(v__namespace.record(NonEmpty, ConditionSchema)),
403
985
  /**
@@ -420,14 +1002,23 @@ function activityFields(field, action, op, target) {
420
1002
  fields: v__namespace.optional(v__namespace.array(field))
421
1003
  };
422
1004
  }
423
- const StoredActivitySchema = v__namespace.strictObject(
424
- activityFields(FieldEntrySchema, StoredActionSchema, StoredOpSchema, StoredManualTargetSchema)
425
- ), AuthoringActivitySchema = v__namespace.strictObject(
426
- activityFields(
427
- AuthoringFieldEntrySchema,
428
- AuthoringActionSchema,
429
- AuthoringOpSchema,
430
- AuthoringManualTargetSchema
1005
+ const StoredActivitySchema = pinned()(
1006
+ v__namespace.strictObject(
1007
+ activityFields({
1008
+ field: FieldEntrySchema,
1009
+ action: StoredActionSchema,
1010
+ op: StoredOpSchema,
1011
+ target: StoredManualTargetSchema
1012
+ })
1013
+ )
1014
+ ), AuthoringActivitySchema = pinned()(
1015
+ v__namespace.strictObject(
1016
+ activityFields({
1017
+ field: AuthoringFieldEntrySchema,
1018
+ action: AuthoringActionSchema,
1019
+ op: AuthoringOpSchema,
1020
+ target: AuthoringManualTargetSchema
1021
+ })
431
1022
  )
432
1023
  );
433
1024
  function transitionFields(op, filter) {
@@ -442,47 +1033,84 @@ function transitionFields(op, filter) {
442
1033
  effects: v__namespace.optional(v__namespace.array(EffectSchema))
443
1034
  };
444
1035
  }
445
- const StoredTransitionSchema = v__namespace.strictObject(
446
- transitionFields(StoredTransitionOpSchema, ConditionSchema)
1036
+ const StoredTransitionSchema = pinned()(
1037
+ v__namespace.strictObject(transitionFields(StoredTransitionOpSchema, ConditionSchema))
447
1038
  ), AuthoringTransitionOpSchema = v__namespace.variant("type", [
448
1039
  ...opSchemas(AuthoringFieldRefSchema),
449
1040
  AuditOpSchema
450
- ]), AuthoringTransitionSchema = v__namespace.strictObject(
451
- transitionFields(AuthoringTransitionOpSchema, v__namespace.optional(ConditionSchema))
452
- ), GuardActionSchema = picklist(MUTATION_GUARD_ACTIONS), GuardMatchSchema = v__namespace.strictObject({
453
- /** Subject `_type`(s); empty matches any type. */
454
- types: v__namespace.optional(v__namespace.array(NonEmpty)),
455
- /** Target docs as `$fields` reads (or `"$self"`), resolved at deploy to bare ids + the resource. */
456
- idRefs: v__namespace.optional(v__namespace.array(NonEmpty)),
457
- /** Glob id patterns (bare, resource-local). */
458
- idPatterns: v__namespace.optional(v__namespace.array(NonEmpty)),
459
- actions: v__namespace.pipe(
460
- v__namespace.array(GuardActionSchema),
461
- v__namespace.minLength(1, "a guard must match at least one action")
1041
+ ]), AuthoringTransitionSchema = pinned()(
1042
+ v__namespace.strictObject(transitionFields(AuthoringTransitionOpSchema, v__namespace.optional(ConditionSchema)))
1043
+ ), GuardActionSchema = picklist(MUTATION_GUARD_ACTIONS), GuardReadPath = v__namespace.pipe(
1044
+ NonEmpty,
1045
+ v__namespace.check(
1046
+ (path) => !/[\r\n\u2028\u2029]/.test(path),
1047
+ "a guard read path cannot contain a line break"
462
1048
  )
463
- }), GuardSchema = v__namespace.strictObject({
464
- name: NonEmpty,
465
- title: v__namespace.optional(v__namespace.string()),
466
- description: v__namespace.optional(v__namespace.string()),
467
- match: GuardMatchSchema,
468
- /**
469
- * Lake GROQ predicate — a distinct eval context reading
470
- * `document.before`/`document.after`, `mutation`, `guard`, and
471
- * `identity()`. Bare ids/fields only. Omitted or empty means
472
- * UNCONDITIONAL DENY.
473
- */
474
- predicate: v__namespace.optional(v__namespace.string()),
475
- /**
476
- * Projected workflow fields the predicate reads as `guard.metadata.*` —
477
- * the only bridge from the lake eval context (which cannot see `$fields`)
478
- * to workflow fields. Values are NOT GROQ: each is a deploy-time read in
479
- * the guard mini-language — `"$self"`, `"$now"`, or
480
- * `"$fields.<name>[.path]"` — resolved into a bare value at deploy and
481
- * re-synced by the post-field-op guard refresh.
482
- */
483
- metadata: v__namespace.optional(v__namespace.record(NonEmpty, NonEmpty))
484
- });
485
- function stageFields(field, activity, transition, editable) {
1049
+ ), GuardReadSchema = v__namespace.variant("type", [
1050
+ v__namespace.strictObject({ type: v__namespace.literal("self") }),
1051
+ v__namespace.strictObject({ type: v__namespace.literal("now") }),
1052
+ v__namespace.strictObject({
1053
+ type: v__namespace.literal("fieldRead"),
1054
+ field: FieldEntryName,
1055
+ path: v__namespace.optional(GuardReadPath)
1056
+ }),
1057
+ v__namespace.strictObject({
1058
+ type: v__namespace.literal("effectsRead"),
1059
+ // The stored spelling bracket-quotes the name (`$effects['<name>']`), so a
1060
+ // quote inside it would corrupt the printed read.
1061
+ effect: v__namespace.pipe(
1062
+ NonEmpty,
1063
+ v__namespace.check((name) => !name.includes("'"), "an effect name cannot contain `'`")
1064
+ ),
1065
+ path: v__namespace.optional(GuardReadPath)
1066
+ })
1067
+ ]);
1068
+ function guardMatchFields(read) {
1069
+ return {
1070
+ /** Subject `_type`(s); empty matches any type. */
1071
+ types: v__namespace.optional(v__namespace.array(NonEmpty)),
1072
+ /** Target docs as field reads (or the instance itself), resolved at deploy to bare ids + the resource. */
1073
+ idRefs: v__namespace.optional(v__namespace.array(read)),
1074
+ /** Glob id patterns (bare, resource-local). */
1075
+ idPatterns: v__namespace.optional(v__namespace.array(NonEmpty)),
1076
+ actions: v__namespace.pipe(
1077
+ v__namespace.array(GuardActionSchema),
1078
+ v__namespace.minLength(1, "a guard must match at least one action")
1079
+ )
1080
+ };
1081
+ }
1082
+ function guardFields(read) {
1083
+ return {
1084
+ name: NonEmpty,
1085
+ title: v__namespace.optional(v__namespace.string()),
1086
+ description: v__namespace.optional(v__namespace.string()),
1087
+ match: v__namespace.strictObject(guardMatchFields(read)),
1088
+ /**
1089
+ * Lake GROQ predicate — a distinct eval context reading
1090
+ * `document.before`/`document.after`, `mutation`, `guard`, and
1091
+ * `identity()`. Bare ids/fields only. Omitted or empty means
1092
+ * UNCONDITIONAL DENY.
1093
+ */
1094
+ predicate: v__namespace.optional(v__namespace.string()),
1095
+ /**
1096
+ * Projected workflow fields the predicate reads as `guard.metadata.*` —
1097
+ * the only bridge from the lake eval context (which cannot see `$fields`)
1098
+ * to workflow fields. Each value is a deploy-time read — a typed
1099
+ * {@link GuardRead} when authoring, the printed string spelling once
1100
+ * stored — resolved into a bare value at deploy and re-synced by the
1101
+ * post-field-op guard refresh.
1102
+ */
1103
+ metadata: v__namespace.optional(v__namespace.record(NonEmpty, read))
1104
+ };
1105
+ }
1106
+ const GuardSchema = v__namespace.strictObject(guardFields(NonEmpty)), AuthoringGuardSchema = v__namespace.strictObject(guardFields(GuardReadSchema));
1107
+ function stageFields({
1108
+ field,
1109
+ activity,
1110
+ transition,
1111
+ guard,
1112
+ editable
1113
+ }) {
486
1114
  return {
487
1115
  name: NonEmpty,
488
1116
  title: v__namespace.optional(v__namespace.string()),
@@ -491,49 +1119,60 @@ function stageFields(field, activity, transition, editable) {
491
1119
  transitions: v__namespace.optional(v__namespace.array(transition)),
492
1120
  /**
493
1121
  * Lake mutation guards active while this stage holds. Each compiles to a
494
- * `temp.system.guard` doc deployed on stage entry and retracted on exit.
1122
+ * persisted guard document deployed on stage entry and retracted on exit.
495
1123
  */
496
- guards: v__namespace.optional(v__namespace.array(GuardSchema)),
1124
+ guards: v__namespace.optional(v__namespace.array(guard)),
497
1125
  /** Stage-scoped field entries. Resolved at stage entry. */
498
1126
  fields: v__namespace.optional(v__namespace.array(field)),
499
1127
  /**
500
1128
  * Tighten-only editability overrides for the time this stage holds, keyed
501
- * by an in-scope slot name. The slot's own `editable` is the ceiling; a
1129
+ * by an in-scope field name. The field's own `editable` is the ceiling; a
502
1130
  * stage value is ANDed with it at runtime, so an override can only NARROW
503
- * (never open a slot the baseline left closed). An unlisted slot inherits
1131
+ * (never open a field the baseline left closed). An unlisted field inherits
504
1132
  * its baseline.
505
1133
  */
506
1134
  editable: v__namespace.optional(v__namespace.record(FieldEntryName, editable))
507
1135
  };
508
1136
  }
509
- const StoredStageSchema = v__namespace.strictObject(
510
- stageFields(FieldEntrySchema, StoredActivitySchema, StoredTransitionSchema, StoredEditableSchema)
511
- ), AuthoringStageSchema = v__namespace.strictObject(
512
- stageFields(
513
- AuthoringFieldEntrySchema,
514
- AuthoringActivitySchema,
515
- AuthoringTransitionSchema,
516
- AuthoringEditableSchema
1137
+ const StoredStageSchema = pinned()(
1138
+ v__namespace.strictObject(
1139
+ stageFields({
1140
+ field: FieldEntrySchema,
1141
+ activity: StoredActivitySchema,
1142
+ transition: StoredTransitionSchema,
1143
+ guard: GuardSchema,
1144
+ editable: StoredEditableSchema
1145
+ })
1146
+ )
1147
+ ), AuthoringStageSchema = pinned()(
1148
+ v__namespace.strictObject(
1149
+ stageFields({
1150
+ field: AuthoringFieldEntrySchema,
1151
+ activity: AuthoringActivitySchema,
1152
+ transition: AuthoringTransitionSchema,
1153
+ guard: AuthoringGuardSchema,
1154
+ editable: AuthoringEditableSchema
1155
+ })
517
1156
  )
518
1157
  ), RoleAliasesSchema = v__namespace.record(
519
1158
  NonEmpty,
520
1159
  v__namespace.pipe(v__namespace.array(NonEmpty), v__namespace.minLength(1, "a role alias must list at least one fulfilling role"))
521
- ), WORKFLOW_ROLES = ["workflow", "child"];
1160
+ ), WORKFLOW_LIFECYCLES = ["standalone", "child"];
522
1161
  function workflowFields(field, stage) {
523
1162
  return {
524
1163
  name: NonEmpty,
525
1164
  title: NonEmpty,
526
1165
  description: v__namespace.optional(v__namespace.string()),
527
1166
  /**
528
- * Whether a human may start this workflow standalone. `'child'` marks a
1167
+ * How instances of this workflow come to exist. `'child'` marks a
529
1168
  * spawn-only definition — instantiated by a parent via `activity.subworkflows`,
530
- * never started cold from a picker. Omitted ⇒ `'workflow'` (startable).
1169
+ * never started cold from a picker. Omitted ⇒ `'standalone'` (startable).
531
1170
  * Advisory: consumers filter their start pickers on it (see
532
1171
  * {@link isStartableDefinition}); the engine does NOT refuse a
533
1172
  * `startInstance` on a `'child'` def — load-bearing `required` field is the
534
1173
  * runtime backstop.
535
1174
  */
536
- role: v__namespace.optional(picklist(WORKFLOW_ROLES)),
1175
+ lifecycle: v__namespace.optional(picklist(WORKFLOW_LIFECYCLES)),
537
1176
  /** Reference field: named for the target, holds the stage's `name`. */
538
1177
  initialStage: NonEmpty,
539
1178
  /** Workflow-scope field entries. Persist for the instance lifetime. */
@@ -549,12 +1188,14 @@ function workflowFields(field, stage) {
549
1188
  roleAliases: v__namespace.optional(RoleAliasesSchema)
550
1189
  };
551
1190
  }
552
- v__namespace.strictObject(workflowFields(FieldEntrySchema, StoredStageSchema));
553
- const AuthoringWorkflowSchema = v__namespace.strictObject(
554
- workflowFields(AuthoringFieldEntrySchema, AuthoringStageSchema)
1191
+ pinned()(
1192
+ v__namespace.strictObject(workflowFields(FieldEntrySchema, StoredStageSchema))
1193
+ );
1194
+ const AuthoringWorkflowSchema = pinned()(
1195
+ v__namespace.strictObject(workflowFields(AuthoringFieldEntrySchema, AuthoringStageSchema))
555
1196
  ), WORKFLOW_DEFINITION_TYPE = "sanity.workflow.definition";
556
1197
  function isStartableDefinition(definition) {
557
- return definition.role !== "child";
1198
+ return definition.lifecycle !== "child";
558
1199
  }
559
1200
  function formatValidationError(label, issues) {
560
1201
  const lines = issues.map((issue) => ` - ${issue.path.length === 0 ? "(root)" : formatPath(issue.path)}: ${issue.message}`);
@@ -575,25 +1216,74 @@ function formatPath(path) {
575
1216
  return out;
576
1217
  }
577
1218
  exports.ACTIVITY_KINDS = ACTIVITY_KINDS;
1219
+ exports.ACTOR_KINDS = ACTOR_KINDS;
578
1220
  exports.AuthoringActionSchema = AuthoringActionSchema;
579
1221
  exports.AuthoringActivitySchema = AuthoringActivitySchema;
580
1222
  exports.AuthoringFieldEntrySchema = AuthoringFieldEntrySchema;
1223
+ exports.AuthoringGuardSchema = AuthoringGuardSchema;
581
1224
  exports.AuthoringOpSchema = AuthoringOpSchema;
582
1225
  exports.AuthoringStageSchema = AuthoringStageSchema;
583
1226
  exports.AuthoringTransitionSchema = AuthoringTransitionSchema;
584
1227
  exports.AuthoringWorkflowSchema = AuthoringWorkflowSchema;
1228
+ exports.CONDITION_VARS = CONDITION_VARS;
1229
+ exports.ContractViolationError = ContractViolationError;
585
1230
  exports.DOCUMENT_VALUE_PERMISSIONS = DOCUMENT_VALUE_PERMISSIONS;
586
1231
  exports.DRIVER_KINDS = DRIVER_KINDS;
1232
+ exports.DefinitionInUseError = DefinitionInUseError;
1233
+ exports.DefinitionNotFoundError = DefinitionNotFoundError;
1234
+ exports.EFFECTS_READ = EFFECTS_READ;
1235
+ exports.EffectNotFoundError = EffectNotFoundError;
587
1236
  exports.EffectSchema = EffectSchema;
1237
+ exports.FIELD_READ = FIELD_READ;
1238
+ exports.FILTER_SCOPE_VARS = FILTER_SCOPE_VARS;
1239
+ exports.FieldValueShapeError = FieldValueShapeError;
588
1240
  exports.GROQ_IDENTIFIER = GROQ_IDENTIFIER;
589
- exports.GuardSchema = GuardSchema;
1241
+ exports.GUARD_PREDICATE_VARS = GUARD_PREDICATE_VARS;
1242
+ exports.InstanceNotFoundError = InstanceNotFoundError;
1243
+ exports.RESERVED_CONDITION_VARS = RESERVED_CONDITION_VARS;
1244
+ exports.RESOURCE_ALIAS_NAME_SOURCE = RESOURCE_ALIAS_NAME_SOURCE;
1245
+ exports.StoredFieldOpSchema = StoredFieldOpSchema;
590
1246
  exports.WORKFLOW_DEFINITION_TYPE = WORKFLOW_DEFINITION_TYPE;
1247
+ exports.WorkflowConfigSchema = WorkflowConfigSchema;
1248
+ exports.WorkflowError = WorkflowError;
591
1249
  exports.actorFulfillsRole = actorFulfillsRole;
592
1250
  exports.andConditions = andConditions;
1251
+ exports.checkFieldValue = checkFieldValue;
1252
+ exports.datasetResourceParts = datasetResourceParts;
1253
+ exports.definitionDocId = definitionDocId;
593
1254
  exports.expandRequiredRoles = expandRequiredRoles;
1255
+ exports.extractDocumentId = extractDocumentId;
1256
+ exports.formatIssues = formatIssues;
594
1257
  exports.formatValidationError = formatValidationError;
1258
+ exports.gdrFromResource = gdrFromResource;
1259
+ exports.gdrRef = gdrRef;
1260
+ exports.gdrResourcePrefix = gdrResourcePrefix;
1261
+ exports.gdrUri = gdrUri;
1262
+ exports.isBareSeedId = isBareSeedId;
1263
+ exports.isGdr = isGdr;
1264
+ exports.isGdrUri = isGdrUri;
1265
+ exports.isGuardReadExpr = isGuardReadExpr;
595
1266
  exports.isStartableDefinition = isStartableDefinition;
596
1267
  exports.isTerminalActivityStatus = isTerminalActivityStatus;
597
1268
  exports.issuesFromValibot = issuesFromValibot;
598
1269
  exports.normalizeRoleAliases = normalizeRoleAliases;
1270
+ exports.parseGdr = parseGdr;
1271
+ exports.printGuardRead = printGuardRead;
1272
+ exports.refCanvas = refCanvas;
1273
+ exports.refDashboard = refDashboard;
1274
+ exports.refDataset = refDataset;
1275
+ exports.refMediaLibrary = refMediaLibrary;
1276
+ exports.resourceAliasesToMap = resourceAliasesToMap;
1277
+ exports.resourceFromGdrUri = resourceFromGdrUri;
1278
+ exports.resourceFromParsed = resourceFromParsed;
1279
+ exports.sameResource = sameResource;
1280
+ exports.selfGdr = selfGdr;
1281
+ exports.tagScopeFilter = tagScopeFilter;
1282
+ exports.toBareId = toBareId;
1283
+ exports.toPhysicalGdr = toPhysicalGdr;
1284
+ exports.tryParseGdr = tryParseGdr;
1285
+ exports.validateFieldAppendItem = validateFieldAppendItem;
1286
+ exports.validateFieldValue = validateFieldValue;
1287
+ exports.validateResourceAliasName = validateResourceAliasName;
1288
+ exports.validateTag = validateTag;
599
1289
  //# sourceMappingURL=schema.cjs.map