@sanity/workflow-engine 0.17.0 → 0.18.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/CHANGELOG.md +44 -0
- package/DATAMODEL.md +140 -13
- package/README.md +23 -0
- package/dist/_chunks-cjs/invariants.cjs +620 -116
- package/dist/_chunks-es/invariants.js +567 -117
- package/dist/define.cjs +6 -1
- package/dist/define.d.cts +185 -36
- package/dist/define.d.ts +185 -36
- package/dist/define.js +7 -2
- package/dist/index.cjs +852 -392
- package/dist/index.d.cts +746 -198
- package/dist/index.d.ts +746 -198
- package/dist/index.js +752 -330
- package/package.json +1 -1
|
@@ -4,6 +4,291 @@ import { conditionOutcome, runGroq as runGroq$1, evaluateConditionOutcome as eva
|
|
|
4
4
|
|
|
5
5
|
import { parse } from "groq-js";
|
|
6
6
|
|
|
7
|
+
class WorkflowError extends Error {
|
|
8
|
+
kind;
|
|
9
|
+
constructor(kind, message, options) {
|
|
10
|
+
super(message, options), this.kind = kind;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
class ContractViolationError extends WorkflowError {
|
|
15
|
+
constructor(message) {
|
|
16
|
+
super("contract-violation", message), this.name = "ContractViolationError";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
class InstanceNotFoundError extends WorkflowError {
|
|
21
|
+
instanceId;
|
|
22
|
+
constructor(args) {
|
|
23
|
+
super("instance-not-found", `Workflow instance ${args.instanceId} not found${args.detail ? ` (${args.detail})` : ""}`),
|
|
24
|
+
this.name = "InstanceNotFoundError", this.instanceId = args.instanceId;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
class DefinitionNotFoundError extends WorkflowError {
|
|
29
|
+
definition;
|
|
30
|
+
version;
|
|
31
|
+
constructor(args) {
|
|
32
|
+
super("definition-not-found", args.version !== void 0 ? `Workflow definition ${args.definition} v${args.version} not deployed` : `Workflow definition ${args.definition} has no deployed versions`),
|
|
33
|
+
this.name = "DefinitionNotFoundError", this.definition = args.definition, args.version !== void 0 && (this.version = args.version);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
class SpawnContractsInvalidError extends WorkflowError {
|
|
38
|
+
issues;
|
|
39
|
+
constructor(args) {
|
|
40
|
+
super("spawn-contracts-invalid", args.message), this.name = "SpawnContractsInvalidError",
|
|
41
|
+
this.issues = args.issues;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
class DefinitionInUseError extends WorkflowError {
|
|
46
|
+
definition;
|
|
47
|
+
blockedBy;
|
|
48
|
+
constructor(args) {
|
|
49
|
+
super("definition-in-use", definitionInUseMessage(args.definition, args.blockedBy)),
|
|
50
|
+
this.name = "DefinitionInUseError", this.definition = args.definition, this.blockedBy = args.blockedBy;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function definitionInUseMessage(definition, blockedBy) {
|
|
55
|
+
if (blockedBy.reason === "non-terminal-instances") {
|
|
56
|
+
const head = blockedBy.instanceIds.slice(0, 3).join(", "), preview = blockedBy.instanceIds.length > 3 ? `${head}, …` : head;
|
|
57
|
+
return `Cannot delete ${definition}: ${blockedBy.instanceIds.length} non-terminal instance(s) exist (${preview}). Pass cascade to abort them first — instances are aborted in place, never deleted.`;
|
|
58
|
+
}
|
|
59
|
+
const names = blockedBy.referrers.map(r => `${r.definition} v${r.version}`).join(", ");
|
|
60
|
+
return `Cannot delete ${definition}: still spawn-referenced by deployed definition(s) ${names}. Delete or redeploy the referrer(s) first.`;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
class EffectNotFoundError extends WorkflowError {
|
|
64
|
+
instanceId;
|
|
65
|
+
effectKey;
|
|
66
|
+
settled;
|
|
67
|
+
constructor(args) {
|
|
68
|
+
super("effect-not-found", effectNotFoundMessage(args)), this.name = "EffectNotFoundError",
|
|
69
|
+
this.instanceId = args.instanceId, this.effectKey = args.effectKey, args.settled !== void 0 && (this.settled = args.settled);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function effectNotFoundMessage(args) {
|
|
74
|
+
const base = `Pending effect "${args.effectKey}" not found on instance ${args.instanceId}`;
|
|
75
|
+
if (args.settled === void 0) return base;
|
|
76
|
+
const cause = args.settled.detail !== void 0 ? ` (${args.settled.detail})` : "";
|
|
77
|
+
return args.settled.status === "cancelled" ? `${base} — it was cancelled at ${args.settled.ranAt}${cause}` : `${base} — it already settled "${args.settled.status}" at ${args.settled.ranAt}${cause}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function errorMessage(err) {
|
|
81
|
+
return (err instanceof Error ? err.message : String(err)).replace(/[^\P{Cc}\n\t]/gu, "");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function rethrowWithContext(err, context) {
|
|
85
|
+
throw new Error(`${context}: ${errorMessage(err)}`, {
|
|
86
|
+
cause: err
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const WORKFLOW_INSTANCE_TYPE = "sanity.workflow.instance";
|
|
91
|
+
|
|
92
|
+
function terminalState(instance) {
|
|
93
|
+
return instance.abortedAt !== void 0 ? "aborted" : instance.completedAt !== void 0 ? "completed" : "in-flight";
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function isUnprimed(instance) {
|
|
97
|
+
return instance.stages.length === 0 && terminalState(instance) === "in-flight";
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function parseDefinitionSnapshotValue(instance) {
|
|
101
|
+
try {
|
|
102
|
+
return JSON.parse(instance.definitionSnapshot);
|
|
103
|
+
} catch (err) {
|
|
104
|
+
rethrowWithContext(err, `Failed to parse definitionSnapshot on instance "${instance._id}"`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function parseDefinitionSnapshot(instance) {
|
|
109
|
+
return parseDefinitionSnapshotValue(instance);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function parentRef(instance) {
|
|
113
|
+
return instance.ancestors.at(-1);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const DATA_MODEL_VERSION = 2, DATA_MODEL_MIN_READER = 2, READER_MODEL_ROLLOUT_URL = "https://github.com/sanity-io/workflows/blob/main/docs/reader-model-rollout.md";
|
|
117
|
+
|
|
118
|
+
class ReaderModelAcknowledgementError extends WorkflowError {
|
|
119
|
+
code="WORKFLOW_READER_MODEL_ACKNOWLEDGEMENT_MISMATCH";
|
|
120
|
+
expectedMinReaderModel;
|
|
121
|
+
engineMinReaderModel=DATA_MODEL_MIN_READER;
|
|
122
|
+
engineModelVersion=DATA_MODEL_VERSION;
|
|
123
|
+
documentationUrl=READER_MODEL_ROLLOUT_URL;
|
|
124
|
+
constructor(expectedMinReaderModel, context = "Deployment") {
|
|
125
|
+
const expected = expectedMinReaderModel === void 0 ? "missing" : String(expectedMinReaderModel);
|
|
126
|
+
super("reader-model-acknowledgement", `${context} expected reader floor ${expected}; the installed engine requires acknowledgement ${DATA_MODEL_MIN_READER}. Dependency upgrades can change writer compatibility. Upgrade all readers and Functions before accepting a higher floor; after verifying the rollout, change the literal in deployment configuration. ${READER_MODEL_ROLLOUT_URL}`),
|
|
127
|
+
this.name = "ReaderModelAcknowledgementError", this.expectedMinReaderModel = expectedMinReaderModel;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function assertReaderModelAcknowledgement(expectedMinReaderModel, context) {
|
|
132
|
+
if (typeof expectedMinReaderModel != "number" || !Number.isFinite(expectedMinReaderModel) || !Number.isInteger(expectedMinReaderModel) || expectedMinReaderModel < 0 || expectedMinReaderModel !== DATA_MODEL_MIN_READER) throw new ReaderModelAcknowledgementError(expectedMinReaderModel, context);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const DATA_MODEL_CHANGES = Object.freeze([ Object.freeze({
|
|
136
|
+
id: "governed-model-stamps",
|
|
137
|
+
introducedInModel: 1,
|
|
138
|
+
minReaderModel: 0,
|
|
139
|
+
documentTypes: Object.freeze([ "definition", "instance" ]),
|
|
140
|
+
compatibility: "additive",
|
|
141
|
+
applicability: "unconditional",
|
|
142
|
+
summary: "Definition and instance documents carry model provenance and reader-floor stamps."
|
|
143
|
+
}), Object.freeze({
|
|
144
|
+
id: "subject-field-kind",
|
|
145
|
+
introducedInModel: 2,
|
|
146
|
+
minReaderModel: 0,
|
|
147
|
+
documentTypes: Object.freeze([ "definition", "instance" ]),
|
|
148
|
+
compatibility: "additive",
|
|
149
|
+
applicability: "detectable",
|
|
150
|
+
summary: "A workflow-level subject field identifies the document a workflow is about."
|
|
151
|
+
}), Object.freeze({
|
|
152
|
+
id: "typed-scalar-choice-lists",
|
|
153
|
+
introducedInModel: 2,
|
|
154
|
+
minReaderModel: 2,
|
|
155
|
+
documentTypes: Object.freeze([ "definition", "instance" ]),
|
|
156
|
+
compatibility: "reader-floor",
|
|
157
|
+
applicability: "detectable",
|
|
158
|
+
summary: "Scalar fields may constrain writes to a persisted typed choice list."
|
|
159
|
+
}), Object.freeze({
|
|
160
|
+
id: "action-semantics",
|
|
161
|
+
introducedInModel: 2,
|
|
162
|
+
minReaderModel: 0,
|
|
163
|
+
documentTypes: Object.freeze([ "definition" ]),
|
|
164
|
+
compatibility: "additive",
|
|
165
|
+
applicability: "detectable",
|
|
166
|
+
summary: "Ordinary actions may carry a closed bag of advisory workflow semantics."
|
|
167
|
+
}), Object.freeze({
|
|
168
|
+
id: "inclusive-scalar-bounds",
|
|
169
|
+
introducedInModel: 2,
|
|
170
|
+
minReaderModel: 2,
|
|
171
|
+
documentTypes: Object.freeze([ "definition", "instance" ]),
|
|
172
|
+
compatibility: "reader-floor",
|
|
173
|
+
applicability: "detectable",
|
|
174
|
+
summary: "String, text, and number values may carry persisted inclusive bounds."
|
|
175
|
+
}) ]);
|
|
176
|
+
|
|
177
|
+
function recordOf(value) {
|
|
178
|
+
return value !== null && typeof value == "object" && !Array.isArray(value) ? value : void 0;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function recordsAt(record, key) {
|
|
182
|
+
const value = record[key];
|
|
183
|
+
return Array.isArray(value) ? value.map(recordOf).filter(item => item !== void 0) : [];
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function nestedFieldEntries(entries) {
|
|
187
|
+
return entries.flatMap(entry => [ entry, ...nestedFieldEntries(recordsAt(entry, "fields")), ...nestedFieldEntries(recordsAt(entry, "of")) ]);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function parsedDefinitionSnapshot(root) {
|
|
191
|
+
if (typeof root.definitionSnapshot == "string") return recordOf(parseDefinitionSnapshotValue({
|
|
192
|
+
_id: typeof root._id == "string" ? root._id : "<unknown instance>",
|
|
193
|
+
definitionSnapshot: root.definitionSnapshot
|
|
194
|
+
}));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function persistedFieldEntries(document) {
|
|
198
|
+
const root = recordOf(document);
|
|
199
|
+
if (root === void 0) return [];
|
|
200
|
+
const snapshot = parsedDefinitionSnapshot(root), roots = snapshot === void 0 ? [ root ] : [ root, snapshot ], stages = roots.flatMap(candidate => recordsAt(candidate, "stages")), activities = stages.flatMap(stage => recordsAt(stage, "activities")), actions = activities.flatMap(activity => recordsAt(activity, "actions")), effects = actions.flatMap(action => recordsAt(action, "effects"));
|
|
201
|
+
return nestedFieldEntries([ ...roots.flatMap(candidate => recordsAt(candidate, "fields")), ...stages.flatMap(stage => recordsAt(stage, "fields")), ...activities.flatMap(activity => recordsAt(activity, "fields")), ...actions.flatMap(action => recordsAt(action, "params")), ...effects.flatMap(effect => recordsAt(effect, "outputs")) ]);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function hasChoiceList(document) {
|
|
205
|
+
return persistedFieldEntries(document).some(entry => {
|
|
206
|
+
const options = recordOf(entry.options);
|
|
207
|
+
return options !== void 0 && Array.isArray(options.list);
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function hasSubjectField(document) {
|
|
212
|
+
return persistedFieldEntries(document).some(entry => entry.type === "subject" || entry._type === "subject");
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
function hasActionSemantics(document) {
|
|
216
|
+
const root = recordOf(document);
|
|
217
|
+
return root === void 0 ? !1 : recordsAt(root, "stages").flatMap(stage => recordsAt(stage, "activities")).flatMap(activity => recordsAt(activity, "actions")).some(action => Array.isArray(action.semantics));
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function hasScalarValidation(document) {
|
|
221
|
+
return persistedFieldEntries(document).some(entry => {
|
|
222
|
+
const validation = recordOf(entry.validation);
|
|
223
|
+
return typeof validation?.min == "number" || typeof validation?.max == "number";
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const featureDetectors = {
|
|
228
|
+
"governed-model-stamps": () => !0,
|
|
229
|
+
"subject-field-kind": hasSubjectField,
|
|
230
|
+
"typed-scalar-choice-lists": hasChoiceList,
|
|
231
|
+
"action-semantics": hasActionSemantics,
|
|
232
|
+
"inclusive-scalar-bounds": hasScalarValidation
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
function requiredModelFeatures(documentType, document) {
|
|
236
|
+
return DATA_MODEL_CHANGES.filter(change => change.documentTypes.some(candidate => candidate === documentType) && featureDetectors[change.id](document));
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function requiredReaderModel(documentType, document) {
|
|
240
|
+
return Math.max(0, ...requiredModelFeatures(documentType, document).map(change => change.minReaderModel));
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function modelStampFor(args) {
|
|
244
|
+
return {
|
|
245
|
+
modelVersion: DATA_MODEL_VERSION,
|
|
246
|
+
minReaderModel: Math.max(args.storedMinReaderModel ?? 0, requiredReaderModel(args.documentType, args.document))
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function fieldTreeShape(value) {
|
|
251
|
+
if (Array.isArray(value)) return value.map(fieldTreeShape);
|
|
252
|
+
if (value === null) return "null";
|
|
253
|
+
if (typeof value == "object") {
|
|
254
|
+
const record = value;
|
|
255
|
+
return Object.fromEntries(Object.keys(record).toSorted().map(key => [ key, fieldTreeShape(record[key]) ]));
|
|
256
|
+
}
|
|
257
|
+
return typeof value;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function modelVersionOf(doc) {
|
|
261
|
+
const stamp = doc.modelVersion;
|
|
262
|
+
return typeof stamp == "number" ? stamp : 0;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function minReaderModelOf(doc) {
|
|
266
|
+
const floor = doc.minReaderModel;
|
|
267
|
+
return typeof floor == "number" ? floor : modelVersionOf(doc);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
class ModelVersionAheadError extends WorkflowError {
|
|
271
|
+
documentId;
|
|
272
|
+
documentModelVersion;
|
|
273
|
+
requiredReaderModel;
|
|
274
|
+
engineModelVersion;
|
|
275
|
+
constructor(args) {
|
|
276
|
+
super("model-version-ahead", `Document "${args.documentId}" was written by engine data model ${args.documentModelVersion} and requires a reader at model ${args.requiredReaderModel} or newer; this engine reads up to model ${DATA_MODEL_VERSION}. Upgrade @sanity/workflow-engine to a version that understands it.`),
|
|
277
|
+
this.name = "ModelVersionAheadError", this.documentId = args.documentId, this.documentModelVersion = args.documentModelVersion,
|
|
278
|
+
this.requiredReaderModel = args.requiredReaderModel, this.engineModelVersion = DATA_MODEL_VERSION;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
function assertReadableModel(doc) {
|
|
283
|
+
const documentReaderModel = minReaderModelOf(doc);
|
|
284
|
+
if (documentReaderModel > DATA_MODEL_VERSION) throw new ModelVersionAheadError({
|
|
285
|
+
documentId: doc._id,
|
|
286
|
+
documentModelVersion: modelVersionOf(doc),
|
|
287
|
+
requiredReaderModel: documentReaderModel
|
|
288
|
+
});
|
|
289
|
+
return doc;
|
|
290
|
+
}
|
|
291
|
+
|
|
7
292
|
function isCascadeFired(action) {
|
|
8
293
|
return action.when !== void 0;
|
|
9
294
|
}
|
|
@@ -24,16 +309,6 @@ function driverKind(actor) {
|
|
|
24
309
|
return actor.kind === "person" || actor.kind === "agent" ? actor.kind : "service";
|
|
25
310
|
}
|
|
26
311
|
|
|
27
|
-
function errorMessage(err) {
|
|
28
|
-
return (err instanceof Error ? err.message : String(err)).replace(/[^\P{Cc}\n\t]/gu, "");
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function rethrowWithContext(err, context) {
|
|
32
|
-
throw new Error(`${context}: ${errorMessage(err)}`, {
|
|
33
|
-
cause: err
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
|
|
37
312
|
function andConditions(parts) {
|
|
38
313
|
const present = parts.filter(p => p !== void 0);
|
|
39
314
|
if (present.length !== 0) return present.length === 1 ? present[0] : present.map(p => `(${p})`).join(" && ");
|
|
@@ -234,71 +509,6 @@ function isGdr(value) {
|
|
|
234
509
|
return typeof value == "object" && value !== null && typeof value.id == "string" && typeof value.type == "string";
|
|
235
510
|
}
|
|
236
511
|
|
|
237
|
-
class WorkflowError extends Error {
|
|
238
|
-
kind;
|
|
239
|
-
constructor(kind, message, options) {
|
|
240
|
-
super(message, options), this.kind = kind;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
class ContractViolationError extends WorkflowError {
|
|
245
|
-
constructor(message) {
|
|
246
|
-
super("contract-violation", message), this.name = "ContractViolationError";
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
class InstanceNotFoundError extends WorkflowError {
|
|
251
|
-
instanceId;
|
|
252
|
-
constructor(args) {
|
|
253
|
-
super("instance-not-found", `Workflow instance ${args.instanceId} not found${args.detail ? ` (${args.detail})` : ""}`),
|
|
254
|
-
this.name = "InstanceNotFoundError", this.instanceId = args.instanceId;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
class DefinitionNotFoundError extends WorkflowError {
|
|
259
|
-
definition;
|
|
260
|
-
version;
|
|
261
|
-
constructor(args) {
|
|
262
|
-
super("definition-not-found", args.version !== void 0 ? `Workflow definition ${args.definition} v${args.version} not deployed` : `Workflow definition ${args.definition} has no deployed versions`),
|
|
263
|
-
this.name = "DefinitionNotFoundError", this.definition = args.definition, args.version !== void 0 && (this.version = args.version);
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
class DefinitionInUseError extends WorkflowError {
|
|
268
|
-
definition;
|
|
269
|
-
blockedBy;
|
|
270
|
-
constructor(args) {
|
|
271
|
-
super("definition-in-use", definitionInUseMessage(args.definition, args.blockedBy)),
|
|
272
|
-
this.name = "DefinitionInUseError", this.definition = args.definition, this.blockedBy = args.blockedBy;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
function definitionInUseMessage(definition, blockedBy) {
|
|
277
|
-
if (blockedBy.reason === "non-terminal-instances") {
|
|
278
|
-
const head = blockedBy.instanceIds.slice(0, 3).join(", "), preview = blockedBy.instanceIds.length > 3 ? `${head}, …` : head;
|
|
279
|
-
return `Cannot delete ${definition}: ${blockedBy.instanceIds.length} non-terminal instance(s) exist (${preview}). Pass cascade to abort them first — instances are aborted in place, never deleted.`;
|
|
280
|
-
}
|
|
281
|
-
const names = blockedBy.referrers.map(r => `${r.definition} v${r.version}`).join(", ");
|
|
282
|
-
return `Cannot delete ${definition}: still spawn-referenced by deployed definition(s) ${names}. Delete or redeploy the referrer(s) first.`;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
class EffectNotFoundError extends WorkflowError {
|
|
286
|
-
instanceId;
|
|
287
|
-
effectKey;
|
|
288
|
-
settled;
|
|
289
|
-
constructor(args) {
|
|
290
|
-
super("effect-not-found", effectNotFoundMessage(args)), this.name = "EffectNotFoundError",
|
|
291
|
-
this.instanceId = args.instanceId, this.effectKey = args.effectKey, args.settled !== void 0 && (this.settled = args.settled);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
function effectNotFoundMessage(args) {
|
|
296
|
-
const base = `Pending effect "${args.effectKey}" not found on instance ${args.instanceId}`;
|
|
297
|
-
if (args.settled === void 0) return base;
|
|
298
|
-
const cause = args.settled.detail !== void 0 ? ` (${args.settled.detail})` : "";
|
|
299
|
-
return args.settled.status === "cancelled" ? `${base} — it was cancelled at ${args.settled.ranAt}${cause}` : `${base} — it already settled "${args.settled.status}" at ${args.settled.ranAt}${cause}`;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
512
|
const LAKE_ID_SEGMENT_RE = /^[a-z0-9][a-z0-9-]*$/, LAKE_ID_SEGMENT_GLOSS = "ASCII lowercase + digits + dashes, no leading dash, no dots";
|
|
303
513
|
|
|
304
514
|
function validateTag(tag) {
|
|
@@ -338,6 +548,7 @@ const isValidTag = asPredicate(validateTag), isValidAliasName = asPredicate(vali
|
|
|
338
548
|
resource: WorkflowResourceSchema
|
|
339
549
|
}), DefinitionSchema = v.custom(input => typeof input == "object" && input !== null && typeof input.name == "string", "expected a workflow definition (an object with a string `name`)"), DeploymentSchema = v.object({
|
|
340
550
|
name: NonEmptyString$1,
|
|
551
|
+
expectedMinReaderModel: v.optional(v.custom(() => !0), void 0),
|
|
341
552
|
tag: v.pipe(v.string(), v.nonEmpty(), v.check(isValidTag, "invalid tag — lowercase letters, digits and dashes only, no leading dash, no dots")),
|
|
342
553
|
workflowResource: WorkflowResourceSchema,
|
|
343
554
|
resourceAliases: v.optional(v.pipe(v.array(ResourceBindingSchema), v.check(bindings => new Set(bindings.map(binding => binding.name)).size === bindings.length, "duplicate resource handle name — each binding name must be unique within a deployment"))),
|
|
@@ -580,6 +791,8 @@ function desugarFieldEntry({entry: entry, path: path, ctx: ctx}) {
|
|
|
580
791
|
required: entry.required,
|
|
581
792
|
initialValue: entry.initialValue,
|
|
582
793
|
editable: editable,
|
|
794
|
+
options: entry.options,
|
|
795
|
+
validation: entry.validation,
|
|
583
796
|
types: entry.types,
|
|
584
797
|
fields: entry.fields,
|
|
585
798
|
of: entry.of
|
|
@@ -722,7 +935,7 @@ function desugarActivity({activity: activity, path: path, stageEnv: stageEnv, ct
|
|
|
722
935
|
};
|
|
723
936
|
}
|
|
724
937
|
|
|
725
|
-
const TARGET_DOC_KINDS = [ "doc.ref", "doc.refs", "release.ref" ];
|
|
938
|
+
const TARGET_DOC_KINDS = [ "doc.ref", "doc.refs", "subject", "release.ref" ];
|
|
726
939
|
|
|
727
940
|
function desugarTarget({target: target, env: env, path: path, ctx: ctx}) {
|
|
728
941
|
if (target === void 0 || target.type === "url") return target;
|
|
@@ -743,18 +956,15 @@ function desugarTarget({target: target, env: env, path: path, ctx: ctx}) {
|
|
|
743
956
|
};
|
|
744
957
|
}
|
|
745
958
|
|
|
746
|
-
function
|
|
747
|
-
|
|
748
|
-
action: action,
|
|
749
|
-
path: path,
|
|
750
|
-
env: env,
|
|
751
|
-
ctx: ctx
|
|
752
|
-
});
|
|
753
|
-
action.roles !== void 0 && action.roles.length === 0 && ctx.issues.push({
|
|
959
|
+
function reportEmptyActionRoles({action: action, path: path, ctx: ctx}) {
|
|
960
|
+
action.roles === void 0 || action.roles.length > 0 || ctx.issues.push({
|
|
754
961
|
path: [ ...path, "roles" ],
|
|
755
962
|
message: "roles: [] names no roles — omit it to allow any identity, or list at least one role"
|
|
756
963
|
});
|
|
757
|
-
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
function desugarActionOps(args) {
|
|
967
|
+
const {action: action, path: path, env: env, activityName: activityName, ctx: ctx} = args, ops = desugarOps({
|
|
758
968
|
ops: action.ops,
|
|
759
969
|
path: [ ...path, "ops" ],
|
|
760
970
|
env: env,
|
|
@@ -765,9 +975,32 @@ function desugarAction({action: action, path: path, env: env, activityName: acti
|
|
|
765
975
|
type: "status.set",
|
|
766
976
|
activity: activityName,
|
|
767
977
|
status: action.status
|
|
768
|
-
}),
|
|
978
|
+
}), ops;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
function desugarAction({action: action, path: path, env: env, activityName: activityName, ctx: ctx}) {
|
|
982
|
+
if ("type" in action) return desugarClaimAction({
|
|
983
|
+
action: action,
|
|
984
|
+
path: path,
|
|
985
|
+
env: env,
|
|
986
|
+
ctx: ctx
|
|
987
|
+
});
|
|
988
|
+
reportEmptyActionRoles({
|
|
989
|
+
action: action,
|
|
990
|
+
path: path,
|
|
991
|
+
ctx: ctx
|
|
992
|
+
});
|
|
993
|
+
const cascadeFired = isCascadeFired(action), filter = cascadeFired ? action.filter : andConditions([ rolesCondition(action.roles, ctx.roleAliases), action.filter ]), ops = desugarActionOps({
|
|
994
|
+
action: action,
|
|
995
|
+
path: path,
|
|
996
|
+
env: env,
|
|
997
|
+
activityName: activityName,
|
|
998
|
+
ctx: ctx
|
|
999
|
+
});
|
|
1000
|
+
return {
|
|
769
1001
|
...stripUndefined({
|
|
770
1002
|
name: action.name,
|
|
1003
|
+
semantics: action.semantics,
|
|
771
1004
|
title: action.title,
|
|
772
1005
|
description: action.description,
|
|
773
1006
|
group: normalizeGroup(action.group),
|
|
@@ -1154,7 +1387,7 @@ function isTerminalActivityStatus(status) {
|
|
|
1154
1387
|
return TERMINAL_ACTIVITY_STATUSES.includes(status);
|
|
1155
1388
|
}
|
|
1156
1389
|
|
|
1157
|
-
const FIELD_SCOPES = [ "workflow", "stage", "activity" ], DOCUMENT_VALUE_PERMISSIONS = [ "create", "read", "update" ], MUTATION_GUARD_ACTIONS = [ "create", "update", "delete", "publish", "unpublish" ], ACTIVITY_KINDS = [ "user", "service", "script", "manual", "receive" ], EXECUTOR_CLASSIFICATIONS = [ "autonomous", "interactive", "off-system", "hybrid" ], GROUP_KINDS = [ "core", "details" ], DRIVER_KINDS = [ "person", "agent", "service", "engine" ], CONDITION_VARS = [ {
|
|
1390
|
+
const ACTION_SEMANTICS = [ "decision.accept", "decision.decline" ], FIELD_SCOPES = [ "workflow", "stage", "activity" ], DOCUMENT_VALUE_PERMISSIONS = [ "create", "read", "update" ], MUTATION_GUARD_ACTIONS = [ "create", "update", "delete", "publish", "unpublish" ], ACTIVITY_KINDS = [ "user", "service", "script", "manual", "receive" ], EXECUTOR_CLASSIFICATIONS = [ "autonomous", "interactive", "off-system", "hybrid" ], GROUP_KINDS = [ "core", "details" ], DRIVER_KINDS = [ "person", "agent", "service", "engine" ], CONDITION_VARS = [ {
|
|
1158
1391
|
name: "self",
|
|
1159
1392
|
binding: "always",
|
|
1160
1393
|
label: "this workflow instance",
|
|
@@ -1163,7 +1396,7 @@ const FIELD_SCOPES = [ "workflow", "stage", "activity" ], DOCUMENT_VALUE_PERMISS
|
|
|
1163
1396
|
name: "fields",
|
|
1164
1397
|
binding: "always",
|
|
1165
1398
|
label: "the workflow's fields",
|
|
1166
|
-
description: "Declared field entries rendered by name (`$fields.<name>` is the value, no wrapper). Stage/activity scopes overlay lexically. What a read puts in your hand follows the declared kind: a singular `doc.ref` DEREFERENCES into the hydrated document (lake `_id`/`_type` plus content fields — which may themselves be named `id`/`type`), while `doc.refs` elements and `release.ref` stay REFERENCES — `{id, type[, releaseName]}` with `id` a GDR URI — because identity reads (membership, counting, joins) must stay total without hydrating every target, and targets may live in resources the evaluation cannot fetch from. Conditions evaluate against the in-memory snapshot only, so a dereferencing plural kind would silently hole wherever hydration lagged."
|
|
1399
|
+
description: "Declared field entries rendered by name (`$fields.<name>` is the value, no wrapper). Stage/activity scopes overlay lexically. What a read puts in your hand follows the declared kind: a singular `doc.ref` (or `subject`) DEREFERENCES into the hydrated document (lake `_id`/`_type` plus content fields — which may themselves be named `id`/`type`), while `doc.refs` elements and `release.ref` stay REFERENCES — `{id, type[, releaseName]}` with `id` a GDR URI — because identity reads (membership, counting, joins) must stay total without hydrating every target, and targets may live in resources the evaluation cannot fetch from. Conditions evaluate against the in-memory snapshot only, so a dereferencing plural kind would silently hole wherever hydration lagged."
|
|
1167
1400
|
}, {
|
|
1168
1401
|
name: "parent",
|
|
1169
1402
|
binding: "always",
|
|
@@ -1244,18 +1477,26 @@ const FIELD_SCOPES = [ "workflow", "stage", "activity" ], DOCUMENT_VALUE_PERMISS
|
|
|
1244
1477
|
binding: "always",
|
|
1245
1478
|
label: "the spawned subworkflows",
|
|
1246
1479
|
description: "Every row of the instance's subworkflow registry, faceted by `activity`/`action`/`definition`/`rowKey`/`status` (`'active'|'done'|'aborted'`) with `current` marking the open stage entry's cohort and `stage` the child's current stage. Usable anywhere — transition `when`s, requirements, any stage's gates; the settled gate is `count($subworkflows[activity == <name> && current && status == 'active']) == 0`."
|
|
1247
|
-
} ], RESERVED_CONDITION_VARS = CONDITION_VARS.map(v2 => v2.name), FILTER_SCOPE_VARS = CONDITION_VARS.filter(v2 => v2.binding === "always").map(v2 => v2.name), CALLER_BOUND_VARS = CONDITION_VARS.filter(v2 => v2.binding === "caller").map(v2 => v2.name), START_FILTER_VARS = [ {
|
|
1480
|
+
} ], SUBJECT_HAS_IN_FLIGHT_INSTANCE_VAR = "subjectHasInFlightInstance", RESERVED_CONDITION_VARS = [ ...CONDITION_VARS.map(v2 => v2.name), SUBJECT_HAS_IN_FLIGHT_INSTANCE_VAR ], FILTER_SCOPE_VARS = CONDITION_VARS.filter(v2 => v2.binding === "always").map(v2 => v2.name), CALLER_BOUND_VARS = CONDITION_VARS.filter(v2 => v2.binding === "caller").map(v2 => v2.name), START_FILTER_VARS = [ {
|
|
1248
1481
|
name: "tag",
|
|
1482
|
+
label: "this engine tag",
|
|
1249
1483
|
description: "The engine's tag partition — scope `*[...]` instance scans with `tag == $tag`."
|
|
1250
1484
|
}, {
|
|
1251
1485
|
name: "definition",
|
|
1486
|
+
label: "this workflow definition",
|
|
1252
1487
|
description: "The `name` of the definition under evaluation (its own start block binds it)."
|
|
1253
1488
|
}, {
|
|
1254
1489
|
name: "now",
|
|
1490
|
+
label: "the current time",
|
|
1255
1491
|
description: "The ISO clock reading of the evaluating engine."
|
|
1492
|
+
}, {
|
|
1493
|
+
name: SUBJECT_HAS_IN_FLIGHT_INSTANCE_VAR,
|
|
1494
|
+
label: "the subject already has an in-flight workflow",
|
|
1495
|
+
description: "Whether any instance in this engine tag, across all definitions, has the same resource-qualified subject and no `completedAt`. Advisory under concurrent starts."
|
|
1256
1496
|
} ], START_ALLOWED_VARS = [ ...START_FILTER_VARS, {
|
|
1257
1497
|
name: "fields",
|
|
1258
|
-
|
|
1498
|
+
label: "the start's input fields",
|
|
1499
|
+
description: "The caller's input entries by name (`initialFields` — at `startInstance`, the values the start would seed; at a pre-flight, the values gathered so far, so a read of a not-yet-supplied entry is GROQ null). Document references bind as GDR envelopes — `$fields.<entry>.id` is the GDR URI, never a string authors assemble — a singular `doc.ref` or `subject` included (nothing hydrates at the gate or the pre-flight). Pathed reads are deploy-checked against these envelope shapes."
|
|
1259
1500
|
} ], GUARD_PREDICATE_VARS = [ {
|
|
1260
1501
|
name: "guard",
|
|
1261
1502
|
description: "The guard document itself (its `metadata` carries deploy-time resolved values)."
|
|
@@ -1405,6 +1646,18 @@ function releaseRef({res: res, releaseName: releaseName}) {
|
|
|
1405
1646
|
};
|
|
1406
1647
|
}
|
|
1407
1648
|
|
|
1649
|
+
function isSingleDocRefKind(kind) {
|
|
1650
|
+
return kind === "doc.ref" || kind === "subject";
|
|
1651
|
+
}
|
|
1652
|
+
|
|
1653
|
+
function refKindAcceptsTypes(kind) {
|
|
1654
|
+
return isSingleDocRefKind(kind) || kind === "doc.refs";
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
function isSingleDocRefEntry(entry) {
|
|
1658
|
+
return isSingleDocRefKind(entry._type);
|
|
1659
|
+
}
|
|
1660
|
+
|
|
1408
1661
|
function isTodoListItem(row) {
|
|
1409
1662
|
if (typeof row != "object" || row === null) return !1;
|
|
1410
1663
|
const candidate = row, status = candidate.status;
|
|
@@ -1455,9 +1708,34 @@ const GdrUriSchema = v.custom(s => typeof s == "string" && isGdrUri(s), "must be
|
|
|
1455
1708
|
}), tolerantObject()({
|
|
1456
1709
|
type: v.literal("role"),
|
|
1457
1710
|
role: NonEmptyString
|
|
1458
|
-
}) ]), NullableString = v.union([ v.null(), v.string() ]), NullableNumber = v.union([ v.null(), v.number() ]), NullableBoolean = v.union([ v.null(), v.boolean() ]), NullableDateTime = v.union([ v.null(), IsoTimestamp ]), NullableDate = v.union([ v.null(), v.pipe(v.string(), v.regex(/^\d{4}-\d{2}-\d{2}$/, "must be a `YYYY-MM-DD` date")) ]), NullableUrl = NullableString,
|
|
1711
|
+
}) ]), NullableString = v.union([ v.null(), v.string() ]), NullableNumber = v.union([ v.null(), v.number() ]), NullableBoolean = v.union([ v.null(), v.boolean() ]), NullableDateTime = v.union([ v.null(), IsoTimestamp ]), NullableDate = v.union([ v.null(), v.pipe(v.string(), v.regex(/^\d{4}-\d{2}-\d{2}$/, "must be a `YYYY-MM-DD` date")) ]), NullableUrl = NullableString, CHOICE_KINDS = /* @__PURE__ */ new Set([ "string", "text", "number", "url", "date", "datetime", "dateTime" ]);
|
|
1712
|
+
|
|
1713
|
+
function normalizedChoiceKind(kind) {
|
|
1714
|
+
return kind === "dateTime" ? "datetime" : kind;
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
function checkChoiceList(args) {
|
|
1718
|
+
const {entryType: entryType, options: options, validation: validation} = args;
|
|
1719
|
+
if (options === void 0) return;
|
|
1720
|
+
if (!CHOICE_KINDS.has(entryType)) return [ `\`options\` is not valid on "${entryType}" values` ];
|
|
1721
|
+
const kind = normalizedChoiceKind(entryType), issues = options.list.flatMap((option, index) => checkValueAgainst({
|
|
1722
|
+
entryType: kind,
|
|
1723
|
+
value: option.value,
|
|
1724
|
+
validation: validation
|
|
1725
|
+
}, valueSchemas)?.map(issue => `at options.list.${index}.value: ${issue}`) ?? []), seen = /* @__PURE__ */ new Set;
|
|
1726
|
+
for (const option of options.list) seen.has(option.value) && issues.push(`duplicate option value ${JSON.stringify(option.value)}`),
|
|
1727
|
+
seen.add(option.value);
|
|
1728
|
+
return issues.length === 0 ? void 0 : issues;
|
|
1729
|
+
}
|
|
1730
|
+
|
|
1731
|
+
function choiceValueIssues(options, value) {
|
|
1732
|
+
if (!(options === void 0 || value === null || value === void 0)) return options.list.some(option => Object.is(option.value, value)) ? void 0 : [ `value ${JSON.stringify(value)} is not declared in \`options.list\`; expected one of ${options.list.map(option => JSON.stringify(option.value)).join(", ")}` ];
|
|
1733
|
+
}
|
|
1734
|
+
|
|
1735
|
+
const fieldValueSchemas = {
|
|
1459
1736
|
"doc.ref": v.union([ v.null(), GdrShape ]),
|
|
1460
1737
|
"doc.refs": v.array(GdrShape),
|
|
1738
|
+
subject: v.union([ v.null(), GdrShape ]),
|
|
1461
1739
|
"release.ref": v.union([ v.null(), ReleaseRefShape ]),
|
|
1462
1740
|
string: NullableString,
|
|
1463
1741
|
text: NullableString,
|
|
@@ -1475,7 +1753,56 @@ const GdrUriSchema = v.custom(s => typeof s == "string" && isGdrUri(s), "must be
|
|
|
1475
1753
|
};
|
|
1476
1754
|
|
|
1477
1755
|
function shapeValueSchema(shape, leaf) {
|
|
1478
|
-
|
|
1756
|
+
if (shape.type === "object") return objectSchema(shape.fields ?? [], leaf);
|
|
1757
|
+
if (shape.type === "array") return v.array(objectSchema(shape.of ?? [], leaf));
|
|
1758
|
+
const schema = leaf[shape.type] ?? v.any();
|
|
1759
|
+
return constrainedScalarSchema({
|
|
1760
|
+
schema: schema,
|
|
1761
|
+
entryType: shape.type,
|
|
1762
|
+
...shape
|
|
1763
|
+
});
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
function scalarMeasurement(entryType, value) {
|
|
1767
|
+
if (entryType === "number" && typeof value == "number") return value;
|
|
1768
|
+
if ((entryType === "string" || entryType === "text") && typeof value == "string") return value.length;
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
function scalarBoundIssue(args) {
|
|
1772
|
+
const {entryType: entryType, measured: measured, bound: bound, limit: limit} = args;
|
|
1773
|
+
return bound === void 0 || limit === "min" && measured >= bound || limit === "max" && measured <= bound ? void 0 : `${entryType === "number" ? "" : "length "}must be ${limit === "min" ? "greater than or equal to" : "less than or equal to"} ${bound}`;
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1776
|
+
function scalarValidationIssues(args) {
|
|
1777
|
+
const {entryType: entryType, validation: validation, value: value} = args;
|
|
1778
|
+
if (validation === void 0 || value === null || value === void 0) return;
|
|
1779
|
+
const measured = scalarMeasurement(entryType, value);
|
|
1780
|
+
if (measured === void 0) return;
|
|
1781
|
+
const issues = [ scalarBoundIssue({
|
|
1782
|
+
entryType: entryType,
|
|
1783
|
+
measured: measured,
|
|
1784
|
+
bound: validation.min,
|
|
1785
|
+
limit: "min"
|
|
1786
|
+
}), scalarBoundIssue({
|
|
1787
|
+
entryType: entryType,
|
|
1788
|
+
measured: measured,
|
|
1789
|
+
bound: validation.max,
|
|
1790
|
+
limit: "max"
|
|
1791
|
+
}) ].filter(issue => issue !== void 0);
|
|
1792
|
+
return issues.length === 0 ? void 0 : issues;
|
|
1793
|
+
}
|
|
1794
|
+
|
|
1795
|
+
function constrainedScalarSchema(args) {
|
|
1796
|
+
const {schema: schema, entryType: entryType, options: options, validation: validation} = args;
|
|
1797
|
+
return options === void 0 && validation === void 0 ? schema : v.pipe(schema, v.check(value => choiceValueIssues(options, value) === void 0 && scalarValidationIssues({
|
|
1798
|
+
entryType: entryType,
|
|
1799
|
+
validation: validation,
|
|
1800
|
+
value: value
|
|
1801
|
+
}) === void 0, issue => [ ...choiceValueIssues(options, issue.input) ?? [], ...scalarValidationIssues({
|
|
1802
|
+
entryType: entryType,
|
|
1803
|
+
validation: validation,
|
|
1804
|
+
value: issue.input
|
|
1805
|
+
}) ?? [] ].join("; ")));
|
|
1479
1806
|
}
|
|
1480
1807
|
|
|
1481
1808
|
function objectSchema(fields, leaf) {
|
|
@@ -1498,7 +1825,7 @@ function appendItemSchema(entryType, shape) {
|
|
|
1498
1825
|
function rejectedRefTypes(args) {
|
|
1499
1826
|
const {entryType: entryType, types: types, value: value} = args;
|
|
1500
1827
|
if (types === void 0 || value === null || value === void 0) return [];
|
|
1501
|
-
if (entryType
|
|
1828
|
+
if (!refKindAcceptsTypes(entryType)) return [];
|
|
1502
1829
|
let items = [ value ];
|
|
1503
1830
|
return entryType === "doc.refs" && (items = Array.isArray(value) ? value : []),
|
|
1504
1831
|
[ ...new Set(items.map(gdrTypeOf).filter(t => t !== void 0 && !types.includes(t))) ];
|
|
@@ -1533,6 +1860,10 @@ function checkValueAgainst(args, leaf) {
|
|
|
1533
1860
|
entryType: args.entryType,
|
|
1534
1861
|
types: args.types,
|
|
1535
1862
|
value: args.value
|
|
1863
|
+
}) ?? choiceValueIssues(args.options, args.value) ?? scalarValidationIssues({
|
|
1864
|
+
entryType: args.entryType,
|
|
1865
|
+
validation: args.validation,
|
|
1866
|
+
value: args.value
|
|
1536
1867
|
}) : formatIssues(result.issues);
|
|
1537
1868
|
}
|
|
1538
1869
|
|
|
@@ -1563,6 +1894,7 @@ const AuthoringRefId = v.pipe(v.string(), v.check(isAuthoringRefId, "must be a b
|
|
|
1563
1894
|
...valueSchemas,
|
|
1564
1895
|
"doc.ref": v.union([ v.null(), AuthoringGdrShape ]),
|
|
1565
1896
|
"doc.refs": v.array(AuthoringGdrShape),
|
|
1897
|
+
subject: v.union([ v.null(), AuthoringGdrShape ]),
|
|
1566
1898
|
"release.ref": v.union([ v.null(), v.looseObject({
|
|
1567
1899
|
id: AuthoringRefId,
|
|
1568
1900
|
type: v.literal("system.release"),
|
|
@@ -1602,10 +1934,10 @@ function validateFieldAppendItem(args) {
|
|
|
1602
1934
|
});
|
|
1603
1935
|
}
|
|
1604
1936
|
|
|
1605
|
-
function formatIssues(issues) {
|
|
1937
|
+
function formatIssues(issues, formatMessage = issue => issue.message) {
|
|
1606
1938
|
return issues.map(i => {
|
|
1607
1939
|
const keys = i.path?.map(p => p.key) ?? [];
|
|
1608
|
-
return `${keys.length > 0 ? `at ${keys.join(".")}: ` : ""}${i
|
|
1940
|
+
return `${keys.length > 0 ? `at ${keys.join(".")}: ` : ""}${formatMessage(i)}`;
|
|
1609
1941
|
});
|
|
1610
1942
|
}
|
|
1611
1943
|
|
|
@@ -1731,7 +2063,15 @@ function groupMembershipNames(group) {
|
|
|
1731
2063
|
return group === void 0 ? [] : typeof group == "string" ? [ group ] : [ ...group ];
|
|
1732
2064
|
}
|
|
1733
2065
|
|
|
1734
|
-
const FIELD_VALUE_KINDS = [ "doc.ref", "doc.refs", "release.ref", "string", "text", "number", "boolean", "date", "datetime", "url", "actor", "assignee", "assignees", "object", "array" ], FieldValueKindSchema = picklist(FIELD_VALUE_KINDS), FieldKindSchema = picklist(FIELD_VALUE_KINDS), FieldEntryName = groqIdentifier("`$fields.<name>`")
|
|
2066
|
+
const FIELD_VALUE_KINDS = [ "doc.ref", "doc.refs", "subject", "release.ref", "string", "text", "number", "boolean", "date", "datetime", "url", "actor", "assignee", "assignees", "object", "array" ], FieldValueKindSchema = picklist(FIELD_VALUE_KINDS), FieldKindSchema = picklist(FIELD_VALUE_KINDS), FieldEntryName = groqIdentifier("`$fields.<name>`"), FiniteNumber = v.pipe(v.number(), v.finite("must be finite")), ScalarValidationSchema = v.pipe(v.strictObject({
|
|
2067
|
+
min: v.optional(FiniteNumber),
|
|
2068
|
+
max: v.optional(FiniteNumber)
|
|
2069
|
+
}), v.check(validation => validation.min !== void 0 || validation.max !== void 0, "declare at least one bound, or omit `validation`"), v.check(validation => validation.min === void 0 || validation.max === void 0 || validation.min <= validation.max, "`min` must be less than or equal to `max`")), ChoiceOptionsSchema = v.strictObject({
|
|
2070
|
+
list: v.pipe(v.array(v.strictObject({
|
|
2071
|
+
title: NonEmpty,
|
|
2072
|
+
value: v.union([ v.string(), v.number() ])
|
|
2073
|
+
})), v.minLength(1, "declare at least one choice, or omit `options`"))
|
|
2074
|
+
});
|
|
1735
2075
|
|
|
1736
2076
|
function asShape(input) {
|
|
1737
2077
|
return typeof input == "object" && input !== null ? input : {};
|
|
@@ -1765,14 +2105,16 @@ function compositeChecked(entries) {
|
|
|
1765
2105
|
return v.pipe(v.strictObject(entries), v.check(input => compositeShapeOk(input), issue => compositeShapeMessage(issue.input)), v.check(input => duplicateSubfieldName(input) === void 0, issue => `duplicate sub-field name "${duplicateSubfieldName(issue.input)}" — sub-field names must be unique within \`fields\` / \`of\``));
|
|
1766
2106
|
}
|
|
1767
2107
|
|
|
1768
|
-
const FieldShapeSchema = v.lazy(() => compositeChecked({
|
|
2108
|
+
const FieldShapeSchema = v.lazy(() => v.pipe(compositeChecked({
|
|
1769
2109
|
type: FieldValueKindSchema,
|
|
1770
2110
|
name: FieldEntryName,
|
|
1771
2111
|
title: v.optional(v.string()),
|
|
1772
2112
|
description: v.optional(v.string()),
|
|
2113
|
+
options: v.optional(ChoiceOptionsSchema),
|
|
2114
|
+
validation: v.optional(ScalarValidationSchema),
|
|
1773
2115
|
fields: v.optional(v.array(FieldShapeSchema)),
|
|
1774
2116
|
of: v.optional(v.array(FieldShapeSchema))
|
|
1775
|
-
})), StoredEditableSchema = v.union([ v.literal(!0), NonEmpty ]), AuthoringEditableSchema = v.union([ v.literal(!0), v.array(NonEmpty), NonEmpty ]);
|
|
2117
|
+
}), choiceOptionsCheck(), scalarValidationCheck())), StoredEditableSchema = v.union([ v.literal(!0), NonEmpty ]), AuthoringEditableSchema = v.union([ v.literal(!0), v.array(NonEmpty), NonEmpty ]);
|
|
1776
2118
|
|
|
1777
2119
|
function fieldBase(editable, group) {
|
|
1778
2120
|
return {
|
|
@@ -1790,6 +2132,8 @@ function fieldEntryFields(editable, group) {
|
|
|
1790
2132
|
return {
|
|
1791
2133
|
type: FieldKindSchema,
|
|
1792
2134
|
...fieldBase(editable, group),
|
|
2135
|
+
options: v.optional(ChoiceOptionsSchema),
|
|
2136
|
+
validation: v.optional(ScalarValidationSchema),
|
|
1793
2137
|
types: v.optional(v.pipe(v.array(NonEmpty), v.minLength(1, "declare at least one accepted type, or omit `types` to accept any"))),
|
|
1794
2138
|
fields: v.optional(v.array(FieldShapeSchema)),
|
|
1795
2139
|
of: v.optional(v.array(FieldShapeSchema))
|
|
@@ -1802,7 +2146,9 @@ function literalSeedIssues(entry) {
|
|
|
1802
2146
|
value: entry.initialValue.value,
|
|
1803
2147
|
types: entry.types,
|
|
1804
2148
|
fields: entry.fields,
|
|
1805
|
-
of: entry.of
|
|
2149
|
+
of: entry.of,
|
|
2150
|
+
options: entry.options,
|
|
2151
|
+
validation: entry.validation
|
|
1806
2152
|
});
|
|
1807
2153
|
}
|
|
1808
2154
|
|
|
@@ -1811,10 +2157,37 @@ function literalSeedCheck() {
|
|
|
1811
2157
|
}
|
|
1812
2158
|
|
|
1813
2159
|
function refTypesCheck() {
|
|
1814
|
-
return v.check(entry => entry.types === void 0 || entry.type
|
|
2160
|
+
return v.check(entry => entry.types === void 0 || refKindAcceptsTypes(entry.type), issue => `\`types\` is only valid on \`doc.ref\` / \`doc.refs\` / \`subject\` entries, not "${issue.input.type}"`);
|
|
2161
|
+
}
|
|
2162
|
+
|
|
2163
|
+
function choiceOptionsCheck() {
|
|
2164
|
+
return v.check(entry => checkChoiceList({
|
|
2165
|
+
entryType: entry.type,
|
|
2166
|
+
options: entry.options,
|
|
2167
|
+
validation: entry.validation
|
|
2168
|
+
}) === void 0, issue => (checkChoiceList({
|
|
2169
|
+
entryType: issue.input.type,
|
|
2170
|
+
options: issue.input.options,
|
|
2171
|
+
validation: issue.input.validation
|
|
2172
|
+
}) ?? []).join("; "));
|
|
2173
|
+
}
|
|
2174
|
+
|
|
2175
|
+
const SCALAR_VALIDATION_KINDS = /* @__PURE__ */ new Set([ "string", "text", "number" ]);
|
|
2176
|
+
|
|
2177
|
+
function scalarValidationCheck() {
|
|
2178
|
+
return v.check(entry => scalarValidationDeclarationIssues(entry) === void 0, issue => (scalarValidationDeclarationIssues(issue.input) ?? []).join("; "));
|
|
1815
2179
|
}
|
|
1816
2180
|
|
|
1817
|
-
|
|
2181
|
+
function scalarValidationDeclarationIssues(entry) {
|
|
2182
|
+
const {type: type, validation: validation} = entry;
|
|
2183
|
+
if (validation === void 0) return;
|
|
2184
|
+
if (!SCALAR_VALIDATION_KINDS.has(type)) return [ `\`validation\` is only valid on \`string\` / \`text\` / \`number\` values, not "${type}"` ];
|
|
2185
|
+
if (type === "number") return;
|
|
2186
|
+
const issues = Object.entries(validation).flatMap(([bound, value]) => Number.isInteger(value) && value >= 0 ? [] : [ `\`validation.${bound}\` must be a non-negative integer for ${type} length` ]);
|
|
2187
|
+
return issues.length === 0 ? void 0 : issues;
|
|
2188
|
+
}
|
|
2189
|
+
|
|
2190
|
+
const FieldEntrySchema = pinned()(v.pipe(compositeChecked(fieldEntryFields(StoredEditableSchema, StoredGroupMembershipSchema)), refTypesCheck(), choiceOptionsCheck(), scalarValidationCheck(), literalSeedCheck())), RawAuthoringFieldEntrySchema = pinned()(v.pipe(compositeChecked(fieldEntryFields(AuthoringEditableSchema, AuthoringGroupMembershipSchema)), refTypesCheck(), choiceOptionsCheck(), scalarValidationCheck(), literalSeedCheck())), ClaimFieldSchema = pinned()(v.strictObject({
|
|
1818
2191
|
type: v.literal("claim"),
|
|
1819
2192
|
name: FieldEntryName,
|
|
1820
2193
|
title: v.optional(v.string()),
|
|
@@ -1845,17 +2218,25 @@ const TodoListFieldSchema = pinned()(v.strictObject(listSugarFields("todoList"))
|
|
|
1845
2218
|
with: v.optional(v.record(NonEmpty, ConditionSchema)),
|
|
1846
2219
|
context: v.optional(v.record(NonEmpty, ConditionSchema)),
|
|
1847
2220
|
onExit: v.optional(picklist([ "detach", "abort" ]))
|
|
1848
|
-
}), ActionParamSchema = v.strictObject({
|
|
2221
|
+
}), ActionParamSchema = v.pipe(v.strictObject({
|
|
1849
2222
|
type: picklist([ "string", "number", "boolean", "url", "dateTime", "actor", "doc.ref", "doc.refs", "json" ]),
|
|
1850
2223
|
name: NonEmpty,
|
|
1851
2224
|
title: v.optional(v.string()),
|
|
1852
2225
|
description: v.optional(v.string()),
|
|
1853
|
-
required: v.optional(v.boolean())
|
|
1854
|
-
|
|
2226
|
+
required: v.optional(v.boolean()),
|
|
2227
|
+
options: v.optional(ChoiceOptionsSchema),
|
|
2228
|
+
validation: v.optional(ScalarValidationSchema)
|
|
2229
|
+
}), choiceOptionsCheck(), scalarValidationCheck());
|
|
2230
|
+
|
|
2231
|
+
function hasUniqueSemanticNamespaces(semantics) {
|
|
2232
|
+
const namespaces = semantics.map(semantic => semantic.split(".", 1)[0]);
|
|
2233
|
+
return new Set(namespaces).size === namespaces.length;
|
|
2234
|
+
}
|
|
1855
2235
|
|
|
1856
2236
|
function actionFields(op, group) {
|
|
1857
2237
|
return {
|
|
1858
2238
|
name: NonEmpty,
|
|
2239
|
+
semantics: v.optional(v.pipe(v.array(picklist(ACTION_SEMANTICS)), v.minLength(1, "declare at least one semantic, or omit `semantics`"), v.check(hasUniqueSemanticNamespaces, "declare at most one semantic from each namespace"))),
|
|
1859
2240
|
title: v.optional(v.string()),
|
|
1860
2241
|
description: v.optional(v.string()),
|
|
1861
2242
|
group: v.optional(group),
|
|
@@ -2044,7 +2425,7 @@ function startKindOf(definition) {
|
|
|
2044
2425
|
}
|
|
2045
2426
|
|
|
2046
2427
|
function isSubjectEntry(entry) {
|
|
2047
|
-
return entry.
|
|
2428
|
+
return entry.type === "subject";
|
|
2048
2429
|
}
|
|
2049
2430
|
|
|
2050
2431
|
function isInputSourced(entry) {
|
|
@@ -2546,6 +2927,52 @@ function checkAssigneesEntries(def, issues) {
|
|
|
2546
2927
|
});
|
|
2547
2928
|
}
|
|
2548
2929
|
|
|
2930
|
+
function nestedSubjectPaths(shapes, base) {
|
|
2931
|
+
return (shapes ?? []).flatMap((shape, i) => isSubjectEntry(shape) ? [ [ ...base, i, "type" ] ] : [ ...nestedSubjectPaths(shape.fields, [ ...base, i, "fields" ]), ...nestedSubjectPaths(shape.of, [ ...base, i, "of" ]) ]);
|
|
2932
|
+
}
|
|
2933
|
+
|
|
2934
|
+
function checkSubjectEffectOutputs(def, issues) {
|
|
2935
|
+
for (const [i, stage] of def.stages.entries()) for (const [j, activity] of (stage.activities ?? []).entries()) for (const [a, action] of (activity.actions ?? []).entries()) pushSubjectOutputIssues({
|
|
2936
|
+
action: action,
|
|
2937
|
+
path: [ "stages", i, "activities", j, "actions", a ],
|
|
2938
|
+
issues: issues
|
|
2939
|
+
});
|
|
2940
|
+
}
|
|
2941
|
+
|
|
2942
|
+
function pushSubjectOutputIssues(args) {
|
|
2943
|
+
const {action: action, path: path, issues: issues} = args;
|
|
2944
|
+
for (const [e, effect] of (action.effects ?? []).entries()) {
|
|
2945
|
+
const base = [ ...path, "effects", e, "outputs" ];
|
|
2946
|
+
for (const nestedPath of nestedSubjectPaths(effect.outputs, base)) issues.push({
|
|
2947
|
+
path: nestedPath,
|
|
2948
|
+
message: `effect "${effect.name}" declares a \`subject\` output shape — no subject reader ever looks at effect outputs. Declare \`doc.ref\` for a document-valued output`
|
|
2949
|
+
});
|
|
2950
|
+
}
|
|
2951
|
+
}
|
|
2952
|
+
|
|
2953
|
+
function checkSubjectEntries(def, issues) {
|
|
2954
|
+
for (const {entries: entries, scope: scope, path: path, label: label} of fieldScopes(def)) {
|
|
2955
|
+
const subjects = (entries ?? []).flatMap((entry, n) => isSubjectEntry(entry) ? [ {
|
|
2956
|
+
entry: entry,
|
|
2957
|
+
n: n
|
|
2958
|
+
} ] : []);
|
|
2959
|
+
if (scope !== "workflow") for (const {entry: entry, n: n} of subjects) issues.push({
|
|
2960
|
+
path: [ ...path, n, "type" ],
|
|
2961
|
+
message: `${label} entry "${entry.name}" is a \`subject\` — the subject names the document the WORKFLOW is about, so it is valid only on a workflow-scope entry. Declare \`doc.ref\` for a plain reference here`
|
|
2962
|
+
}); else for (const {n: n} of subjects.slice(1)) issues.push({
|
|
2963
|
+
path: [ ...path, n ],
|
|
2964
|
+
message: "at most one subject-kind field entry per workflow — the runtime identifies THE subject by kind, and a second one makes it ambiguous"
|
|
2965
|
+
});
|
|
2966
|
+
for (const [n, entry] of (entries ?? []).entries()) {
|
|
2967
|
+
const nested = [ ...nestedSubjectPaths(entry.fields, [ ...path, n, "fields" ]), ...nestedSubjectPaths(entry.of, [ ...path, n, "of" ]) ];
|
|
2968
|
+
for (const nestedPath of nested) issues.push({
|
|
2969
|
+
path: nestedPath,
|
|
2970
|
+
message: `${label} entry "${entry.name}" declares a \`subject\` sub-field — a nested subject can never be read as the workflow's subject. Declare \`doc.ref\` for a nested reference`
|
|
2971
|
+
});
|
|
2972
|
+
}
|
|
2973
|
+
}
|
|
2974
|
+
}
|
|
2975
|
+
|
|
2549
2976
|
function checkActivityTerminalPaths(def, issues) {
|
|
2550
2977
|
for (const [i, stage] of def.stages.entries()) {
|
|
2551
2978
|
const resolvable = terminallyResolvableActivities(stage);
|
|
@@ -2596,12 +3023,30 @@ function checkStart(def, issues) {
|
|
|
2596
3023
|
if (def.start !== void 0 && (def.lifecycle === "child" && issues.push({
|
|
2597
3024
|
path: [ "start" ],
|
|
2598
3025
|
message: "a spawn-only (lifecycle 'child') definition declares `start` — children are instantiated by a parent's `spawn`, never started standalone, so the block would never apply. Remove `start`, or drop `lifecycle: 'child'`"
|
|
2599
|
-
}), checkStartFilterReads(def, issues), checkStartAllowedReads(def, issues), def
|
|
3026
|
+
}), checkStartFilterReads(def, issues), checkStartAllowedReads(def, issues), checkStartSubjectVariable(def, issues),
|
|
3027
|
+
def.start.kind === "autonomous")) for (const [n, entry] of (def.fields ?? []).entries()) entry.required !== !0 || isSubjectEntry(entry) || issues.push({
|
|
2600
3028
|
path: [ "fields", n, "required" ],
|
|
2601
|
-
message: `start.kind 'autonomous' means runs are initiated by a system reacting to a document, so every required input must be derivable from that triggering document — required entry "${entry.name}" (kind "${entry.type}") is not
|
|
3029
|
+
message: `start.kind 'autonomous' means runs are initiated by a system reacting to a document, so every required input must be derivable from that triggering document — required entry "${entry.name}" (kind "${entry.type}") is not the workflow's subject. Make it optional, seed it another way (query/literal), or declare it the \`subject\` entry (the document the run is about)`
|
|
2602
3030
|
});
|
|
2603
3031
|
}
|
|
2604
3032
|
|
|
3033
|
+
function checkStartSubjectVariable(def, issues) {
|
|
3034
|
+
const subject = (def.fields ?? []).find(isSubjectEntry);
|
|
3035
|
+
for (const [key, condition] of [ [ "filter", def.start?.filter ], [ "allowed", def.start?.allowed ] ]) if (condition !== void 0 && conditionParameterNames(condition).has(SUBJECT_HAS_IN_FLIGHT_INSTANCE_VAR)) {
|
|
3036
|
+
if (subject === void 0) {
|
|
3037
|
+
issues.push({
|
|
3038
|
+
path: [ "start", key ],
|
|
3039
|
+
message: `start.${key} reads $${SUBJECT_HAS_IN_FLIGHT_INSTANCE_VAR}, but the definition declares no \`subject\` entry — the engine has no prospective subject identity to match. Declare a \`subject\` entry (the document the workflow is about), or remove the variable`
|
|
3040
|
+
});
|
|
3041
|
+
continue;
|
|
3042
|
+
}
|
|
3043
|
+
key === "allowed" && !isInputSourced(subject) && issues.push({
|
|
3044
|
+
path: [ "start", key ],
|
|
3045
|
+
message: `start.allowed reads $${SUBJECT_HAS_IN_FLIGHT_INSTANCE_VAR}, but the definition's subject entry "${subject.name}" is not \`input\`-sourced — the start gate cannot bind its prospective subject. Make the subject input-sourced, or remove the variable`
|
|
3046
|
+
});
|
|
3047
|
+
}
|
|
3048
|
+
}
|
|
3049
|
+
|
|
2605
3050
|
function checkStartFilterReads(def, issues) {
|
|
2606
3051
|
const filter = def.start?.filter;
|
|
2607
3052
|
filter !== void 0 && (conditionParameterNames(filter).has("fields") && issues.push({
|
|
@@ -2609,7 +3054,7 @@ function checkStartFilterReads(def, issues) {
|
|
|
2609
3054
|
message: "start.filter reads $fields, but the filter is browse-time-pure — a start surface evaluates it per document, before any inputs exist, so $fields cannot be bound. Move the input-dependent rule to start.allowed, the start-time permission predicate that binds $fields and is enforced by startInstance"
|
|
2610
3055
|
}), readsRootDocument(filter) && !(def.fields ?? []).some(isSubjectEntry) && issues.push({
|
|
2611
3056
|
path: [ "start", "filter" ],
|
|
2612
|
-
message: "start.filter reads the candidate document (its root), but the definition declares no
|
|
3057
|
+
message: "start.filter reads the candidate document (its root), but the definition declares no `subject` entry — a read surface would never have a document to bind as root, so every root read is GROQ null and the filter silently misevaluates. Declare a `subject` entry (the document the workflow is about), or gate on the dataset instead"
|
|
2613
3058
|
}));
|
|
2614
3059
|
}
|
|
2615
3060
|
|
|
@@ -2926,7 +3371,7 @@ function checkFieldReadOpValues(def, issues) {
|
|
|
2926
3371
|
ops: site.ops,
|
|
2927
3372
|
path: site.path,
|
|
2928
3373
|
label: site.label,
|
|
2929
|
-
activityEntries:
|
|
3374
|
+
activityEntries: site.activity.fields ?? []
|
|
2930
3375
|
});
|
|
2931
3376
|
}
|
|
2932
3377
|
|
|
@@ -2948,6 +3393,9 @@ function fieldReadsIn(value, path) {
|
|
|
2948
3393
|
|
|
2949
3394
|
function checkOpFieldRead({read: read, where: where, path: path, workflowEntries: workflowEntries, stageEntries: stageEntries, activityEntries: activityEntries, issues: issues}) {
|
|
2950
3395
|
const hosts = [ {
|
|
3396
|
+
scope: "activity",
|
|
3397
|
+
entries: activityEntries
|
|
3398
|
+
}, {
|
|
2951
3399
|
scope: "stage",
|
|
2952
3400
|
entries: stageEntries
|
|
2953
3401
|
}, {
|
|
@@ -2960,8 +3408,7 @@ function checkOpFieldRead({read: read, where: where, path: path, workflowEntries
|
|
|
2960
3408
|
message: opFieldReadMissMessage({
|
|
2961
3409
|
read: read,
|
|
2962
3410
|
where: where,
|
|
2963
|
-
hosts: hosts
|
|
2964
|
-
activityEntries: activityEntries
|
|
3411
|
+
hosts: hosts
|
|
2965
3412
|
})
|
|
2966
3413
|
});
|
|
2967
3414
|
return;
|
|
@@ -2975,9 +3422,9 @@ function checkOpFieldRead({read: read, where: where, path: path, workflowEntries
|
|
|
2975
3422
|
});
|
|
2976
3423
|
}
|
|
2977
3424
|
|
|
2978
|
-
function opFieldReadMissMessage({read: read, where: where, hosts: hosts
|
|
2979
|
-
const searched = read.scope === void 0 ? "stage or workflow scope" : `${read.scope} scope`,
|
|
2980
|
-
return `${where} reads field "${read.field}", which is not declared at ${searched} — the read resolves to undefined at op time, so the write silently lands empty
|
|
3425
|
+
function opFieldReadMissMessage({read: read, where: where, hosts: hosts}) {
|
|
3426
|
+
const searched = read.scope === void 0 ? "activity, stage, or workflow scope" : `${read.scope} scope`, known = hosts.flatMap(host => host.entries.map(entry => `${host.scope}:${entry.name}`));
|
|
3427
|
+
return `${where} reads field "${read.field}", which is not declared at ${searched} — the read resolves to undefined at op time, so the write silently lands empty. Known: ${known.join(", ") || "(none)"}`;
|
|
2981
3428
|
}
|
|
2982
3429
|
|
|
2983
3430
|
function checkUpdateWhereOps(def, issues) {
|
|
@@ -3140,6 +3587,7 @@ const SCALAR = {
|
|
|
3140
3587
|
kind: "list",
|
|
3141
3588
|
item: GDR_VALUE
|
|
3142
3589
|
}),
|
|
3590
|
+
subject: () => DOC_CONTENT,
|
|
3143
3591
|
"release.ref": () => RELEASE_VALUE,
|
|
3144
3592
|
string: () => SCALAR,
|
|
3145
3593
|
text: () => SCALAR,
|
|
@@ -3164,7 +3612,8 @@ const SCALAR = {
|
|
|
3164
3612
|
})
|
|
3165
3613
|
}, START_ALLOWED_VALUE_NODES = {
|
|
3166
3614
|
...VALUE_NODES,
|
|
3167
|
-
"doc.ref": () => GDR_VALUE
|
|
3615
|
+
"doc.ref": () => GDR_VALUE,
|
|
3616
|
+
subject: () => GDR_VALUE
|
|
3168
3617
|
};
|
|
3169
3618
|
|
|
3170
3619
|
function valueNodeFor(shape, nodes) {
|
|
@@ -3236,9 +3685,10 @@ function checkWorkflowInvariants(def) {
|
|
|
3236
3685
|
checkRequiredField(def, issues), checkStart(def, issues), checkPredicates(def, issues),
|
|
3237
3686
|
checkUnboundConditionVars(def, issues), checkConditionFieldReads(def, issues), checkFieldReadSeeds(def, issues),
|
|
3238
3687
|
checkFieldReadOpValues(def, issues), checkUpdateWhereOps(def, issues), checkGuardFieldReads(def, issues),
|
|
3239
|
-
checkAssigneesEntries(def, issues),
|
|
3688
|
+
checkAssigneesEntries(def, issues), checkSubjectEntries(def, issues), checkSubjectEffectOutputs(def, issues),
|
|
3689
|
+
checkActivityTerminalPaths(def, issues), checkTerminalStageActivities(def, issues),
|
|
3240
3690
|
checkTriggeredActionParams(def, issues), checkStoredRolesPlacement(def, issues),
|
|
3241
3691
|
checkGroups(def, issues), issues;
|
|
3242
3692
|
}
|
|
3243
3693
|
|
|
3244
|
-
export { ACTIVITY_KINDS, ACTIVITY_STATUSES, ACTOR_KINDS, ActorShape, AuthoringActionSchema, AuthoringActivitySchema, AuthoringFieldEntrySchema, AuthoringGuardSchema, AuthoringOpSchema, AuthoringStageSchema, AuthoringTransitionSchema, AuthoringWorkflowSchema, CALLER_BOUND_VARS, CONDITION_VARS, ContractViolationError, DEFAULT_TRANSITION_WHEN, DOCUMENT_VALUE_PERMISSIONS, DRIVER_KINDS, DefinitionInUseError, DefinitionNotFoundError, EFFECTS_READ, EXECUTOR_CLASSIFICATIONS, EffectNotFoundError, EffectSchema, FIELD_READ, FIELD_SCOPES, FIELD_VALUE_KINDS, FILTER_SCOPE_VARS, FieldValueShapeError, GROUP_KINDS, GUARD_PREDICATE_VARS, GdrShape, GroupSchema, InstanceNotFoundError, IsoTimestamp, MUTATION_GUARD_ACTIONS, NonEmptyString, PersistedDocShapeError, RESERVED_CONDITION_VARS, RESOURCE_ALIAS_NAME_SOURCE, START_ALLOWED_VARS, START_FILTER_VARS, StoredFieldOpSchema, WORKFLOW_DEFINITION_TYPE, WorkflowConfigSchema, WorkflowError, actorFulfillsRole, andConditions, checkFieldValue, checkWorkflowInvariants, clientConfigFromResource, conditionEffectReads, conditionFieldReadNames, conditionParameterNames, conditionSyntaxIssues, datasetResourceParts, definitionDocId, deriveActivityKind, deriveExecutorClassification, desugarWorkflow, driverKind, errorMessage, evaluateCondition, evaluateConditionOutcome, evaluatePredicates, extractDocumentId, fieldValueSchemas, formatIssuePath, formatIssues, formatValidationError, gdrFromResource, gdrRef, gdrResourcePrefix, gdrUri, groq, groupMembershipNames, isBareSeedId, isCascadeFired, isGdr, isGdrUri, isGuardReadExpr, isInputSourced, isNotesEntry, isParseableInstant, isStartableDefinition, isSubjectEntry, isTerminalActivityStatus, isTodoListEntry, isTodoListItem, isUnevaluable, labelFor, parseGdr, parseOrThrow, parsePersistedDoc, parseResourceGdr, parseStoredDefinition, readsRootDocument, refCanvas, refDashboard, refDataset, refMediaLibrary, refTypeIssues, rejectedRefTypes, releaseDocId, releaseRef, resourceAliasesToMap, resourceFromGdrUri, resourceFromParsed, resourceGdr, rethrowWithContext, runGroq, sameResource, schemaTreeShape, selfGdr, startKindOf, tagScopeFilter, toBareId, toPhysicalGdr, tolerantEntries, tolerantObject, tryParseGdr, validateFieldAppendItem, validateFieldValue, validateResourceAliasName, validateTag };
|
|
3694
|
+
export { ACTION_SEMANTICS, ACTIVITY_KINDS, ACTIVITY_STATUSES, ACTOR_KINDS, ActorShape, AuthoringActionSchema, AuthoringActivitySchema, AuthoringFieldEntrySchema, AuthoringGuardSchema, AuthoringOpSchema, AuthoringStageSchema, AuthoringTransitionSchema, AuthoringWorkflowSchema, CALLER_BOUND_VARS, CONDITION_VARS, ContractViolationError, DATA_MODEL_CHANGES, DATA_MODEL_MIN_READER, DATA_MODEL_VERSION, DEFAULT_TRANSITION_WHEN, DOCUMENT_VALUE_PERMISSIONS, DRIVER_KINDS, DefinitionInUseError, DefinitionNotFoundError, EFFECTS_READ, EXECUTOR_CLASSIFICATIONS, EffectNotFoundError, EffectSchema, FIELD_READ, FIELD_SCOPES, FIELD_VALUE_KINDS, FILTER_SCOPE_VARS, FieldValueShapeError, GROUP_KINDS, GUARD_PREDICATE_VARS, GdrShape, GroupSchema, InstanceNotFoundError, IsoTimestamp, MUTATION_GUARD_ACTIONS, ModelVersionAheadError, NonEmptyString, PersistedDocShapeError, READER_MODEL_ROLLOUT_URL, RESERVED_CONDITION_VARS, RESOURCE_ALIAS_NAME_SOURCE, ReaderModelAcknowledgementError, START_ALLOWED_VARS, START_FILTER_VARS, SUBJECT_HAS_IN_FLIGHT_INSTANCE_VAR, SpawnContractsInvalidError, StoredFieldOpSchema, WORKFLOW_DEFINITION_TYPE, WORKFLOW_INSTANCE_TYPE, WorkflowConfigSchema, WorkflowError, actorFulfillsRole, andConditions, assertReadableModel, assertReaderModelAcknowledgement, checkFieldValue, checkWorkflowInvariants, choiceValueIssues, clientConfigFromResource, conditionEffectReads, conditionFieldReadNames, conditionParameterNames, conditionSyntaxIssues, datasetResourceParts, definitionDocId, deriveActivityKind, deriveExecutorClassification, desugarWorkflow, driverKind, errorMessage, evaluateCondition, evaluateConditionOutcome, evaluatePredicates, extractDocumentId, fieldTreeShape, fieldValueSchemas, formatIssuePath, formatIssues, formatValidationError, gdrFromResource, gdrRef, gdrResourcePrefix, gdrUri, groq, groupMembershipNames, isBareSeedId, isCascadeFired, isGdr, isGdrUri, isGuardReadExpr, isInputSourced, isNotesEntry, isParseableInstant, isSingleDocRefEntry, isSingleDocRefKind, isStartableDefinition, isSubjectEntry, isTerminalActivityStatus, isTodoListEntry, isTodoListItem, isUnevaluable, isUnprimed, labelFor, minReaderModelOf, modelStampFor, modelVersionOf, parentRef, parseDefinitionSnapshot, parseGdr, parseOrThrow, parsePersistedDoc, parseResourceGdr, parseStoredDefinition, readsRootDocument, refCanvas, refDashboard, refDataset, refKindAcceptsTypes, refMediaLibrary, refTypeIssues, rejectedRefTypes, releaseDocId, releaseRef, requiredModelFeatures, requiredReaderModel, resourceAliasesToMap, resourceFromGdrUri, resourceFromParsed, resourceGdr, rethrowWithContext, runGroq, sameResource, scalarValidationIssues, schemaTreeShape, selfGdr, startKindOf, tagScopeFilter, terminalState, toBareId, toPhysicalGdr, tolerantEntries, tolerantObject, tryParseGdr, validateFieldAppendItem, validateFieldValue, validateResourceAliasName, validateTag };
|