@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.
@@ -1,1289 +0,0 @@
1
- "use strict";
2
- var v = require("valibot");
3
- function _interopNamespaceCompat(e) {
4
- if (e && typeof e == "object" && "default" in e) return e;
5
- var n = /* @__PURE__ */ Object.create(null);
6
- return e && Object.keys(e).forEach(function(k) {
7
- if (k !== "default") {
8
- var d = Object.getOwnPropertyDescriptor(e, k);
9
- Object.defineProperty(n, k, d.get ? d : {
10
- enumerable: !0,
11
- get: function() {
12
- return e[k];
13
- }
14
- });
15
- }
16
- }), n.default = e, Object.freeze(n);
17
- }
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
- }
317
- function andConditions(parts) {
318
- const present = parts.filter((p) => p !== void 0);
319
- if (present.length !== 0)
320
- return present.length === 1 ? present[0] : present.map((p) => `(${p})`).join(" && ");
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
- }
338
- const UNIVERSAL_ROLE_ALIAS_KEY = "$all";
339
- function normalizeRoleAliases(aliases) {
340
- if (aliases === void 0 || !("*" in aliases)) return aliases;
341
- const { ["*"]: universal, ...perRole } = aliases;
342
- return { ...perRole, [UNIVERSAL_ROLE_ALIAS_KEY]: universal };
343
- }
344
- function expandRequiredRoles(required, aliases) {
345
- if (aliases === void 0) return [...required];
346
- const out = /* @__PURE__ */ new Set();
347
- for (const role of required) {
348
- out.add(role);
349
- for (const fulfiller of aliases[role] ?? []) out.add(fulfiller);
350
- }
351
- for (const fulfiller of aliases[UNIVERSAL_ROLE_ALIAS_KEY] ?? []) out.add(fulfiller);
352
- return [...out];
353
- }
354
- function actorFulfillsRole({
355
- actorRoles,
356
- required,
357
- aliases
358
- }) {
359
- if (actorRoles === void 0 || actorRoles.length === 0) return !1;
360
- const accepted = new Set(expandRequiredRoles([required], aliases));
361
- return actorRoles.some((role) => accepted.has(role));
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
- }
607
- const ACTIVITY_STATUSES = ["pending", "active", "done", "skipped", "failed"], TERMINAL_ACTIVITY_STATUSES = ["done", "skipped", "failed"];
608
- function isTerminalActivityStatus(status) {
609
- return TERMINAL_ACTIVITY_STATUSES.includes(status);
610
- }
611
- const FIELD_SCOPES = ["workflow", "stage", "activity"], DOCUMENT_VALUE_PERMISSIONS = ["create", "read", "update"], MUTATION_GUARD_ACTIONS = [
612
- "create",
613
- "update",
614
- "delete",
615
- "publish",
616
- "unpublish"
617
- ], ACTIVITY_KINDS = ["user", "service", "script", "manual", "receive"], DRIVER_KINDS = ["person", "agent", "service", "engine"], 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_]*$/;
618
- function groqIdentifier(referencedAs) {
619
- return v__namespace.pipe(
620
- v__namespace.string(),
621
- v__namespace.regex(
622
- GROQ_IDENTIFIER,
623
- `must be a GROQ-safe identifier (letters, digits, underscore; not starting with a digit) because it is referenced as ${referencedAs} in GROQ conditions`
624
- )
625
- );
626
- }
627
- function picklist(options) {
628
- return v__namespace.picklist(
629
- options,
630
- `Invalid option: expected one of ${options.map((o) => `"${o}"`).join("|")}`
631
- );
632
- }
633
- function pinned() {
634
- return (schema, ..._exact) => schema;
635
- }
636
- const LiteralSchema = v__namespace.strictObject({ type: v__namespace.literal("literal"), value: v__namespace.unknown() }), FieldReadSchema = v__namespace.strictObject({
637
- type: v__namespace.literal("fieldRead"),
638
- scope: v__namespace.optional(v__namespace.union([v__namespace.literal("workflow"), v__namespace.literal("stage")])),
639
- field: NonEmpty,
640
- path: v__namespace.optional(v__namespace.string())
641
- }), FieldSourceSchema = v__namespace.union([
642
- // The caller supplies it at start/spawn (`initialFields` / `subworkflows.with`).
643
- v__namespace.strictObject({ type: v__namespace.literal("input") }),
644
- // Computed once by running GROQ against the lake at materialisation.
645
- v__namespace.strictObject({ type: v__namespace.literal("query"), query: NonEmpty }),
646
- LiteralSchema,
647
- FieldReadSchema
648
- ]), ValueExprSchema = v__namespace.lazy(
649
- () => v__namespace.union([
650
- LiteralSchema,
651
- FieldReadSchema,
652
- v__namespace.strictObject({ type: v__namespace.literal("param"), param: NonEmpty }),
653
- v__namespace.strictObject({ type: v__namespace.literal("actor") }),
654
- v__namespace.strictObject({ type: v__namespace.literal("now") }),
655
- v__namespace.strictObject({ type: v__namespace.literal("self") }),
656
- v__namespace.strictObject({ type: v__namespace.literal("stage") }),
657
- v__namespace.strictObject({ type: v__namespace.literal("object"), fields: v__namespace.record(NonEmpty, ValueExprSchema) })
658
- ])
659
- ), StoredFieldRefSchema = v__namespace.strictObject({
660
- scope: picklist(FIELD_SCOPES),
661
- field: NonEmpty
662
- }), AuthoringFieldRefSchema = v__namespace.strictObject({
663
- scope: v__namespace.optional(picklist(FIELD_SCOPES)),
664
- field: NonEmpty
665
- }), HREF_SCHEMES = ["http:", "https:"];
666
- function isHttpUrl(value) {
667
- try {
668
- return HREF_SCHEMES.includes(new URL(value).protocol);
669
- } catch {
670
- return !1;
671
- }
672
- }
673
- const UrlString = v__namespace.pipe(
674
- v__namespace.string(),
675
- v__namespace.url("must be a valid URL"),
676
- v__namespace.check(isHttpUrl, "must be an http(s) URL")
677
- );
678
- function manualTargetSchema(ref) {
679
- return v__namespace.variant("type", [
680
- v__namespace.strictObject({ type: v__namespace.literal("url"), url: UrlString }),
681
- v__namespace.strictObject({ type: v__namespace.literal("field"), field: ref })
682
- ]);
683
- }
684
- const StoredManualTargetSchema = manualTargetSchema(StoredFieldRefSchema), AuthoringManualTargetSchema = manualTargetSchema(v__namespace.union([NonEmpty, AuthoringFieldRefSchema])), ConditionSchema = NonEmpty;
685
- function opSchemas(targetSchema) {
686
- return [
687
- v__namespace.strictObject({
688
- type: v__namespace.literal("field.set"),
689
- target: targetSchema,
690
- value: ValueExprSchema
691
- }),
692
- v__namespace.strictObject({
693
- type: v__namespace.literal("field.unset"),
694
- target: targetSchema
695
- }),
696
- v__namespace.strictObject({
697
- type: v__namespace.literal("field.append"),
698
- target: targetSchema,
699
- value: ValueExprSchema
700
- }),
701
- v__namespace.strictObject({
702
- type: v__namespace.literal("field.updateWhere"),
703
- target: targetSchema,
704
- where: ConditionSchema,
705
- value: ValueExprSchema
706
- }),
707
- v__namespace.strictObject({
708
- type: v__namespace.literal("field.removeWhere"),
709
- target: targetSchema,
710
- where: ConditionSchema
711
- })
712
- ];
713
- }
714
- const StoredFieldOpSchema = v__namespace.variant("type", [...opSchemas(StoredFieldRefSchema)]), StoredOpSchema = v__namespace.variant("type", [
715
- ...opSchemas(StoredFieldRefSchema),
716
- v__namespace.strictObject({
717
- type: v__namespace.literal("status.set"),
718
- activity: NonEmpty,
719
- status: picklist(ACTIVITY_STATUSES)
720
- })
721
- ]), StoredTransitionOpSchema = StoredFieldOpSchema, AuditOpSchema = v__namespace.strictObject({
722
- type: v__namespace.literal("audit"),
723
- target: AuthoringFieldRefSchema,
724
- value: ValueExprSchema,
725
- stampFields: v__namespace.optional(
726
- v__namespace.strictObject({
727
- actor: v__namespace.optional(NonEmpty),
728
- at: v__namespace.optional(NonEmpty)
729
- })
730
- )
731
- }), AuthoringOpSchema = v__namespace.variant("type", [
732
- ...opSchemas(AuthoringFieldRefSchema),
733
- v__namespace.strictObject({
734
- type: v__namespace.literal("status.set"),
735
- activity: v__namespace.optional(NonEmpty),
736
- status: picklist(ACTIVITY_STATUSES)
737
- }),
738
- AuditOpSchema
739
- ]), FIELD_VALUE_KINDS = [
740
- "doc.ref",
741
- "doc.refs",
742
- // Release reference. A workflow declares this entry to say "I target a
743
- // Content Release"; the runtime fills it at start time and auto-derives
744
- // the instance's read perspective from it.
745
- "release.ref",
746
- "string",
747
- "text",
748
- "number",
749
- "boolean",
750
- "date",
751
- "datetime",
752
- "url",
753
- "actor",
754
- // A single {@link Assignee} — the singular of `assignees`.
755
- "assignee",
756
- // The WHO-FOR entry: the inbox reverse-query reads it by kind, and the
757
- // rendered `$assigned` gate matches the caller against it.
758
- "assignees",
759
- // Compositional kinds — mirror Sanity's `object.fields` / `array.of`.
760
- "object",
761
- "array"
762
- ], FieldValueKindSchema = picklist(FIELD_VALUE_KINDS), FieldKindSchema = picklist(FIELD_VALUE_KINDS), FieldEntryName = groqIdentifier("`$fields.<name>`");
763
- function asShape(input) {
764
- return typeof input == "object" && input !== null ? input : {};
765
- }
766
- function compositeShapeOk(input) {
767
- const shape = asShape(input);
768
- 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;
769
- }
770
- function compositeShapeMessage(input) {
771
- const shape = asShape(input);
772
- 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)}"`;
773
- }
774
- function duplicateSubfieldName(input) {
775
- const shape = asShape(input);
776
- let list = [];
777
- Array.isArray(shape.fields) ? list = shape.fields : Array.isArray(shape.of) && (list = shape.of);
778
- const seen = /* @__PURE__ */ new Set();
779
- for (const item of list) {
780
- const name = asShape(item).name;
781
- if (typeof name == "string") {
782
- if (seen.has(name)) return name;
783
- seen.add(name);
784
- }
785
- }
786
- }
787
- function compositeChecked(entries) {
788
- return v__namespace.pipe(
789
- v__namespace.strictObject(entries),
790
- v__namespace.check(
791
- (input) => compositeShapeOk(input),
792
- (issue) => compositeShapeMessage(issue.input)
793
- ),
794
- v__namespace.check(
795
- (input) => duplicateSubfieldName(input) === void 0,
796
- (issue) => `duplicate sub-field name "${duplicateSubfieldName(issue.input)}" \u2014 sub-field names must be unique within \`fields\` / \`of\``
797
- )
798
- );
799
- }
800
- const FieldShapeSchema = v__namespace.lazy(
801
- () => compositeChecked({
802
- type: FieldValueKindSchema,
803
- name: FieldEntryName,
804
- title: v__namespace.optional(v__namespace.string()),
805
- description: v__namespace.optional(v__namespace.string()),
806
- fields: v__namespace.optional(v__namespace.array(FieldShapeSchema)),
807
- of: v__namespace.optional(v__namespace.array(FieldShapeSchema))
808
- })
809
- ), StoredEditableSchema = v__namespace.union([v__namespace.literal(!0), NonEmpty]), AuthoringEditableSchema = v__namespace.union([v__namespace.literal(!0), v__namespace.array(NonEmpty), NonEmpty]);
810
- function fieldBase(editable) {
811
- return {
812
- name: FieldEntryName,
813
- title: v__namespace.optional(v__namespace.string()),
814
- description: v__namespace.optional(v__namespace.string()),
815
- /**
816
- * When true, the caller MUST supply this entry at start (via
817
- * `initialFields`) or spawn (via the parent's `subworkflows.with`). A
818
- * missing required entry throws rather than silently defaulting to
819
- * `null`/`[]` — the same fail-fast an action's `required` param gets.
820
- * Valid only on a workflow-scope `input`-sourced entry (deploy invariant).
821
- */
822
- required: v__namespace.optional(v__namespace.boolean()),
823
- /**
824
- * How this field SEEDS its value at materialisation — see
825
- * {@link FieldSourceSchema}. Optional: an absent `initialValue` is working
826
- * memory (the field starts empty and an op fills it later), the common case.
827
- */
828
- initialValue: v__namespace.optional(FieldSourceSchema),
829
- /** Who-may-edit this field via the edit seam — see {@link StoredEditableSchema}. */
830
- editable: v__namespace.optional(editable)
831
- };
832
- }
833
- function fieldEntryFields(editable) {
834
- return {
835
- type: FieldKindSchema,
836
- ...fieldBase(editable),
837
- /** Sub-field shapes for an `object` kind (see {@link compositeShapeOk}). */
838
- fields: v__namespace.optional(v__namespace.array(FieldShapeSchema)),
839
- /** Item sub-field shapes for an `array` kind (see {@link compositeShapeOk}). */
840
- of: v__namespace.optional(v__namespace.array(FieldShapeSchema))
841
- };
842
- }
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
- );
873
- function listSugarFields(type) {
874
- return {
875
- type: v__namespace.literal(type),
876
- ...fieldBase(AuthoringEditableSchema)
877
- };
878
- }
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({
882
- name: NonEmpty,
883
- title: v__namespace.optional(v__namespace.string()),
884
- description: v__namespace.optional(v__namespace.string()),
885
- /** GROQ reads over the rendered scope, resolved to concrete JSON at queue time. */
886
- bindings: v__namespace.optional(v__namespace.record(v__namespace.string(), ConditionSchema)),
887
- /** Static config, passed through to the handler verbatim. */
888
- input: v__namespace.optional(v__namespace.record(v__namespace.string(), v__namespace.unknown()))
889
- }), ActionParamSchema = v__namespace.strictObject({
890
- type: picklist([
891
- "string",
892
- "number",
893
- "boolean",
894
- "url",
895
- "dateTime",
896
- "actor",
897
- "doc.ref",
898
- "doc.refs",
899
- "json"
900
- ]),
901
- name: NonEmpty,
902
- title: v__namespace.optional(v__namespace.string()),
903
- description: v__namespace.optional(v__namespace.string()),
904
- required: v__namespace.optional(v__namespace.boolean())
905
- });
906
- function actionFields(op) {
907
- return {
908
- name: NonEmpty,
909
- title: v__namespace.optional(v__namespace.string()),
910
- description: v__namespace.optional(v__namespace.string()),
911
- /**
912
- * The one gate mechanism: a condition over the rendered scope. Hard
913
- * enforcement lives outside the engine; every engine-evaluated gate is
914
- * authoring/UX.
915
- */
916
- filter: v__namespace.optional(ConditionSchema),
917
- params: v__namespace.optional(v__namespace.array(ActionParamSchema)),
918
- ops: v__namespace.optional(v__namespace.array(op)),
919
- effects: v__namespace.optional(v__namespace.array(EffectSchema))
920
- };
921
- }
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({
943
- name: NonEmpty,
944
- version: v__namespace.optional(v__namespace.union([PositiveInt, v__namespace.literal("latest")]))
945
- }), SubworkflowsSchema = v__namespace.strictObject({
946
- /** GROQ producing one row per subworkflow; each row binds as `$row`. */
947
- forEach: NonEmpty,
948
- definition: DefinitionRefSchema,
949
- /** Initial fields for each subworkflow — entry name → GROQ over `$row` + the parent scope. */
950
- with: v__namespace.optional(v__namespace.record(NonEmpty, ConditionSchema)),
951
- /**
952
- * Extra values evaluated in the parent's rendered scope at spawn time and
953
- * delivered into each subworkflow's `$effects` bag — the parent→child
954
- * handoff, read exactly like an effect output.
955
- */
956
- context: v__namespace.optional(v__namespace.record(NonEmpty, ConditionSchema))
957
- });
958
- function activityFields({ field, action, op, target }) {
959
- return {
960
- name: NonEmpty,
961
- title: v__namespace.optional(v__namespace.string()),
962
- description: v__namespace.optional(v__namespace.string()),
963
- /**
964
- * Advisory BPMN-aligned classification of this activity by its executor (see
965
- * {@link ACTIVITY_KINDS}). Optional and derived from the activity's shape when
966
- * omitted; declaring it lets deploy check the shape matches the lane.
967
- * Changes no gating or resolution — a label for tooling, like `assignees`.
968
- */
969
- kind: v__namespace.optional(picklist(ACTIVITY_KINDS)),
970
- /** Deep-link target for a `kind: "manual"` activity (off-system work). Render-only
971
- * metadata; only valid on a manual activity (deploy-checked). */
972
- target: v__namespace.optional(target),
973
- activation: v__namespace.optional(picklist(["auto", "manual"])),
974
- filter: v__namespace.optional(ConditionSchema),
975
- /**
976
- * Readiness gates, by name — conditions over the rendered scope that must
977
- * hold for the activity to be *executable*, orthogonal to `filter`
978
- * (visibility). An unmet requirement keeps the activity visible but disables
979
- * its actions with a `requirements-unmet` verdict naming the unmet keys;
980
- * all must hold (any-of lives inside one condition). Advisory like every
981
- * engine gate. Distinct from ACL (authorization) and guards
982
- * (content-write locks).
983
- */
984
- requirements: v__namespace.optional(v__namespace.record(NonEmpty, ConditionSchema)),
985
- /**
986
- * Auto-completion condition — evaluated at activation and on every
987
- * cascade; truthy flips the activity to `done` with a system actor. On a
988
- * spawning activity it typically reads `$subworkflows`.
989
- */
990
- completeWhen: v__namespace.optional(ConditionSchema),
991
- /**
992
- * Auto-failure condition — symmetric to `completeWhen`, flips to
993
- * `failed`. When both are truthy on the same evaluation, `failWhen`
994
- * wins — failure is the more notable signal.
995
- */
996
- failWhen: v__namespace.optional(ConditionSchema),
997
- ops: v__namespace.optional(v__namespace.array(op)),
998
- effects: v__namespace.optional(v__namespace.array(EffectSchema)),
999
- actions: v__namespace.optional(v__namespace.array(action)),
1000
- subworkflows: v__namespace.optional(SubworkflowsSchema),
1001
- /** Activity-scoped field entries. Resolved at activity activation time. */
1002
- fields: v__namespace.optional(v__namespace.array(field))
1003
- };
1004
- }
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
- })
1022
- )
1023
- );
1024
- function transitionFields(op, filter) {
1025
- return {
1026
- name: NonEmpty,
1027
- title: v__namespace.optional(v__namespace.string()),
1028
- description: v__namespace.optional(v__namespace.string()),
1029
- to: NonEmpty,
1030
- filter,
1031
- /** The `field.*` subset — field-write-on-move, the stage's EXIT/ARRIVAL payload. */
1032
- ops: v__namespace.optional(v__namespace.array(op)),
1033
- effects: v__namespace.optional(v__namespace.array(EffectSchema))
1034
- };
1035
- }
1036
- const StoredTransitionSchema = pinned()(
1037
- v__namespace.strictObject(transitionFields(StoredTransitionOpSchema, ConditionSchema))
1038
- ), AuthoringTransitionOpSchema = v__namespace.variant("type", [
1039
- ...opSchemas(AuthoringFieldRefSchema),
1040
- AuditOpSchema
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"
1048
- )
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
- }) {
1114
- return {
1115
- name: NonEmpty,
1116
- title: v__namespace.optional(v__namespace.string()),
1117
- description: v__namespace.optional(v__namespace.string()),
1118
- activities: v__namespace.optional(v__namespace.array(activity)),
1119
- transitions: v__namespace.optional(v__namespace.array(transition)),
1120
- /**
1121
- * Lake mutation guards active while this stage holds. Each compiles to a
1122
- * persisted guard document deployed on stage entry and retracted on exit.
1123
- */
1124
- guards: v__namespace.optional(v__namespace.array(guard)),
1125
- /** Stage-scoped field entries. Resolved at stage entry. */
1126
- fields: v__namespace.optional(v__namespace.array(field)),
1127
- /**
1128
- * Tighten-only editability overrides for the time this stage holds, keyed
1129
- * by an in-scope field name. The field's own `editable` is the ceiling; a
1130
- * stage value is ANDed with it at runtime, so an override can only NARROW
1131
- * (never open a field the baseline left closed). An unlisted field inherits
1132
- * its baseline.
1133
- */
1134
- editable: v__namespace.optional(v__namespace.record(FieldEntryName, editable))
1135
- };
1136
- }
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
- })
1156
- )
1157
- ), RoleAliasesSchema = v__namespace.record(
1158
- NonEmpty,
1159
- v__namespace.pipe(v__namespace.array(NonEmpty), v__namespace.minLength(1, "a role alias must list at least one fulfilling role"))
1160
- ), WORKFLOW_LIFECYCLES = ["standalone", "child"];
1161
- function workflowFields(field, stage) {
1162
- return {
1163
- name: NonEmpty,
1164
- title: NonEmpty,
1165
- description: v__namespace.optional(v__namespace.string()),
1166
- /**
1167
- * How instances of this workflow come to exist. `'child'` marks a
1168
- * spawn-only definition — instantiated by a parent via `activity.subworkflows`,
1169
- * never started cold from a picker. Omitted ⇒ `'standalone'` (startable).
1170
- * Advisory: consumers filter their start pickers on it (see
1171
- * {@link isStartableDefinition}); the engine does NOT refuse a
1172
- * `startInstance` on a `'child'` def — load-bearing `required` field is the
1173
- * runtime backstop.
1174
- */
1175
- lifecycle: v__namespace.optional(picklist(WORKFLOW_LIFECYCLES)),
1176
- /** Reference field: named for the target, holds the stage's `name`. */
1177
- initialStage: NonEmpty,
1178
- /** Workflow-scope field entries. Persist for the instance lifetime. */
1179
- fields: v__namespace.optional(v__namespace.array(field)),
1180
- stages: v__namespace.pipe(v__namespace.array(stage), v__namespace.minLength(1, "must declare at least one stage")),
1181
- /**
1182
- * Nullary named conditions — each `name: groq` entry is pre-evaluated
1183
- * and bound as the boolean `$name` var, composable with native GROQ.
1184
- * Redefining a built-in var is a deploy error (never silently shadow).
1185
- */
1186
- predicates: v__namespace.optional(v__namespace.record(groqIdentifier("`$<name>`"), ConditionSchema)),
1187
- /** Role aliasing for this definition — see {@link RoleAliasesSchema}. */
1188
- roleAliases: v__namespace.optional(RoleAliasesSchema)
1189
- };
1190
- }
1191
- pinned()(
1192
- v__namespace.strictObject(workflowFields(FieldEntrySchema, StoredStageSchema))
1193
- );
1194
- const AuthoringWorkflowSchema = pinned()(
1195
- v__namespace.strictObject(workflowFields(AuthoringFieldEntrySchema, AuthoringStageSchema))
1196
- ), WORKFLOW_DEFINITION_TYPE = "sanity.workflow.definition";
1197
- function isStartableDefinition(definition) {
1198
- return definition.lifecycle !== "child";
1199
- }
1200
- function formatValidationError(label, issues) {
1201
- const lines = issues.map((issue) => ` - ${issue.path.length === 0 ? "(root)" : formatPath(issue.path)}: ${issue.message}`);
1202
- return `${label} failed validation (${issues.length} issue${issues.length === 1 ? "" : "s"}):
1203
- ${lines.join(`
1204
- `)}`;
1205
- }
1206
- function issuesFromValibot(issues) {
1207
- return issues.map((issue) => ({
1208
- path: issue.path ? issue.path.map((item) => item.key) : [],
1209
- message: issue.message
1210
- }));
1211
- }
1212
- function formatPath(path) {
1213
- let out = "";
1214
- for (const seg of path)
1215
- typeof seg == "number" ? out += `[${seg}]` : out += out.length === 0 ? String(seg) : `.${String(seg)}`;
1216
- return out;
1217
- }
1218
- exports.ACTIVITY_KINDS = ACTIVITY_KINDS;
1219
- exports.ACTOR_KINDS = ACTOR_KINDS;
1220
- exports.AuthoringActionSchema = AuthoringActionSchema;
1221
- exports.AuthoringActivitySchema = AuthoringActivitySchema;
1222
- exports.AuthoringFieldEntrySchema = AuthoringFieldEntrySchema;
1223
- exports.AuthoringGuardSchema = AuthoringGuardSchema;
1224
- exports.AuthoringOpSchema = AuthoringOpSchema;
1225
- exports.AuthoringStageSchema = AuthoringStageSchema;
1226
- exports.AuthoringTransitionSchema = AuthoringTransitionSchema;
1227
- exports.AuthoringWorkflowSchema = AuthoringWorkflowSchema;
1228
- exports.CONDITION_VARS = CONDITION_VARS;
1229
- exports.ContractViolationError = ContractViolationError;
1230
- exports.DOCUMENT_VALUE_PERMISSIONS = DOCUMENT_VALUE_PERMISSIONS;
1231
- exports.DRIVER_KINDS = DRIVER_KINDS;
1232
- exports.DefinitionInUseError = DefinitionInUseError;
1233
- exports.DefinitionNotFoundError = DefinitionNotFoundError;
1234
- exports.EFFECTS_READ = EFFECTS_READ;
1235
- exports.EffectNotFoundError = EffectNotFoundError;
1236
- exports.EffectSchema = EffectSchema;
1237
- exports.FIELD_READ = FIELD_READ;
1238
- exports.FILTER_SCOPE_VARS = FILTER_SCOPE_VARS;
1239
- exports.FieldValueShapeError = FieldValueShapeError;
1240
- exports.GROQ_IDENTIFIER = GROQ_IDENTIFIER;
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;
1246
- exports.WORKFLOW_DEFINITION_TYPE = WORKFLOW_DEFINITION_TYPE;
1247
- exports.WorkflowConfigSchema = WorkflowConfigSchema;
1248
- exports.WorkflowError = WorkflowError;
1249
- exports.actorFulfillsRole = actorFulfillsRole;
1250
- exports.andConditions = andConditions;
1251
- exports.checkFieldValue = checkFieldValue;
1252
- exports.datasetResourceParts = datasetResourceParts;
1253
- exports.definitionDocId = definitionDocId;
1254
- exports.expandRequiredRoles = expandRequiredRoles;
1255
- exports.extractDocumentId = extractDocumentId;
1256
- exports.formatIssues = formatIssues;
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;
1266
- exports.isStartableDefinition = isStartableDefinition;
1267
- exports.isTerminalActivityStatus = isTerminalActivityStatus;
1268
- exports.issuesFromValibot = issuesFromValibot;
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;
1289
- //# sourceMappingURL=schema.cjs.map