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