@sanity/workflow-engine 0.3.0 → 0.4.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,195 +16,170 @@ function _interopNamespaceCompat(e) {
16
16
  }), n.default = e, Object.freeze(n);
17
17
  }
18
18
  var v__namespace = /* @__PURE__ */ _interopNamespaceCompat(v);
19
- const TASK_STATUSES = ["pending", "active", "done", "skipped", "failed"], STATE_SCOPES = ["workflow", "stage", "task"], DOCUMENT_VALUE_PERMISSIONS = ["create", "read", "update"], MUTATION_GUARD_ACTIONS = [
19
+ const TASK_STATUSES = ["pending", "active", "done", "skipped", "failed"], TERMINAL_TASK_STATUSES = ["done", "skipped", "failed"];
20
+ function isTerminalTaskStatus(status) {
21
+ return TERMINAL_TASK_STATUSES.includes(status);
22
+ }
23
+ const STATE_SCOPES = ["workflow", "stage", "task"], DOCUMENT_VALUE_PERMISSIONS = ["create", "read", "update"], MUTATION_GUARD_ACTIONS = [
20
24
  "create",
21
25
  "update",
22
26
  "delete",
23
27
  "publish",
24
28
  "unpublish"
25
- ], 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));
29
+ ], 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_]*$/;
30
+ function groqIdentifier(referencedAs) {
31
+ return v__namespace.pipe(
32
+ v__namespace.string(),
33
+ v__namespace.regex(
34
+ GROQ_IDENTIFIER,
35
+ `must be a GROQ-safe identifier (letters, digits, underscore; not starting with a digit) because it is referenced as ${referencedAs} in GROQ conditions`
36
+ )
37
+ );
38
+ }
26
39
  function picklist(options) {
27
40
  return v__namespace.picklist(
28
41
  options,
29
42
  `Invalid option: expected one of ${options.map((o) => `"${o}"`).join("|")}`
30
43
  );
31
44
  }
32
- const GdrSchema = v__namespace.strictObject({
33
- id: v__namespace.pipe(
34
- v__namespace.string(),
35
- v__namespace.regex(
36
- /^(dataset|canvas|media-library|dashboard):/,
37
- "must be a GDR URI of form '<scheme>:<...id-parts>' where scheme is one of: dataset, canvas, media-library, dashboard"
38
- )
39
- ),
40
- type: NonEmpty
41
- }), SourceSchema = v__namespace.lazy(
45
+ const SourceSchema = v__namespace.lazy(
42
46
  () => v__namespace.union([
47
+ // State-entry origins: who fills the entry, and when.
48
+ v__namespace.strictObject({ type: v__namespace.literal("init") }),
49
+ v__namespace.strictObject({ type: v__namespace.literal("write") }),
43
50
  v__namespace.strictObject({
44
- source: v__namespace.literal("literal"),
45
- value: v__namespace.union([v__namespace.string(), v__namespace.number(), v__namespace.boolean(), v__namespace.null()])
46
- }),
47
- v__namespace.strictObject({ source: v__namespace.literal("param"), paramId: NonEmpty }),
48
- v__namespace.strictObject({ source: v__namespace.literal("actor") }),
49
- v__namespace.strictObject({ source: v__namespace.literal("now") }),
50
- v__namespace.strictObject({ source: v__namespace.literal("self") }),
51
- v__namespace.strictObject({ source: v__namespace.literal("stageId") }),
52
- v__namespace.strictObject({
53
- source: v__namespace.literal("stateRead"),
54
- scope: picklist(STATE_SCOPES),
55
- slotId: NonEmpty,
56
- path: v__namespace.optional(v__namespace.string())
51
+ type: v__namespace.literal("query"),
52
+ query: NonEmpty
57
53
  }),
58
- // effectsContext lookup reads from `instance.effectsContext[id === ID].value`.
59
- // Used by chained effects where one handler's output feeds another's binding.
54
+ // Value sources: resolved to concrete JSON when an op or seed applies.
55
+ v__namespace.strictObject({ type: v__namespace.literal("literal"), value: v__namespace.unknown() }),
56
+ v__namespace.strictObject({ type: v__namespace.literal("param"), param: NonEmpty }),
57
+ v__namespace.strictObject({ type: v__namespace.literal("actor") }),
58
+ v__namespace.strictObject({ type: v__namespace.literal("now") }),
59
+ v__namespace.strictObject({ type: v__namespace.literal("self") }),
60
+ v__namespace.strictObject({ type: v__namespace.literal("stage") }),
60
61
  v__namespace.strictObject({
61
- source: v__namespace.literal("effectOutput"),
62
- contextId: NonEmpty,
62
+ type: v__namespace.literal("stateRead"),
63
+ scope: v__namespace.optional(v__namespace.union([v__namespace.literal("workflow"), v__namespace.literal("stage")])),
64
+ state: NonEmpty,
63
65
  path: v__namespace.optional(v__namespace.string())
64
66
  }),
65
- v__namespace.strictObject({ source: v__namespace.literal("row"), path: v__namespace.optional(v__namespace.string()) }),
66
67
  v__namespace.strictObject({
67
- source: v__namespace.literal("parentState"),
68
- slotId: NonEmpty,
69
- path: v__namespace.optional(v__namespace.string())
70
- }),
71
- // Compound object source — resolves each field's Source recursively
72
- // and packages the result into a JSON object. Lets ops append rich
73
- // rows to `checklist` / `notes` / `assignees` slots, e.g.
74
- // `{ source: "object", fields: { signer: { source: "actor" },
75
- // at: { source: "now" } } }`.
76
- v__namespace.strictObject({
77
- source: v__namespace.literal("object"),
68
+ type: v__namespace.literal("object"),
78
69
  fields: v__namespace.record(NonEmpty, SourceSchema)
79
70
  })
80
71
  ])
81
- ), ScopeKeySchema = v__namespace.strictObject({
72
+ ), StoredStateRefSchema = v__namespace.strictObject({
82
73
  scope: picklist(STATE_SCOPES),
83
- slotId: NonEmpty
74
+ state: NonEmpty
75
+ }), AuthoringStateRefSchema = v__namespace.strictObject({
76
+ scope: v__namespace.optional(picklist(STATE_SCOPES)),
77
+ state: NonEmpty
84
78
  }), OpPredicateSchema = v__namespace.lazy(
85
79
  () => v__namespace.union([
86
80
  v__namespace.strictObject({
81
+ type: v__namespace.literal("field"),
87
82
  field: NonEmpty,
88
83
  equals: SourceSchema
89
84
  }),
90
- v__namespace.strictObject({ all: v__namespace.array(OpPredicateSchema) }),
91
- v__namespace.strictObject({ any: v__namespace.array(OpPredicateSchema) })
85
+ v__namespace.strictObject({ type: v__namespace.literal("all"), of: v__namespace.array(OpPredicateSchema) }),
86
+ v__namespace.strictObject({ type: v__namespace.literal("any"), of: v__namespace.array(OpPredicateSchema) })
92
87
  ])
93
- ), OpSchema = v__namespace.variant("type", [
94
- v__namespace.strictObject({
95
- type: v__namespace.literal("workflow.op.state.set"),
96
- target: ScopeKeySchema,
97
- value: SourceSchema
98
- }),
99
- v__namespace.strictObject({
100
- type: v__namespace.literal("workflow.op.state.append"),
101
- target: ScopeKeySchema,
102
- item: SourceSchema
103
- }),
104
- v__namespace.strictObject({
105
- type: v__namespace.literal("workflow.op.state.updateWhere"),
106
- target: ScopeKeySchema,
107
- where: OpPredicateSchema,
108
- merge: v__namespace.record(NonEmpty, SourceSchema)
109
- }),
110
- v__namespace.strictObject({
111
- type: v__namespace.literal("workflow.op.state.removeWhere"),
112
- target: ScopeKeySchema,
113
- where: OpPredicateSchema
114
- }),
88
+ );
89
+ function opSchemas(targetSchema) {
90
+ return [
91
+ v__namespace.strictObject({
92
+ type: v__namespace.literal("state.set"),
93
+ target: targetSchema,
94
+ value: SourceSchema
95
+ }),
96
+ v__namespace.strictObject({
97
+ type: v__namespace.literal("state.unset"),
98
+ target: targetSchema
99
+ }),
100
+ v__namespace.strictObject({
101
+ type: v__namespace.literal("state.append"),
102
+ target: targetSchema,
103
+ value: SourceSchema
104
+ }),
105
+ v__namespace.strictObject({
106
+ type: v__namespace.literal("state.updateWhere"),
107
+ target: targetSchema,
108
+ where: OpPredicateSchema,
109
+ value: SourceSchema
110
+ }),
111
+ v__namespace.strictObject({
112
+ type: v__namespace.literal("state.removeWhere"),
113
+ target: targetSchema,
114
+ where: OpPredicateSchema
115
+ })
116
+ ];
117
+ }
118
+ const StoredOpSchema = v__namespace.variant("type", [
119
+ ...opSchemas(StoredStateRefSchema),
115
120
  v__namespace.strictObject({
116
- type: v__namespace.literal("workflow.op.status.set"),
117
- taskId: NonEmpty,
121
+ type: v__namespace.literal("status.set"),
122
+ task: NonEmpty,
118
123
  status: picklist(TASK_STATUSES)
119
124
  })
120
- ]), StateSlotSourceSchema = v__namespace.union([
121
- v__namespace.strictObject({ kind: v__namespace.literal("init") }),
125
+ ]), StoredTransitionOpSchema = v__namespace.variant("type", [...opSchemas(StoredStateRefSchema)]), AuditOpSchema = v__namespace.strictObject({
126
+ type: v__namespace.literal("audit"),
127
+ target: AuthoringStateRefSchema,
128
+ value: SourceSchema,
129
+ stampFields: v__namespace.optional(
130
+ v__namespace.strictObject({
131
+ actor: v__namespace.optional(NonEmpty),
132
+ at: v__namespace.optional(NonEmpty)
133
+ })
134
+ )
135
+ }), AuthoringOpSchema = v__namespace.variant("type", [
136
+ ...opSchemas(AuthoringStateRefSchema),
122
137
  v__namespace.strictObject({
123
- kind: v__namespace.literal("query"),
124
- query: NonEmpty,
125
- resource: v__namespace.optional(v__namespace.union([v__namespace.literal("workflow"), v__namespace.literal("subject"), GdrSchema]))
138
+ type: v__namespace.literal("status.set"),
139
+ task: v__namespace.optional(NonEmpty),
140
+ status: picklist(TASK_STATUSES)
126
141
  }),
127
- v__namespace.strictObject({ kind: v__namespace.literal("write") }),
128
- /**
129
- * Pre-populate the slot at resolution time with a constant value.
130
- * Useful for stage- or task-scope slots that should start non-empty
131
- * (e.g. a default headline, a starting tally).
132
- */
133
- v__namespace.strictObject({ kind: v__namespace.literal("literal"), value: v__namespace.unknown() }),
134
- /**
135
- * Pre-populate by reading another already-resolved slot. `scope:
136
- * "workflow"` reads from the workflow-scope `state[]` (resolved at
137
- * startInstance). `scope: "stage"` reads from an earlier slot in
138
- * this same stage entry's `state[]` (sequential resolution).
139
- * Optional `path` selects a nested field (e.g. `path: "id"` on a
140
- * doc.ref slot).
141
- */
142
- v__namespace.strictObject({
143
- kind: v__namespace.literal("stateRead"),
144
- scope: v__namespace.union([v__namespace.literal("workflow"), v__namespace.literal("stage")]),
145
- slotId: NonEmpty,
146
- path: v__namespace.optional(v__namespace.string())
147
- })
148
- ]), StateSlotKindSchema = picklist([
149
- "workflow.state.doc.ref",
150
- "workflow.state.doc.refs",
151
- // Release reference. A workflow declares this slot to say "I target a
152
- // Content Release"; the runtime fills it with a specific release at
153
- // start time, and the engine auto-derives the instance's read
154
- // perspective from it.
155
- "workflow.state.release.ref",
156
- "workflow.state.query",
157
- "workflow.state.value.string",
158
- "workflow.state.value.url",
159
- "workflow.state.value.number",
160
- "workflow.state.value.boolean",
161
- "workflow.state.value.dateTime",
162
- "workflow.state.value.actor",
163
- "workflow.state.checklist",
164
- "workflow.state.notes",
165
- "workflow.state.assignees"
166
- ]), StateSlotSchema = v__namespace.strictObject({
167
- type: StateSlotKindSchema,
168
- id: NonEmpty,
169
- title: v__namespace.optional(v__namespace.string()),
170
- description: v__namespace.optional(v__namespace.string()),
171
- source: StateSlotSourceSchema
172
- }), FilterSchema = v__namespace.union([
173
- // Inline GROQ — only reserved params allowed at runtime.
174
- NonEmpty,
175
- // Reference to a named predicate, optionally with named args.
176
- v__namespace.strictObject({
177
- ref: NonEmpty,
178
- args: v__namespace.optional(v__namespace.record(v__namespace.string(), v__namespace.unknown()))
179
- })
180
- ]), PredicateParamSchema = v__namespace.strictObject({
181
- id: NonEmpty,
142
+ AuditOpSchema
143
+ ]), StateKindSchema = picklist([
144
+ "doc.ref",
145
+ "doc.refs",
146
+ // Release reference. A workflow declares this entry to say "I target a
147
+ // Content Release"; the runtime fills it at start time and auto-derives
148
+ // the instance's read perspective from it.
149
+ "release.ref",
150
+ "query",
151
+ "value.string",
152
+ "value.url",
153
+ "value.number",
154
+ "value.boolean",
155
+ "value.dateTime",
156
+ "value.actor",
157
+ "checklist",
158
+ "notes",
159
+ // The WHO-FOR entry: the inbox reverse-query reads it by kind, and the
160
+ // rendered `$assigned` gate matches the caller against it.
161
+ "assignees"
162
+ ]), StateEntryName = groqIdentifier("`$state.<name>`"), StateEntrySchema = v__namespace.strictObject({
163
+ type: StateKindSchema,
164
+ name: StateEntryName,
182
165
  title: v__namespace.optional(v__namespace.string()),
183
166
  description: v__namespace.optional(v__namespace.string()),
184
- type: picklist(["string", "number", "boolean", "string[]", "reference"]),
185
- enum: v__namespace.optional(v__namespace.array(v__namespace.string()))
186
- }), PredicateSchema = v__namespace.strictObject({
187
- type: v__namespace.optional(v__namespace.literal("workflow.predicate")),
188
- id: NonEmpty,
167
+ source: SourceSchema
168
+ }), ClaimStateSchema = v__namespace.strictObject({
169
+ type: v__namespace.literal("claim"),
170
+ name: StateEntryName,
189
171
  title: v__namespace.optional(v__namespace.string()),
190
- description: v__namespace.optional(v__namespace.string()),
191
- groq: NonEmpty,
192
- params: v__namespace.optional(v__namespace.array(PredicateParamSchema))
193
- }), EffectSchema = v__namespace.strictObject({
194
- type: v__namespace.optional(v__namespace.literal("workflow.effect")),
195
- id: NonEmpty,
172
+ description: v__namespace.optional(v__namespace.string())
173
+ }), AuthoringStateEntrySchema = v__namespace.union([StateEntrySchema, ClaimStateSchema]), ConditionSchema = NonEmpty, EffectSchema = v__namespace.strictObject({
174
+ name: NonEmpty,
196
175
  title: v__namespace.optional(v__namespace.string()),
197
176
  description: v__namespace.optional(v__namespace.string()),
198
- // Bindings resolve typed Source values at commit time, producing
199
- // concrete JSON in the queued effect's `params`. NO GROQ.
200
- bindings: v__namespace.optional(v__namespace.record(v__namespace.string(), SourceSchema)),
177
+ /** GROQ reads over the rendered scope, resolved to concrete JSON at queue time. */
178
+ bindings: v__namespace.optional(v__namespace.record(v__namespace.string(), ConditionSchema)),
179
+ /** Static config, passed through to the handler verbatim. */
201
180
  input: v__namespace.optional(v__namespace.record(v__namespace.string(), v__namespace.unknown()))
202
- }), TerminalTaskStatus = picklist(["done", "skipped", "failed"]), PermissionSchema = picklist(DOCUMENT_VALUE_PERMISSIONS), ActionParamSchema = v__namespace.strictObject({
203
- type: v__namespace.optional(v__namespace.literal("workflow.action.param")),
204
- id: NonEmpty,
205
- title: v__namespace.optional(v__namespace.string()),
206
- description: v__namespace.optional(v__namespace.string()),
207
- paramType: picklist([
181
+ }), ActionParamSchema = v__namespace.strictObject({
182
+ type: picklist([
208
183
  "string",
209
184
  "number",
210
185
  "boolean",
@@ -215,130 +190,113 @@ const GdrSchema = v__namespace.strictObject({
215
190
  "doc.refs",
216
191
  "json"
217
192
  ]),
218
- required: v__namespace.optional(v__namespace.boolean())
219
- }), ActionSchema = v__namespace.strictObject({
220
- type: v__namespace.optional(v__namespace.literal("workflow.action")),
221
- id: NonEmpty,
193
+ name: NonEmpty,
222
194
  title: v__namespace.optional(v__namespace.string()),
223
195
  description: v__namespace.optional(v__namespace.string()),
224
- filter: v__namespace.optional(FilterSchema),
225
- /** Caller-supplied params, validated before ops/effects run. */
226
- params: v__namespace.optional(v__namespace.array(ActionParamSchema)),
227
- /**
228
- * Status the task flips to when this action fires. Omit to leave
229
- * the task status unchanged — used by intermediate `claim` /
230
- * `assign` / partial-update actions where the task should stay
231
- * active so a later action can fire on it. Declared values must
232
- * be terminal (`done` / `skipped` / `failed`); the engine never
233
- * mid-flip back to `active` outside of the `workflow.op.status.set`
234
- * op explicit path.
235
- */
236
- setStatus: v__namespace.optional(TerminalTaskStatus),
237
- /**
238
- * Inline state-mutation primitives executed during the action commit.
239
- * Each op runs against the in-memory mutation (deterministic, no
240
- * external I/O). Resolved Sources turn into concrete JSON before the
241
- * op applies. Logged on `history[]` as `workflow.history.opApplied`
242
- * and surfaced on the `ActionResult.ranOps`.
243
- */
244
- ops: v__namespace.optional(v__namespace.array(OpSchema)),
245
- effects: v__namespace.optional(v__namespace.array(EffectSchema)),
246
- /**
247
- * Role gating — OR-match against `Actor.roles[]`. If omitted, no role
248
- * gate. `"*"` in Actor.roles always matches (bench / all-access).
249
- */
196
+ required: v__namespace.optional(v__namespace.boolean())
197
+ });
198
+ function actionFields(op) {
199
+ return {
200
+ name: NonEmpty,
201
+ title: v__namespace.optional(v__namespace.string()),
202
+ description: v__namespace.optional(v__namespace.string()),
203
+ /**
204
+ * The one gate mechanism: a condition over the rendered scope. Hard
205
+ * enforcement lives outside the engine entirely (the lake ACL on the
206
+ * documents, and guards) — every engine-evaluated gate is authoring/UX.
207
+ */
208
+ filter: v__namespace.optional(ConditionSchema),
209
+ params: v__namespace.optional(v__namespace.array(ActionParamSchema)),
210
+ ops: v__namespace.optional(v__namespace.array(op)),
211
+ effects: v__namespace.optional(v__namespace.array(EffectSchema))
212
+ };
213
+ }
214
+ const StoredActionSchema = v__namespace.strictObject(actionFields(StoredOpSchema)), TerminalTaskStatus = picklist(TERMINAL_TASK_STATUSES), RawAuthoringActionSchema = v__namespace.strictObject({
215
+ ...actionFields(AuthoringOpSchema),
250
216
  roles: v__namespace.optional(v__namespace.array(NonEmpty)),
251
- /**
252
- * When true, the actor must appear in the task's `assignees[]` (as a
253
- * user-kind assignee matching by id) for the action to be allowed.
254
- */
255
- requireAssignment: v__namespace.optional(v__namespace.boolean()),
256
- /**
257
- * Which permission on the workflow.instance document the actor must
258
- * hold (per supplied `grants`). Defaults to "update". Only checked
259
- * when grants are supplied at evaluate/fireAction time.
260
- */
261
- requiredPermission: v__namespace.optional(PermissionSchema)
262
- }), CompletionPolicySchema = v__namespace.union([
263
- v__namespace.strictObject({ kind: v__namespace.literal("all") }),
264
- v__namespace.strictObject({ kind: v__namespace.literal("any") }),
265
- v__namespace.strictObject({
266
- kind: v__namespace.literal("count"),
267
- n: PositiveInt
268
- })
269
- ]), LogicalDefinitionRefSchema = v__namespace.strictObject({
270
- workflowId: NonEmpty,
271
- version: v__namespace.optional(v__namespace.union([PositiveInt, v__namespace.literal("latest")]))
272
- }), SpawnForEachSchema = v__namespace.strictObject({
273
- groq: NonEmpty,
274
- as: v__namespace.strictObject({
275
- initialState: v__namespace.optional(
276
- v__namespace.array(
277
- v__namespace.strictObject({
278
- type: StateSlotKindSchema,
279
- id: NonEmpty,
280
- value: SourceSchema
281
- })
282
- )
283
- ),
284
- effectsContext: v__namespace.optional(v__namespace.record(v__namespace.string(), SourceSchema))
285
- })
286
- }), SpawnSchema = v__namespace.strictObject({
287
- type: v__namespace.optional(v__namespace.literal("workflow.spawn")),
288
- id: v__namespace.optional(v__namespace.string()),
217
+ status: v__namespace.optional(TerminalTaskStatus)
218
+ }), ClaimActionSchema = v__namespace.strictObject({
219
+ type: v__namespace.literal("claim"),
220
+ name: NonEmpty,
289
221
  title: v__namespace.optional(v__namespace.string()),
290
222
  description: v__namespace.optional(v__namespace.string()),
291
- definitionRef: LogicalDefinitionRefSchema,
292
- forEach: SpawnForEachSchema,
293
- completionPolicy: v__namespace.optional(CompletionPolicySchema)
294
- }), AssigneeSchema = v__namespace.variant("kind", [
295
- v__namespace.strictObject({ kind: v__namespace.literal("user"), id: NonEmpty }),
296
- v__namespace.strictObject({ kind: v__namespace.literal("role"), role: NonEmpty })
297
- ]), TaskSchema = v__namespace.strictObject({
298
- type: v__namespace.optional(v__namespace.literal("workflow.task")),
299
- id: NonEmpty,
300
- title: v__namespace.optional(v__namespace.string()),
301
- description: v__namespace.optional(v__namespace.string()),
302
- assignees: v__namespace.optional(v__namespace.array(AssigneeSchema)),
303
- filter: v__namespace.optional(FilterSchema),
304
- invokeOn: v__namespace.optional(picklist(["stageEnter", "manual"])),
305
- /**
306
- * Auto-completion predicate — same shape as `transition.filter`. When
307
- * truthy at activation or on any subsequent cascade, the engine
308
- * flips the task to `done` with a `{ kind: "system", id: "engine.completeWhen" }`
309
- * actor. A full filter, not just a wall-clock deadline.
310
- */
311
- completeWhen: v__namespace.optional(FilterSchema),
223
+ state: v__namespace.union([NonEmpty, AuthoringStateRefSchema]),
224
+ roles: v__namespace.optional(v__namespace.array(NonEmpty)),
225
+ filter: v__namespace.optional(ConditionSchema),
226
+ params: v__namespace.optional(v__namespace.array(ActionParamSchema)),
227
+ effects: v__namespace.optional(v__namespace.array(EffectSchema))
228
+ }), AuthoringActionSchema = v__namespace.union([RawAuthoringActionSchema, ClaimActionSchema]), DefinitionRefSchema = v__namespace.strictObject({
229
+ name: NonEmpty,
230
+ version: v__namespace.optional(v__namespace.union([PositiveInt, v__namespace.literal("latest")]))
231
+ }), SubworkflowsSchema = v__namespace.strictObject({
232
+ /** GROQ producing one row per subworkflow; each row binds as `$row`. */
233
+ forEach: NonEmpty,
234
+ definition: DefinitionRefSchema,
235
+ /** Initial state for each subworkflow — entry name → GROQ over `$row` + the parent scope. */
236
+ with: v__namespace.optional(v__namespace.record(NonEmpty, ConditionSchema)),
312
237
  /**
313
- * Auto-failure predicate symmetric to `completeWhen`. Same shape,
314
- * same evaluation cadence (activation + every cascade), but flips
315
- * the task to `failed` (not `done`) with a `{ kind: "system",
316
- * id: "engine.failWhen" }` actor. When both `completeWhen` and
317
- * `failWhen` resolve truthy on the same evaluation, `failWhen`
318
- * wins — failure is the more notable signal and should not be
319
- * silently swallowed.
238
+ * Extra values evaluated in the parent's rendered scope at spawn time and
239
+ * delivered into each subworkflow's `$effects` bag the parent→child
240
+ * handoff, read exactly like an effect output.
320
241
  */
321
- failWhen: v__namespace.optional(FilterSchema),
322
- effects: v__namespace.optional(v__namespace.array(EffectSchema)),
323
- actions: v__namespace.optional(v__namespace.array(ActionSchema)),
324
- spawns: v__namespace.optional(SpawnSchema),
325
- contentRefs: v__namespace.optional(v__namespace.array(v__namespace.unknown())),
326
- /** Task-scoped state slots. Resolved at task activation time. */
327
- state: v__namespace.optional(v__namespace.array(StateSlotSchema))
328
- }), TransitionSchema = v__namespace.strictObject({
329
- type: v__namespace.optional(v__namespace.literal("workflow.transition")),
330
- id: NonEmpty,
331
- title: v__namespace.optional(v__namespace.string()),
332
- description: v__namespace.optional(v__namespace.string()),
333
- to: NonEmpty,
334
- on: v__namespace.optional(v__namespace.string()),
335
- filter: v__namespace.optional(FilterSchema),
336
- effects: v__namespace.optional(v__namespace.array(EffectSchema))
337
- }), GuardActionSchema = picklist(MUTATION_GUARD_ACTIONS), GuardMatchSchema = v__namespace.strictObject({
242
+ context: v__namespace.optional(v__namespace.record(NonEmpty, ConditionSchema))
243
+ });
244
+ function taskFields(state, action, op) {
245
+ return {
246
+ name: NonEmpty,
247
+ title: v__namespace.optional(v__namespace.string()),
248
+ description: v__namespace.optional(v__namespace.string()),
249
+ activation: v__namespace.optional(picklist(["auto", "manual"])),
250
+ filter: v__namespace.optional(ConditionSchema),
251
+ /**
252
+ * Auto-completion condition — evaluated at activation and on every
253
+ * cascade; truthy flips the task to `done` with a system actor. On a
254
+ * spawning task it typically reads `$subworkflows`.
255
+ */
256
+ completeWhen: v__namespace.optional(ConditionSchema),
257
+ /**
258
+ * Auto-failure condition symmetric to `completeWhen`, flips to
259
+ * `failed`. When both are truthy on the same evaluation, `failWhen`
260
+ * wins — failure is the more notable signal.
261
+ */
262
+ failWhen: v__namespace.optional(ConditionSchema),
263
+ ops: v__namespace.optional(v__namespace.array(op)),
264
+ effects: v__namespace.optional(v__namespace.array(EffectSchema)),
265
+ actions: v__namespace.optional(v__namespace.array(action)),
266
+ subworkflows: v__namespace.optional(SubworkflowsSchema),
267
+ /** Task-scoped state entries. Resolved at task activation time. */
268
+ state: v__namespace.optional(v__namespace.array(state))
269
+ };
270
+ }
271
+ const StoredTaskSchema = v__namespace.strictObject(
272
+ taskFields(StateEntrySchema, StoredActionSchema, StoredOpSchema)
273
+ ), AuthoringTaskSchema = v__namespace.strictObject(
274
+ taskFields(AuthoringStateEntrySchema, AuthoringActionSchema, AuthoringOpSchema)
275
+ );
276
+ function transitionFields(op, filter) {
277
+ return {
278
+ name: NonEmpty,
279
+ title: v__namespace.optional(v__namespace.string()),
280
+ description: v__namespace.optional(v__namespace.string()),
281
+ to: NonEmpty,
282
+ filter,
283
+ /** The `state.*` subset — state-write-on-move, the stage's EXIT/ARRIVAL payload. */
284
+ ops: v__namespace.optional(v__namespace.array(op)),
285
+ effects: v__namespace.optional(v__namespace.array(EffectSchema))
286
+ };
287
+ }
288
+ const StoredTransitionSchema = v__namespace.strictObject(
289
+ transitionFields(StoredTransitionOpSchema, ConditionSchema)
290
+ ), AuthoringTransitionOpSchema = v__namespace.variant("type", [
291
+ ...opSchemas(AuthoringStateRefSchema),
292
+ AuditOpSchema
293
+ ]), AuthoringTransitionSchema = v__namespace.strictObject(
294
+ transitionFields(AuthoringTransitionOpSchema, v__namespace.optional(ConditionSchema))
295
+ ), GuardActionSchema = picklist(MUTATION_GUARD_ACTIONS), GuardMatchSchema = v__namespace.strictObject({
338
296
  /** Subject `_type`(s); empty matches any type. */
339
297
  types: v__namespace.optional(v__namespace.array(NonEmpty)),
340
- /** Target docs as `Source`s resolved at deploy to bare ids + the resource. */
341
- idRefs: v__namespace.optional(v__namespace.array(SourceSchema)),
298
+ /** Target docs as `$state` reads (or `"$self"`), resolved at deploy to bare ids + the resource. */
299
+ idRefs: v__namespace.optional(v__namespace.array(NonEmpty)),
342
300
  /** Glob id patterns (bare, resource-local). */
343
301
  idPatterns: v__namespace.optional(v__namespace.array(NonEmpty)),
344
302
  actions: v__namespace.pipe(
@@ -346,58 +304,71 @@ const GdrSchema = v__namespace.strictObject({
346
304
  v__namespace.minLength(1, "a guard must match at least one action")
347
305
  )
348
306
  }), GuardSchema = v__namespace.strictObject({
349
- name: v__namespace.optional(v__namespace.string()),
307
+ name: NonEmpty,
308
+ title: v__namespace.optional(v__namespace.string()),
350
309
  description: v__namespace.optional(v__namespace.string()),
351
310
  match: GuardMatchSchema,
352
311
  /**
353
- * Lake GROQ predicate. Reads `document.before`/`document.after`, `mutation`,
354
- * `guard`, and `identity()`. Bare ids/fields only — no GDRs. Omitted or
355
- * empty means UNCONDITIONAL DENY.
312
+ * Lake GROQ predicate a distinct eval context reading
313
+ * `document.before`/`document.after`, `mutation`, `guard`, and
314
+ * `identity()`. Bare ids/fields only. Omitted or empty means
315
+ * UNCONDITIONAL DENY.
356
316
  */
357
317
  predicate: v__namespace.optional(v__namespace.string()),
358
318
  /**
359
- * Projected workflow state the predicate reads as `guard.metadata.*`. Values
360
- * are `Source`s resolved at deploy and re-synced by the engine. Copied data,
361
- * never a cross-resource pointer.
362
- */
363
- metadata: v__namespace.optional(v__namespace.record(NonEmpty, SourceSchema))
364
- }), StageKindSchema = picklist(["initial", "intermediate", "terminal", "waiting"]), StageSchema = v__namespace.strictObject({
365
- type: v__namespace.optional(v__namespace.literal("workflow.stage")),
366
- id: NonEmpty,
367
- title: v__namespace.optional(v__namespace.string()),
368
- description: v__namespace.optional(v__namespace.string()),
369
- kind: v__namespace.optional(StageKindSchema),
370
- tasks: v__namespace.optional(v__namespace.array(TaskSchema)),
371
- transitions: v__namespace.optional(v__namespace.array(TransitionSchema)),
372
- completion: v__namespace.optional(FilterSchema),
373
- onEnter: v__namespace.optional(v__namespace.array(EffectSchema)),
374
- onExit: v__namespace.optional(v__namespace.array(EffectSchema)),
375
- /**
376
- * Lake mutation guards active while this stage holds. Each compiles to a
377
- * `temp.system.guard` doc deployed on stage entry and retracted on exit.
378
- * Plural because one stage's intent may span resources (one guard each).
319
+ * Projected workflow state the predicate reads as `guard.metadata.*`
320
+ * the only bridge from the lake eval context (which cannot see `$state`)
321
+ * to workflow state. Values are NOT GROQ: each is a deploy-time read in
322
+ * the guard mini-language — `"$self"`, `"$now"`, or
323
+ * `"$state.<name>[.path]"` — resolved into a bare value at deploy and
324
+ * re-synced by the post-state-op guard refresh.
379
325
  */
380
- guards: v__namespace.optional(v__namespace.array(GuardSchema)),
381
- contentRefs: v__namespace.optional(v__namespace.array(v__namespace.unknown())),
382
- /** Stage-scoped state slots. Resolved at stage entry and persisted on
383
- * the StageEntry. */
384
- state: v__namespace.optional(v__namespace.array(StateSlotSchema))
385
- }), WorkflowDefinitionSchema = v__namespace.strictObject({
386
- workflowId: NonEmpty,
387
- version: PositiveInt,
388
- title: NonEmpty,
389
- description: v__namespace.optional(v__namespace.string()),
390
- initialStageId: NonEmpty,
391
- /**
392
- * Workflow-level state slots. Persist for the instance lifetime.
393
- * Every stage's filters/ops can read them via
394
- * `*[_id == $self][0].state[key == "<key>"][0].value`. Init-sourced
395
- * slots are supplied at `startInstance` via `initialState`.
396
- */
397
- state: v__namespace.optional(v__namespace.array(StateSlotSchema)),
398
- stages: v__namespace.pipe(v__namespace.array(StageSchema), v__namespace.minLength(1, "must declare at least one stage")),
399
- predicates: v__namespace.optional(v__namespace.array(PredicateSchema))
400
- }), WORKFLOW_DEFINITION_TYPE = "workflow.definition";
326
+ metadata: v__namespace.optional(v__namespace.record(NonEmpty, NonEmpty))
327
+ });
328
+ function stageFields(state, task, transition) {
329
+ return {
330
+ name: NonEmpty,
331
+ title: v__namespace.optional(v__namespace.string()),
332
+ description: v__namespace.optional(v__namespace.string()),
333
+ tasks: v__namespace.optional(v__namespace.array(task)),
334
+ transitions: v__namespace.optional(v__namespace.array(transition)),
335
+ /**
336
+ * Lake mutation guards active while this stage holds. Each compiles to a
337
+ * `temp.system.guard` doc deployed on stage entry and retracted on exit.
338
+ */
339
+ guards: v__namespace.optional(v__namespace.array(GuardSchema)),
340
+ /** Stage-scoped state entries. Resolved at stage entry. */
341
+ state: v__namespace.optional(v__namespace.array(state))
342
+ };
343
+ }
344
+ const StoredStageSchema = v__namespace.strictObject(
345
+ stageFields(StateEntrySchema, StoredTaskSchema, StoredTransitionSchema)
346
+ ), AuthoringStageSchema = v__namespace.strictObject(
347
+ stageFields(AuthoringStateEntrySchema, AuthoringTaskSchema, AuthoringTransitionSchema)
348
+ );
349
+ function workflowFields(state, stage) {
350
+ return {
351
+ name: NonEmpty,
352
+ version: PositiveInt,
353
+ title: NonEmpty,
354
+ description: v__namespace.optional(v__namespace.string()),
355
+ /** Reference field: named for the target, holds the stage's `name`. */
356
+ initialStage: NonEmpty,
357
+ /** Workflow-scope state entries. Persist for the instance lifetime. */
358
+ state: v__namespace.optional(v__namespace.array(state)),
359
+ stages: v__namespace.pipe(v__namespace.array(stage), v__namespace.minLength(1, "must declare at least one stage")),
360
+ /**
361
+ * Nullary named conditions — each `name: groq` entry is pre-evaluated
362
+ * and bound as the boolean `$name` var, composable with native GROQ.
363
+ * Redefining a built-in var is a deploy error (never silently shadow).
364
+ */
365
+ predicates: v__namespace.optional(v__namespace.record(groqIdentifier("`$<name>`"), ConditionSchema))
366
+ };
367
+ }
368
+ v__namespace.strictObject(workflowFields(StateEntrySchema, StoredStageSchema));
369
+ const AuthoringWorkflowSchema = v__namespace.strictObject(
370
+ workflowFields(AuthoringStateEntrySchema, AuthoringStageSchema)
371
+ ), WORKFLOW_DEFINITION_TYPE = "sanity.workflow.definition";
401
372
  function formatValidationError(label, issues) {
402
373
  const lines = issues.map((issue) => ` - ${issue.path.length === 0 ? "(root)" : formatPath(issue.path)}: ${issue.message}`);
403
374
  return `${label} failed validation (${issues.length} issue${issues.length === 1 ? "" : "s"}):
@@ -416,16 +387,19 @@ function formatPath(path) {
416
387
  typeof seg == "number" ? out += `[${seg}]` : out += out.length === 0 ? String(seg) : `.${String(seg)}`;
417
388
  return out;
418
389
  }
419
- exports.ActionSchema = ActionSchema;
420
- exports.AssigneeSchema = AssigneeSchema;
390
+ exports.AuthoringActionSchema = AuthoringActionSchema;
391
+ exports.AuthoringOpSchema = AuthoringOpSchema;
392
+ exports.AuthoringStageSchema = AuthoringStageSchema;
393
+ exports.AuthoringStateEntrySchema = AuthoringStateEntrySchema;
394
+ exports.AuthoringTaskSchema = AuthoringTaskSchema;
395
+ exports.AuthoringTransitionSchema = AuthoringTransitionSchema;
396
+ exports.AuthoringWorkflowSchema = AuthoringWorkflowSchema;
397
+ exports.DOCUMENT_VALUE_PERMISSIONS = DOCUMENT_VALUE_PERMISSIONS;
421
398
  exports.EffectSchema = EffectSchema;
422
- exports.FilterSchema = FilterSchema;
423
- exports.PredicateSchema = PredicateSchema;
424
- exports.StageSchema = StageSchema;
425
- exports.TaskSchema = TaskSchema;
426
- exports.TransitionSchema = TransitionSchema;
399
+ exports.GROQ_IDENTIFIER = GROQ_IDENTIFIER;
400
+ exports.GuardSchema = GuardSchema;
427
401
  exports.WORKFLOW_DEFINITION_TYPE = WORKFLOW_DEFINITION_TYPE;
428
- exports.WorkflowDefinitionSchema = WorkflowDefinitionSchema;
429
402
  exports.formatValidationError = formatValidationError;
403
+ exports.isTerminalTaskStatus = isTerminalTaskStatus;
430
404
  exports.issuesFromValibot = issuesFromValibot;
431
405
  //# sourceMappingURL=schema.cjs.map