@sanity/workflow-engine 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/define.cjs CHANGED
@@ -1,69 +1,71 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: !0 });
3
- var zod = require("zod");
4
- const TASK_STATUSES = ["pending", "active", "done", "skipped", "failed"], STATE_SCOPES = ["workflow", "stage", "task"], DOCUMENT_VALUE_PERMISSIONS = ["create", "read", "update"], MUTATION_GUARD_ACTIONS = [
5
- "create",
6
- "update",
7
- "delete",
8
- "publish",
9
- "unpublish"
10
- ];
3
+ var v = require("valibot");
4
+ function _interopNamespaceCompat(e) {
5
+ if (e && typeof e == "object" && "default" in e) return e;
6
+ var n = /* @__PURE__ */ Object.create(null);
7
+ return e && Object.keys(e).forEach(function(k) {
8
+ if (k !== "default") {
9
+ var d = Object.getOwnPropertyDescriptor(e, k);
10
+ Object.defineProperty(n, k, d.get ? d : {
11
+ enumerable: !0,
12
+ get: function() {
13
+ return e[k];
14
+ }
15
+ });
16
+ }
17
+ }), n.default = e, Object.freeze(n);
18
+ }
19
+ var v__namespace = /* @__PURE__ */ _interopNamespaceCompat(v);
11
20
  function knownList(ids) {
12
21
  return [...ids].map((s) => `"${s}"`).join(", ");
13
22
  }
14
- function checkStagesAndTasks(def, ctx) {
23
+ function checkStagesAndTasks(def, issues) {
15
24
  const stageIds = /* @__PURE__ */ new Set();
16
25
  for (const [i, stage] of def.stages.entries())
17
- stageIds.has(stage.id) && ctx.addIssue({
18
- code: "custom",
26
+ stageIds.has(stage.id) && issues.push({
19
27
  path: ["stages", i, "id"],
20
28
  message: `duplicate stage id "${stage.id}" (already declared earlier)`
21
- }), stageIds.add(stage.id), stage.kind === "terminal" && stage.transitions && stage.transitions.length > 0 && ctx.addIssue({
22
- code: "custom",
29
+ }), stageIds.add(stage.id), stage.kind === "terminal" && stage.transitions && stage.transitions.length > 0 && issues.push({
23
30
  path: ["stages", i, "transitions"],
24
31
  message: `terminal stage "${stage.id}" must not declare transitions`
25
- }), checkDuplicateTaskIds(stage, i, ctx);
32
+ }), checkDuplicateTaskIds(stage, i, issues);
26
33
  return stageIds;
27
34
  }
28
- function checkDuplicateTaskIds(stage, i, ctx) {
35
+ function checkDuplicateTaskIds(stage, i, issues) {
29
36
  const taskIds = /* @__PURE__ */ new Set();
30
37
  for (const [j, task] of (stage.tasks ?? []).entries())
31
- taskIds.has(task.id) && ctx.addIssue({
32
- code: "custom",
38
+ taskIds.has(task.id) && issues.push({
33
39
  path: ["stages", i, "tasks", j, "id"],
34
40
  message: `duplicate task id "${task.id}" in stage "${stage.id}"`
35
41
  }), taskIds.add(task.id);
36
42
  }
37
- function checkInitialStage(def, stageIds, ctx) {
38
- stageIds.has(def.initialStageId) || ctx.addIssue({
39
- code: "custom",
43
+ function checkInitialStage(def, stageIds, issues) {
44
+ stageIds.has(def.initialStageId) || issues.push({
40
45
  path: ["initialStageId"],
41
46
  message: `initialStageId "${def.initialStageId}" is not a declared stage. Known stages: ${knownList(stageIds)}`
42
47
  });
43
48
  }
44
- function checkTransitionTargets(def, stageIds, ctx) {
49
+ function checkTransitionTargets(def, stageIds, issues) {
45
50
  for (const [i, stage] of def.stages.entries())
46
51
  for (const [k, t] of (stage.transitions ?? []).entries())
47
- stageIds.has(t.to) || ctx.addIssue({
48
- code: "custom",
52
+ stageIds.has(t.to) || issues.push({
49
53
  path: ["stages", i, "transitions", k, "to"],
50
54
  message: `transition target "${t.to}" is not a declared stage. Known stages: ${knownList(stageIds)}`
51
55
  });
52
56
  }
53
- function collectPredicateIds(def, ctx) {
57
+ function collectPredicateIds(def, issues) {
54
58
  const predicateIds = /* @__PURE__ */ new Set();
55
59
  for (const [i, p] of (def.predicates ?? []).entries())
56
- predicateIds.has(p.id) && ctx.addIssue({
57
- code: "custom",
60
+ predicateIds.has(p.id) && issues.push({
58
61
  path: ["predicates", i, "id"],
59
62
  message: `duplicate predicate id "${p.id}"`
60
63
  }), predicateIds.add(p.id);
61
64
  return predicateIds;
62
65
  }
63
- function checkFilterRefs(def, predicateIds, ctx) {
66
+ function checkFilterRefs(def, predicateIds, issues) {
64
67
  const visit = (g, path) => {
65
- g === void 0 || typeof g == "string" || predicateIds.has(g.ref) || ctx.addIssue({
66
- code: "custom",
68
+ g === void 0 || typeof g == "string" || predicateIds.has(g.ref) || issues.push({
67
69
  path: [...path, "ref"],
68
70
  message: `Filter references predicate "${g.ref}" which is not declared. Known predicates: ${knownList(predicateIds) || "(none)"}`
69
71
  });
@@ -79,112 +81,127 @@ function checkFilterRefs(def, predicateIds, ctx) {
79
81
  visit(t.filter, ["stages", i, "transitions", k, "filter"]);
80
82
  }
81
83
  }
82
- function checkWorkflowInvariants(def, ctx) {
83
- const stageIds = checkStagesAndTasks(def, ctx);
84
- checkInitialStage(def, stageIds, ctx), checkTransitionTargets(def, stageIds, ctx);
85
- const predicateIds = collectPredicateIds(def, ctx);
86
- checkFilterRefs(def, predicateIds, ctx);
84
+ function checkWorkflowInvariants(def) {
85
+ const issues = [], stageIds = checkStagesAndTasks(def, issues);
86
+ checkInitialStage(def, stageIds, issues), checkTransitionTargets(def, stageIds, issues);
87
+ const predicateIds = collectPredicateIds(def, issues);
88
+ return checkFilterRefs(def, predicateIds, issues), issues;
89
+ }
90
+ const TASK_STATUSES = ["pending", "active", "done", "skipped", "failed"], STATE_SCOPES = ["workflow", "stage", "task"], DOCUMENT_VALUE_PERMISSIONS = ["create", "read", "update"], MUTATION_GUARD_ACTIONS = [
91
+ "create",
92
+ "update",
93
+ "delete",
94
+ "publish",
95
+ "unpublish"
96
+ ], 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));
97
+ function picklist(options) {
98
+ return v__namespace.picklist(
99
+ options,
100
+ `Invalid option: expected one of ${options.map((o) => `"${o}"`).join("|")}`
101
+ );
87
102
  }
88
- const NonEmpty = zod.z.string().min(1, "must be a non-empty string"), GdrSchema = zod.z.object({
89
- id: zod.z.string().regex(
90
- /^(dataset|canvas|media-library|dashboard):/,
91
- "must be a GDR URI of form '<scheme>:<...id-parts>' where scheme is one of: dataset, canvas, media-library, dashboard"
103
+ const GdrSchema = v__namespace.strictObject({
104
+ id: v__namespace.pipe(
105
+ v__namespace.string(),
106
+ v__namespace.regex(
107
+ /^(dataset|canvas|media-library|dashboard):/,
108
+ "must be a GDR URI of form '<scheme>:<...id-parts>' where scheme is one of: dataset, canvas, media-library, dashboard"
109
+ )
92
110
  ),
93
111
  type: NonEmpty
94
- }).strict(), SourceSchema = zod.z.lazy(
95
- () => zod.z.union([
96
- zod.z.object({
97
- source: zod.z.literal("literal"),
98
- value: zod.z.union([zod.z.string(), zod.z.number(), zod.z.boolean(), zod.z.null()])
99
- }).strict(),
100
- zod.z.object({ source: zod.z.literal("param"), paramId: NonEmpty }).strict(),
101
- zod.z.object({ source: zod.z.literal("actor") }).strict(),
102
- zod.z.object({ source: zod.z.literal("now") }).strict(),
103
- zod.z.object({ source: zod.z.literal("self") }).strict(),
104
- zod.z.object({ source: zod.z.literal("stageId") }).strict(),
105
- zod.z.object({
106
- source: zod.z.literal("stateRead"),
107
- scope: zod.z.enum(STATE_SCOPES),
112
+ }), SourceSchema = v__namespace.lazy(
113
+ () => v__namespace.union([
114
+ v__namespace.strictObject({
115
+ source: v__namespace.literal("literal"),
116
+ value: v__namespace.union([v__namespace.string(), v__namespace.number(), v__namespace.boolean(), v__namespace.null()])
117
+ }),
118
+ v__namespace.strictObject({ source: v__namespace.literal("param"), paramId: NonEmpty }),
119
+ v__namespace.strictObject({ source: v__namespace.literal("actor") }),
120
+ v__namespace.strictObject({ source: v__namespace.literal("now") }),
121
+ v__namespace.strictObject({ source: v__namespace.literal("self") }),
122
+ v__namespace.strictObject({ source: v__namespace.literal("stageId") }),
123
+ v__namespace.strictObject({
124
+ source: v__namespace.literal("stateRead"),
125
+ scope: picklist(STATE_SCOPES),
108
126
  slotId: NonEmpty,
109
- path: zod.z.string().optional()
110
- }).strict(),
127
+ path: v__namespace.optional(v__namespace.string())
128
+ }),
111
129
  // effectsContext lookup — reads from `instance.effectsContext[id === ID].value`.
112
130
  // Used by chained effects where one handler's output feeds another's binding.
113
- zod.z.object({
114
- source: zod.z.literal("effectOutput"),
131
+ v__namespace.strictObject({
132
+ source: v__namespace.literal("effectOutput"),
115
133
  contextId: NonEmpty,
116
- path: zod.z.string().optional()
117
- }).strict(),
118
- zod.z.object({ source: zod.z.literal("row"), path: zod.z.string().optional() }).strict(),
119
- zod.z.object({
120
- source: zod.z.literal("parentState"),
134
+ path: v__namespace.optional(v__namespace.string())
135
+ }),
136
+ v__namespace.strictObject({ source: v__namespace.literal("row"), path: v__namespace.optional(v__namespace.string()) }),
137
+ v__namespace.strictObject({
138
+ source: v__namespace.literal("parentState"),
121
139
  slotId: NonEmpty,
122
- path: zod.z.string().optional()
123
- }).strict(),
140
+ path: v__namespace.optional(v__namespace.string())
141
+ }),
124
142
  // Compound object source — resolves each field's Source recursively
125
143
  // and packages the result into a JSON object. Lets ops append rich
126
144
  // rows to `checklist` / `notes` / `assignees` slots, e.g.
127
145
  // `{ source: "object", fields: { signer: { source: "actor" },
128
146
  // at: { source: "now" } } }`.
129
- zod.z.object({
130
- source: zod.z.literal("object"),
131
- fields: zod.z.record(NonEmpty, SourceSchema)
132
- }).strict()
147
+ v__namespace.strictObject({
148
+ source: v__namespace.literal("object"),
149
+ fields: v__namespace.record(NonEmpty, SourceSchema)
150
+ })
133
151
  ])
134
- ), ScopeKeySchema = zod.z.object({
135
- scope: zod.z.enum(STATE_SCOPES),
152
+ ), ScopeKeySchema = v__namespace.strictObject({
153
+ scope: picklist(STATE_SCOPES),
136
154
  slotId: NonEmpty
137
- }).strict(), OpPredicateSchema = zod.z.lazy(
138
- () => zod.z.union([
139
- zod.z.object({
155
+ }), OpPredicateSchema = v__namespace.lazy(
156
+ () => v__namespace.union([
157
+ v__namespace.strictObject({
140
158
  field: NonEmpty,
141
159
  equals: SourceSchema
142
- }).strict(),
143
- zod.z.object({ all: zod.z.array(OpPredicateSchema) }).strict(),
144
- zod.z.object({ any: zod.z.array(OpPredicateSchema) }).strict()
160
+ }),
161
+ v__namespace.strictObject({ all: v__namespace.array(OpPredicateSchema) }),
162
+ v__namespace.strictObject({ any: v__namespace.array(OpPredicateSchema) })
145
163
  ])
146
- ), OpSchema = zod.z.discriminatedUnion("type", [
147
- zod.z.object({
148
- type: zod.z.literal("workflow.op.state.set"),
164
+ ), OpSchema = v__namespace.variant("type", [
165
+ v__namespace.strictObject({
166
+ type: v__namespace.literal("workflow.op.state.set"),
149
167
  target: ScopeKeySchema,
150
168
  value: SourceSchema
151
- }).strict(),
152
- zod.z.object({
153
- type: zod.z.literal("workflow.op.state.append"),
169
+ }),
170
+ v__namespace.strictObject({
171
+ type: v__namespace.literal("workflow.op.state.append"),
154
172
  target: ScopeKeySchema,
155
173
  item: SourceSchema
156
- }).strict(),
157
- zod.z.object({
158
- type: zod.z.literal("workflow.op.state.updateWhere"),
174
+ }),
175
+ v__namespace.strictObject({
176
+ type: v__namespace.literal("workflow.op.state.updateWhere"),
159
177
  target: ScopeKeySchema,
160
178
  where: OpPredicateSchema,
161
- merge: zod.z.record(NonEmpty, SourceSchema)
162
- }).strict(),
163
- zod.z.object({
164
- type: zod.z.literal("workflow.op.state.removeWhere"),
179
+ merge: v__namespace.record(NonEmpty, SourceSchema)
180
+ }),
181
+ v__namespace.strictObject({
182
+ type: v__namespace.literal("workflow.op.state.removeWhere"),
165
183
  target: ScopeKeySchema,
166
184
  where: OpPredicateSchema
167
- }).strict(),
168
- zod.z.object({
169
- type: zod.z.literal("workflow.op.status.set"),
185
+ }),
186
+ v__namespace.strictObject({
187
+ type: v__namespace.literal("workflow.op.status.set"),
170
188
  taskId: NonEmpty,
171
- status: zod.z.enum(TASK_STATUSES)
172
- }).strict()
173
- ]), StateSlotSourceSchema = zod.z.union([
174
- zod.z.object({ kind: zod.z.literal("init") }).strict(),
175
- zod.z.object({
176
- kind: zod.z.literal("query"),
189
+ status: picklist(TASK_STATUSES)
190
+ })
191
+ ]), StateSlotSourceSchema = v__namespace.union([
192
+ v__namespace.strictObject({ kind: v__namespace.literal("init") }),
193
+ v__namespace.strictObject({
194
+ kind: v__namespace.literal("query"),
177
195
  query: NonEmpty,
178
- resource: zod.z.union([zod.z.literal("workflow"), zod.z.literal("subject"), GdrSchema]).optional()
179
- }).strict(),
180
- zod.z.object({ kind: zod.z.literal("write") }).strict(),
181
- zod.z.object({ kind: zod.z.literal("computed"), compute: NonEmpty }).strict(),
196
+ resource: v__namespace.optional(v__namespace.union([v__namespace.literal("workflow"), v__namespace.literal("subject"), GdrSchema]))
197
+ }),
198
+ v__namespace.strictObject({ kind: v__namespace.literal("write") }),
182
199
  /**
183
200
  * Pre-populate the slot at resolution time with a constant value.
184
201
  * Useful for stage- or task-scope slots that should start non-empty
185
202
  * (e.g. a default headline, a starting tally).
186
203
  */
187
- zod.z.object({ kind: zod.z.literal("literal"), value: zod.z.unknown() }).strict(),
204
+ v__namespace.strictObject({ kind: v__namespace.literal("literal"), value: v__namespace.unknown() }),
188
205
  /**
189
206
  * Pre-populate by reading another already-resolved slot. `scope:
190
207
  * "workflow"` reads from the workflow-scope `state[]` (resolved at
@@ -193,18 +210,13 @@ const NonEmpty = zod.z.string().min(1, "must be a non-empty string"), GdrSchema
193
210
  * Optional `path` selects a nested field (e.g. `path: "id"` on a
194
211
  * doc.ref slot).
195
212
  */
196
- zod.z.object({
197
- kind: zod.z.literal("stateRead"),
198
- scope: zod.z.union([zod.z.literal("workflow"), zod.z.literal("stage")]),
213
+ v__namespace.strictObject({
214
+ kind: v__namespace.literal("stateRead"),
215
+ scope: v__namespace.union([v__namespace.literal("workflow"), v__namespace.literal("stage")]),
199
216
  slotId: NonEmpty,
200
- path: zod.z.string().optional()
201
- }).strict()
202
- ]), StateSlotBase = {
203
- id: NonEmpty,
204
- title: zod.z.string().optional(),
205
- description: zod.z.string().optional(),
206
- source: StateSlotSourceSchema
207
- }, StateSlotKindSchema = zod.z.enum([
217
+ path: v__namespace.optional(v__namespace.string())
218
+ })
219
+ ]), StateSlotKindSchema = picklist([
208
220
  "workflow.state.doc.ref",
209
221
  "workflow.state.doc.refs",
210
222
  // Release reference. A workflow declares this slot to say "I target a
@@ -222,47 +234,48 @@ const NonEmpty = zod.z.string().min(1, "must be a non-empty string"), GdrSchema
222
234
  "workflow.state.checklist",
223
235
  "workflow.state.notes",
224
236
  "workflow.state.assignees"
225
- ]), StateSlotSchema = zod.z.discriminatedUnion(
226
- "type",
227
- StateSlotKindSchema.options.map(
228
- (kind) => zod.z.object({ type: zod.z.literal(kind), ...StateSlotBase }).strict()
229
- )
230
- ), FilterSchema = zod.z.union([
237
+ ]), StateSlotSchema = v__namespace.strictObject({
238
+ type: StateSlotKindSchema,
239
+ id: NonEmpty,
240
+ title: v__namespace.optional(v__namespace.string()),
241
+ description: v__namespace.optional(v__namespace.string()),
242
+ source: StateSlotSourceSchema
243
+ }), FilterSchema = v__namespace.union([
231
244
  // Inline GROQ — only reserved params allowed at runtime.
232
245
  NonEmpty,
233
246
  // Reference to a named predicate, optionally with named args.
234
- zod.z.object({
247
+ v__namespace.strictObject({
235
248
  ref: NonEmpty,
236
- args: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
237
- }).strict()
238
- ]), PredicateParamSchema = zod.z.object({
249
+ args: v__namespace.optional(v__namespace.record(v__namespace.string(), v__namespace.unknown()))
250
+ })
251
+ ]), PredicateParamSchema = v__namespace.strictObject({
239
252
  id: NonEmpty,
240
- title: zod.z.string().optional(),
241
- description: zod.z.string().optional(),
242
- type: zod.z.enum(["string", "number", "boolean", "string[]", "reference"]),
243
- enum: zod.z.array(zod.z.string()).optional()
244
- }).strict(), PredicateSchema = zod.z.object({
245
- type: zod.z.literal("workflow.predicate").optional(),
253
+ title: v__namespace.optional(v__namespace.string()),
254
+ description: v__namespace.optional(v__namespace.string()),
255
+ type: picklist(["string", "number", "boolean", "string[]", "reference"]),
256
+ enum: v__namespace.optional(v__namespace.array(v__namespace.string()))
257
+ }), PredicateSchema = v__namespace.strictObject({
258
+ type: v__namespace.optional(v__namespace.literal("workflow.predicate")),
246
259
  id: NonEmpty,
247
- title: zod.z.string().optional(),
248
- description: zod.z.string().optional(),
260
+ title: v__namespace.optional(v__namespace.string()),
261
+ description: v__namespace.optional(v__namespace.string()),
249
262
  groq: NonEmpty,
250
- params: zod.z.array(PredicateParamSchema).optional()
251
- }).strict(), EffectSchema = zod.z.object({
252
- type: zod.z.literal("workflow.effect").optional(),
263
+ params: v__namespace.optional(v__namespace.array(PredicateParamSchema))
264
+ }), EffectSchema = v__namespace.strictObject({
265
+ type: v__namespace.optional(v__namespace.literal("workflow.effect")),
253
266
  id: NonEmpty,
254
- title: zod.z.string().optional(),
255
- description: zod.z.string().optional(),
267
+ title: v__namespace.optional(v__namespace.string()),
268
+ description: v__namespace.optional(v__namespace.string()),
256
269
  // Bindings resolve typed Source values at commit time, producing
257
270
  // concrete JSON in the queued effect's `params`. NO GROQ.
258
- bindings: zod.z.record(zod.z.string(), SourceSchema).optional(),
259
- input: zod.z.record(zod.z.string(), zod.z.unknown()).optional()
260
- }).strict(), TerminalTaskStatus = zod.z.enum(["done", "skipped", "failed"]), PermissionSchema = zod.z.enum(DOCUMENT_VALUE_PERMISSIONS), ActionParamSchema = zod.z.object({
261
- type: zod.z.literal("workflow.action.param").optional(),
271
+ bindings: v__namespace.optional(v__namespace.record(v__namespace.string(), SourceSchema)),
272
+ input: v__namespace.optional(v__namespace.record(v__namespace.string(), v__namespace.unknown()))
273
+ }), TerminalTaskStatus = picklist(["done", "skipped", "failed"]), PermissionSchema = picklist(DOCUMENT_VALUE_PERMISSIONS), ActionParamSchema = v__namespace.strictObject({
274
+ type: v__namespace.optional(v__namespace.literal("workflow.action.param")),
262
275
  id: NonEmpty,
263
- title: zod.z.string().optional(),
264
- description: zod.z.string().optional(),
265
- paramType: zod.z.enum([
276
+ title: v__namespace.optional(v__namespace.string()),
277
+ description: v__namespace.optional(v__namespace.string()),
278
+ paramType: picklist([
266
279
  "string",
267
280
  "number",
268
281
  "boolean",
@@ -273,15 +286,15 @@ const NonEmpty = zod.z.string().min(1, "must be a non-empty string"), GdrSchema
273
286
  "doc.refs",
274
287
  "json"
275
288
  ]),
276
- required: zod.z.boolean().optional()
277
- }).strict(), ActionSchema = zod.z.object({
278
- type: zod.z.literal("workflow.action").optional(),
289
+ required: v__namespace.optional(v__namespace.boolean())
290
+ }), ActionSchema = v__namespace.strictObject({
291
+ type: v__namespace.optional(v__namespace.literal("workflow.action")),
279
292
  id: NonEmpty,
280
- title: zod.z.string().optional(),
281
- description: zod.z.string().optional(),
282
- filter: FilterSchema.optional(),
293
+ title: v__namespace.optional(v__namespace.string()),
294
+ description: v__namespace.optional(v__namespace.string()),
295
+ filter: v__namespace.optional(FilterSchema),
283
296
  /** Caller-supplied params, validated before ops/effects run. */
284
- params: zod.z.array(ActionParamSchema).optional(),
297
+ params: v__namespace.optional(v__namespace.array(ActionParamSchema)),
285
298
  /**
286
299
  * Status the task flips to when this action fires. Omit to leave
287
300
  * the task status unchanged — used by intermediate `claim` /
@@ -291,7 +304,7 @@ const NonEmpty = zod.z.string().min(1, "must be a non-empty string"), GdrSchema
291
304
  * mid-flip back to `active` outside of the `workflow.op.status.set`
292
305
  * op explicit path.
293
306
  */
294
- setStatus: TerminalTaskStatus.optional(),
307
+ setStatus: v__namespace.optional(TerminalTaskStatus),
295
308
  /**
296
309
  * Inline state-mutation primitives executed during the action commit.
297
310
  * Each op runs against the in-memory mutation (deterministic, no
@@ -299,72 +312,74 @@ const NonEmpty = zod.z.string().min(1, "must be a non-empty string"), GdrSchema
299
312
  * op applies. Logged on `history[]` as `workflow.history.opApplied`
300
313
  * and surfaced on the `ActionResult.ranOps`.
301
314
  */
302
- ops: zod.z.array(OpSchema).optional(),
303
- effects: zod.z.array(EffectSchema).optional(),
315
+ ops: v__namespace.optional(v__namespace.array(OpSchema)),
316
+ effects: v__namespace.optional(v__namespace.array(EffectSchema)),
304
317
  /**
305
318
  * Role gating — OR-match against `Actor.roles[]`. If omitted, no role
306
319
  * gate. `"*"` in Actor.roles always matches (bench / all-access).
307
320
  */
308
- roles: zod.z.array(NonEmpty).optional(),
321
+ roles: v__namespace.optional(v__namespace.array(NonEmpty)),
309
322
  /**
310
323
  * When true, the actor must appear in the task's `assignees[]` (as a
311
324
  * user-kind assignee matching by id) for the action to be allowed.
312
325
  */
313
- requireAssignment: zod.z.boolean().optional(),
326
+ requireAssignment: v__namespace.optional(v__namespace.boolean()),
314
327
  /**
315
328
  * Which permission on the workflow.instance document the actor must
316
329
  * hold (per supplied `grants`). Defaults to "update". Only checked
317
330
  * when grants are supplied at evaluate/fireAction time.
318
331
  */
319
- requiredPermission: PermissionSchema.optional()
320
- }).strict(), CompletionPolicySchema = zod.z.union([
321
- zod.z.object({ kind: zod.z.literal("all") }).strict(),
322
- zod.z.object({ kind: zod.z.literal("any") }).strict(),
323
- zod.z.object({
324
- kind: zod.z.literal("count"),
325
- n: zod.z.number().int().positive()
326
- }).strict()
327
- ]), LogicalDefinitionRefSchema = zod.z.object({
332
+ requiredPermission: v__namespace.optional(PermissionSchema)
333
+ }), CompletionPolicySchema = v__namespace.union([
334
+ v__namespace.strictObject({ kind: v__namespace.literal("all") }),
335
+ v__namespace.strictObject({ kind: v__namespace.literal("any") }),
336
+ v__namespace.strictObject({
337
+ kind: v__namespace.literal("count"),
338
+ n: PositiveInt
339
+ })
340
+ ]), LogicalDefinitionRefSchema = v__namespace.strictObject({
328
341
  workflowId: NonEmpty,
329
- version: zod.z.union([zod.z.number().int().positive(), zod.z.literal("latest")]).optional()
330
- }).strict(), SpawnForEachSchema = zod.z.object({
342
+ version: v__namespace.optional(v__namespace.union([PositiveInt, v__namespace.literal("latest")]))
343
+ }), SpawnForEachSchema = v__namespace.strictObject({
331
344
  groq: NonEmpty,
332
- as: zod.z.object({
333
- initialState: zod.z.array(
334
- zod.z.object({
335
- type: StateSlotKindSchema,
336
- id: NonEmpty,
337
- value: SourceSchema
338
- }).strict()
339
- ).optional(),
340
- effectsContext: zod.z.record(zod.z.string(), SourceSchema).optional()
341
- }).strict()
342
- }).strict(), SpawnSchema = zod.z.object({
343
- type: zod.z.literal("workflow.spawn").optional(),
344
- id: zod.z.string().optional(),
345
- title: zod.z.string().optional(),
346
- description: zod.z.string().optional(),
345
+ as: v__namespace.strictObject({
346
+ initialState: v__namespace.optional(
347
+ v__namespace.array(
348
+ v__namespace.strictObject({
349
+ type: StateSlotKindSchema,
350
+ id: NonEmpty,
351
+ value: SourceSchema
352
+ })
353
+ )
354
+ ),
355
+ effectsContext: v__namespace.optional(v__namespace.record(v__namespace.string(), SourceSchema))
356
+ })
357
+ }), SpawnSchema = v__namespace.strictObject({
358
+ type: v__namespace.optional(v__namespace.literal("workflow.spawn")),
359
+ id: v__namespace.optional(v__namespace.string()),
360
+ title: v__namespace.optional(v__namespace.string()),
361
+ description: v__namespace.optional(v__namespace.string()),
347
362
  definitionRef: LogicalDefinitionRefSchema,
348
363
  forEach: SpawnForEachSchema,
349
- completionPolicy: CompletionPolicySchema.optional()
350
- }).strict(), AssigneeSchema = zod.z.discriminatedUnion("kind", [
351
- zod.z.object({ kind: zod.z.literal("user"), id: NonEmpty }).strict(),
352
- zod.z.object({ kind: zod.z.literal("role"), role: NonEmpty }).strict()
353
- ]), TaskSchema = zod.z.object({
354
- type: zod.z.literal("workflow.task").optional(),
364
+ completionPolicy: v__namespace.optional(CompletionPolicySchema)
365
+ }), AssigneeSchema = v__namespace.variant("kind", [
366
+ v__namespace.strictObject({ kind: v__namespace.literal("user"), id: NonEmpty }),
367
+ v__namespace.strictObject({ kind: v__namespace.literal("role"), role: NonEmpty })
368
+ ]), TaskSchema = v__namespace.strictObject({
369
+ type: v__namespace.optional(v__namespace.literal("workflow.task")),
355
370
  id: NonEmpty,
356
- title: zod.z.string().optional(),
357
- description: zod.z.string().optional(),
358
- assignees: zod.z.array(AssigneeSchema).optional(),
359
- filter: FilterSchema.optional(),
360
- invokeOn: zod.z.enum(["stageEnter", "manual"]).optional(),
371
+ title: v__namespace.optional(v__namespace.string()),
372
+ description: v__namespace.optional(v__namespace.string()),
373
+ assignees: v__namespace.optional(v__namespace.array(AssigneeSchema)),
374
+ filter: v__namespace.optional(FilterSchema),
375
+ invokeOn: v__namespace.optional(picklist(["stageEnter", "manual"])),
361
376
  /**
362
377
  * Auto-completion predicate — same shape as `transition.filter`. When
363
378
  * truthy at activation or on any subsequent cascade, the engine
364
379
  * flips the task to `done` with a `{ kind: "system", id: "engine.completeWhen" }`
365
380
  * actor. A full filter, not just a wall-clock deadline.
366
381
  */
367
- completeWhen: FilterSchema.optional(),
382
+ completeWhen: v__namespace.optional(FilterSchema),
368
383
  /**
369
384
  * Auto-failure predicate — symmetric to `completeWhen`. Same shape,
370
385
  * same evaluation cadence (activation + every cascade), but flips
@@ -374,72 +389,75 @@ const NonEmpty = zod.z.string().min(1, "must be a non-empty string"), GdrSchema
374
389
  * wins — failure is the more notable signal and should not be
375
390
  * silently swallowed.
376
391
  */
377
- failWhen: FilterSchema.optional(),
378
- effects: zod.z.array(EffectSchema).optional(),
379
- actions: zod.z.array(ActionSchema).optional(),
380
- spawns: SpawnSchema.optional(),
381
- contentRefs: zod.z.array(zod.z.unknown()).optional(),
392
+ failWhen: v__namespace.optional(FilterSchema),
393
+ effects: v__namespace.optional(v__namespace.array(EffectSchema)),
394
+ actions: v__namespace.optional(v__namespace.array(ActionSchema)),
395
+ spawns: v__namespace.optional(SpawnSchema),
396
+ contentRefs: v__namespace.optional(v__namespace.array(v__namespace.unknown())),
382
397
  /** Task-scoped state slots. Resolved at task activation time. */
383
- state: zod.z.array(StateSlotSchema).optional()
384
- }).strict(), TransitionSchema = zod.z.object({
385
- type: zod.z.literal("workflow.transition").optional(),
398
+ state: v__namespace.optional(v__namespace.array(StateSlotSchema))
399
+ }), TransitionSchema = v__namespace.strictObject({
400
+ type: v__namespace.optional(v__namespace.literal("workflow.transition")),
386
401
  id: NonEmpty,
387
- title: zod.z.string().optional(),
388
- description: zod.z.string().optional(),
402
+ title: v__namespace.optional(v__namespace.string()),
403
+ description: v__namespace.optional(v__namespace.string()),
389
404
  to: NonEmpty,
390
- on: zod.z.string().optional(),
391
- filter: FilterSchema.optional(),
392
- effects: zod.z.array(EffectSchema).optional()
393
- }).strict(), GuardActionSchema = zod.z.enum(MUTATION_GUARD_ACTIONS), GuardMatchSchema = zod.z.object({
405
+ on: v__namespace.optional(v__namespace.string()),
406
+ filter: v__namespace.optional(FilterSchema),
407
+ effects: v__namespace.optional(v__namespace.array(EffectSchema))
408
+ }), GuardActionSchema = picklist(MUTATION_GUARD_ACTIONS), GuardMatchSchema = v__namespace.strictObject({
394
409
  /** Subject `_type`(s); empty matches any type. */
395
- types: zod.z.array(NonEmpty).optional(),
410
+ types: v__namespace.optional(v__namespace.array(NonEmpty)),
396
411
  /** Target docs as `Source`s resolved at deploy to bare ids + the resource. */
397
- idRefs: zod.z.array(SourceSchema).optional(),
412
+ idRefs: v__namespace.optional(v__namespace.array(SourceSchema)),
398
413
  /** Glob id patterns (bare, resource-local). */
399
- idPatterns: zod.z.array(NonEmpty).optional(),
400
- actions: zod.z.array(GuardActionSchema).min(1, "a guard must match at least one action")
401
- }).strict(), GuardSchema = zod.z.object({
402
- name: zod.z.string().optional(),
403
- description: zod.z.string().optional(),
414
+ idPatterns: v__namespace.optional(v__namespace.array(NonEmpty)),
415
+ actions: v__namespace.pipe(
416
+ v__namespace.array(GuardActionSchema),
417
+ v__namespace.minLength(1, "a guard must match at least one action")
418
+ )
419
+ }), GuardSchema = v__namespace.strictObject({
420
+ name: v__namespace.optional(v__namespace.string()),
421
+ description: v__namespace.optional(v__namespace.string()),
404
422
  match: GuardMatchSchema,
405
423
  /**
406
424
  * Lake GROQ predicate. Reads `document.before`/`document.after`, `mutation`,
407
425
  * `guard`, and `identity()`. Bare ids/fields only — no GDRs. Omitted or
408
426
  * empty means UNCONDITIONAL DENY.
409
427
  */
410
- predicate: zod.z.string().optional(),
428
+ predicate: v__namespace.optional(v__namespace.string()),
411
429
  /**
412
430
  * Projected workflow state the predicate reads as `guard.metadata.*`. Values
413
431
  * are `Source`s resolved at deploy and re-synced by the engine. Copied data,
414
432
  * never a cross-resource pointer.
415
433
  */
416
- metadata: zod.z.record(NonEmpty, SourceSchema).optional()
417
- }).strict(), StageKindSchema = zod.z.enum(["initial", "intermediate", "terminal", "waiting"]), StageSchema = zod.z.object({
418
- type: zod.z.literal("workflow.stage").optional(),
434
+ metadata: v__namespace.optional(v__namespace.record(NonEmpty, SourceSchema))
435
+ }), StageKindSchema = picklist(["initial", "intermediate", "terminal", "waiting"]), StageSchema = v__namespace.strictObject({
436
+ type: v__namespace.optional(v__namespace.literal("workflow.stage")),
419
437
  id: NonEmpty,
420
- title: zod.z.string().optional(),
421
- description: zod.z.string().optional(),
422
- kind: StageKindSchema.optional(),
423
- tasks: zod.z.array(TaskSchema).optional(),
424
- transitions: zod.z.array(TransitionSchema).optional(),
425
- completion: FilterSchema.optional(),
426
- onEnter: zod.z.array(EffectSchema).optional(),
427
- onExit: zod.z.array(EffectSchema).optional(),
438
+ title: v__namespace.optional(v__namespace.string()),
439
+ description: v__namespace.optional(v__namespace.string()),
440
+ kind: v__namespace.optional(StageKindSchema),
441
+ tasks: v__namespace.optional(v__namespace.array(TaskSchema)),
442
+ transitions: v__namespace.optional(v__namespace.array(TransitionSchema)),
443
+ completion: v__namespace.optional(FilterSchema),
444
+ onEnter: v__namespace.optional(v__namespace.array(EffectSchema)),
445
+ onExit: v__namespace.optional(v__namespace.array(EffectSchema)),
428
446
  /**
429
447
  * Lake mutation guards active while this stage holds. Each compiles to a
430
448
  * `temp.system.guard` doc deployed on stage entry and retracted on exit.
431
449
  * Plural because one stage's intent may span resources (one guard each).
432
450
  */
433
- guards: zod.z.array(GuardSchema).optional(),
434
- contentRefs: zod.z.array(zod.z.unknown()).optional(),
451
+ guards: v__namespace.optional(v__namespace.array(GuardSchema)),
452
+ contentRefs: v__namespace.optional(v__namespace.array(v__namespace.unknown())),
435
453
  /** Stage-scoped state slots. Resolved at stage entry and persisted on
436
454
  * the StageEntry. */
437
- state: zod.z.array(StateSlotSchema).optional()
438
- }).strict(), WorkflowDefinitionShape = zod.z.object({
455
+ state: v__namespace.optional(v__namespace.array(StateSlotSchema))
456
+ }), WorkflowDefinitionSchema = v__namespace.strictObject({
439
457
  workflowId: NonEmpty,
440
- version: zod.z.number().int().positive(),
458
+ version: PositiveInt,
441
459
  title: NonEmpty,
442
- description: zod.z.string().optional(),
460
+ description: v__namespace.optional(v__namespace.string()),
443
461
  initialStageId: NonEmpty,
444
462
  /**
445
463
  * Workflow-level state slots. Persist for the instance lifetime.
@@ -447,16 +465,22 @@ const NonEmpty = zod.z.string().min(1, "must be a non-empty string"), GdrSchema
447
465
  * `*[_id == $self][0].state[key == "<key>"][0].value`. Init-sourced
448
466
  * slots are supplied at `startInstance` via `initialState`.
449
467
  */
450
- state: zod.z.array(StateSlotSchema).optional(),
451
- stages: zod.z.array(StageSchema).min(1, "must declare at least one stage"),
452
- predicates: zod.z.array(PredicateSchema).optional()
453
- }).strict(), WorkflowDefinitionSchema = WorkflowDefinitionShape.superRefine(checkWorkflowInvariants);
454
- function formatZodError(error, label) {
455
- const lines = error.issues.map((issue) => ` - ${issue.path.length === 0 ? "(root)" : formatPath(issue.path)}: ${issue.message}`);
456
- return `${label} failed validation (${error.issues.length} issue${error.issues.length === 1 ? "" : "s"}):
468
+ state: v__namespace.optional(v__namespace.array(StateSlotSchema)),
469
+ stages: v__namespace.pipe(v__namespace.array(StageSchema), v__namespace.minLength(1, "must declare at least one stage")),
470
+ predicates: v__namespace.optional(v__namespace.array(PredicateSchema))
471
+ });
472
+ function formatValidationError(label, issues) {
473
+ const lines = issues.map((issue) => ` - ${issue.path.length === 0 ? "(root)" : formatPath(issue.path)}: ${issue.message}`);
474
+ return `${label} failed validation (${issues.length} issue${issues.length === 1 ? "" : "s"}):
457
475
  ${lines.join(`
458
476
  `)}`;
459
477
  }
478
+ function issuesFromValibot(issues) {
479
+ return issues.map((issue) => ({
480
+ path: issue.path ? issue.path.map((item) => item.key) : [],
481
+ message: issue.message
482
+ }));
483
+ }
460
484
  function formatPath(path) {
461
485
  let out = "";
462
486
  for (const seg of path)
@@ -464,11 +488,9 @@ function formatPath(path) {
464
488
  return out;
465
489
  }
466
490
  function defineWorkflow(definition) {
467
- return parseOrThrow(
468
- WorkflowDefinitionSchema,
469
- definition,
470
- typeof definition.workflowId == "string" ? `defineWorkflow("${definition.workflowId}")` : "defineWorkflow"
471
- );
491
+ const workflowId = definition.workflowId, label = typeof workflowId == "string" ? `defineWorkflow("${workflowId}")` : "defineWorkflow", parsed = parseOrThrow(WorkflowDefinitionSchema, definition, label), issues = checkWorkflowInvariants(parsed);
492
+ if (issues.length > 0) throw new Error(formatValidationError(label, issues));
493
+ return parsed;
472
494
  }
473
495
  function defineStage(stage) {
474
496
  return parseOrThrow(StageSchema, stage, labelFor("defineStage", stage, "id"));
@@ -503,15 +525,15 @@ function defineEffectDescriptor(descriptor) {
503
525
  return descriptor;
504
526
  }
505
527
  function parseOrThrow(schema, input, label) {
506
- const result = schema.safeParse(input);
528
+ const result = v__namespace.safeParse(schema, input);
507
529
  if (!result.success)
508
- throw new Error(formatZodError(result.error, label));
509
- return result.data;
530
+ throw new Error(formatValidationError(label, issuesFromValibot(result.issues)));
531
+ return result.output;
510
532
  }
511
533
  function labelFor(fn, value, key) {
512
534
  if (value && typeof value == "object" && key in value) {
513
- const v = value[key];
514
- if (typeof v == "string") return `${fn}("${v}")`;
535
+ const raw = value[key];
536
+ if (typeof raw == "string") return `${fn}("${raw}")`;
515
537
  }
516
538
  return fn;
517
539
  }