@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
package/dist/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { terminalState, andConditions, deriveActivityKind, parseDefinitionSnapshot, CALLER_BOUND_VARS, START_ALLOWED_VARS, CONDITION_VARS, SUBJECT_HAS_IN_FLIGHT_INSTANCE_VAR, isSubjectEntry, isUnevaluable, ContractViolationError, isStartableDefinition, conditionFieldReadNames, rethrowWithContext, conditionParameterNames, isGdr, WORKFLOW_INSTANCE_TYPE, START_FILTER_VARS, gdrFromResource, selfGdr, isSingleDocRefKind, parentRef, actorFulfillsRole, toBareId, WorkflowError, sameResource, resourceFromParsed, tryParseGdr, isTerminalActivityStatus, errorMessage, validateFieldValue, validateFieldAppendItem, evaluateCondition, isSingleDocRefEntry, choiceValueIssues, scalarValidationIssues, StoredFieldOpSchema, formatIssues, conditionSyntaxIssues, checkWorkflowInvariants, formatIssuePath, isGuardReadExpr, conditionEffectReads, EFFECTS_READ, datasetResourceParts, evaluatePredicates, WORKFLOW_DEFINITION_TYPE, tagScopeFilter, isCascadeFired, runGroq, parseGdr, isGdrUri, gdrRef, isInputSourced, checkFieldValue, toPhysicalGdr, isBareSeedId, tolerantEntries, NonEmptyString, FIELD_VALUE_KINDS, tolerantObject, ActorShape, IsoTimestamp, ACTIVITY_STATUSES, GdrShape, DRIVER_KINDS, FIELD_SCOPES, fieldValueSchemas, parsePersistedDoc, assertReadableModel, InstanceNotFoundError, modelStampFor, evaluateConditionOutcome, FIELD_READ, MUTATION_GUARD_ACTIONS, resourceGdr, minReaderModelOf, resourceFromGdrUri, refTypeIssues, rejectedRefTypes, DOCUMENT_VALUE_PERMISSIONS, deriveExecutorClassification, validateTag, driverKind, EffectNotFoundError, extractDocumentId, parseStoredDefinition, validateResourceAliasName, labelFor, DefinitionNotFoundError, definitionDocId, SpawnContractsInvalidError, gdrResourcePrefix, RESOURCE_ALIAS_NAME_SOURCE, isUnprimed, DefinitionInUseError, assertReaderModelAcknowledgement, FieldValueShapeError, isParseableInstant, groupMembershipNames } from "./_chunks-es/invariants.js";
|
|
2
2
|
|
|
3
|
-
import { ACTIVITY_KINDS, ACTOR_KINDS, DEFAULT_TRANSITION_WHEN, EXECUTOR_CLASSIFICATIONS, FILTER_SCOPE_VARS, GROUP_KINDS, GUARD_PREDICATE_VARS, PersistedDocShapeError, RESERVED_CONDITION_VARS, clientConfigFromResource, gdrUri, isNotesEntry, isTodoListEntry, isTodoListItem, parseResourceGdr, readsRootDocument, refCanvas, refDashboard, refDataset, refMediaLibrary, releaseDocId, releaseRef, resourceAliasesToMap, schemaTreeShape, startKindOf } from "./_chunks-es/invariants.js";
|
|
3
|
+
import { ACTION_SEMANTICS, ACTIVITY_KINDS, ACTOR_KINDS, DATA_MODEL_CHANGES, DATA_MODEL_MIN_READER, DATA_MODEL_VERSION, DEFAULT_TRANSITION_WHEN, EXECUTOR_CLASSIFICATIONS, FILTER_SCOPE_VARS, GROUP_KINDS, GUARD_PREDICATE_VARS, ModelVersionAheadError, PersistedDocShapeError, READER_MODEL_ROLLOUT_URL, RESERVED_CONDITION_VARS, ReaderModelAcknowledgementError, clientConfigFromResource, fieldTreeShape, gdrUri, isNotesEntry, isTodoListEntry, isTodoListItem, modelVersionOf, parseResourceGdr, readsRootDocument, refCanvas, refDashboard, refDataset, refKindAcceptsTypes, refMediaLibrary, releaseDocId, releaseRef, requiredModelFeatures, requiredReaderModel, resourceAliasesToMap, schemaTreeShape, startKindOf } from "./_chunks-es/invariants.js";
|
|
4
4
|
|
|
5
5
|
import { atomNode, dedupeBy, analyzeCondition, atomReadsDataset, checklistLines as checklistLines$1, phrase, quoted, listAnd, describeAtom as describeAtom$1, describeCondition as describeCondition$1, guillemets, formatValue, humanize, listOr, scopeReadOf, describeRead, explainCondition, dedupeReads, whatIfCondition, MAX_COUNTERFACTUAL_INDEX } from "@sanity/groq-condition-describe";
|
|
6
6
|
|
|
7
7
|
import { MAX_COUNTERFACTUAL_INDEX as MAX_COUNTERFACTUAL_INDEX2, analyzeCondition as analyzeCondition2, atomReadsDataset as atomReadsDataset2, explainCondition as explainCondition2, formatRead, guillemets as guillemets2, humanize as humanize2, isComparisonOp, quoted as quoted2, sentenceCase, whatIfCondition as whatIfCondition2, withAssignment } from "@sanity/groq-condition-describe";
|
|
8
8
|
|
|
9
|
-
import {
|
|
9
|
+
import { evaluate, parse } from "groq-js";
|
|
10
10
|
|
|
11
11
|
import * as v from "valibot";
|
|
12
12
|
|
|
@@ -41,28 +41,6 @@ function findCurrentActivityEntry(host, activityName) {
|
|
|
41
41
|
return findOpenStageEntry(host)?.activities.find(a => a.name === activityName);
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
const WORKFLOW_INSTANCE_TYPE = "sanity.workflow.instance";
|
|
45
|
-
|
|
46
|
-
function terminalState(instance) {
|
|
47
|
-
return instance.abortedAt !== void 0 ? "aborted" : instance.completedAt !== void 0 ? "completed" : "in-flight";
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
function isUnprimed(instance) {
|
|
51
|
-
return instance.stages.length === 0 && terminalState(instance) === "in-flight";
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function parseDefinitionSnapshot(instance) {
|
|
55
|
-
try {
|
|
56
|
-
return JSON.parse(instance.definitionSnapshot);
|
|
57
|
-
} catch (err) {
|
|
58
|
-
rethrowWithContext(err, `Failed to parse definitionSnapshot on instance "${instance._id}"`);
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function parentRef(instance) {
|
|
63
|
-
return instance.ancestors.at(-1);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
44
|
function effectiveEditable(baseline, override) {
|
|
67
45
|
if (baseline === void 0) return;
|
|
68
46
|
if (override === void 0) return baseline;
|
|
@@ -102,6 +80,9 @@ function resolveFieldSite(site, stage) {
|
|
|
102
80
|
...site.entry.title !== void 0 ? {
|
|
103
81
|
title: site.entry.title
|
|
104
82
|
} : {},
|
|
83
|
+
...site.entry.validation !== void 0 ? {
|
|
84
|
+
validation: site.entry.validation
|
|
85
|
+
} : {},
|
|
105
86
|
ref: {
|
|
106
87
|
scope: site.scope,
|
|
107
88
|
field: site.entry.name
|
|
@@ -189,14 +170,8 @@ function editDisabledReason(args) {
|
|
|
189
170
|
kind: "not-editable"
|
|
190
171
|
};
|
|
191
172
|
if (!window.open) {
|
|
192
|
-
const
|
|
193
|
-
return
|
|
194
|
-
kind: "instance-aborted",
|
|
195
|
-
abortedAt: instance.abortedAt
|
|
196
|
-
} : terminal === "completed" && instance.completedAt !== void 0 ? {
|
|
197
|
-
kind: "instance-completed",
|
|
198
|
-
completedAt: instance.completedAt
|
|
199
|
-
} : {
|
|
173
|
+
const terminalReason = instanceTerminalReason(instance);
|
|
174
|
+
return terminalReason !== void 0 ? terminalReason : {
|
|
200
175
|
kind: "edit-window-closed",
|
|
201
176
|
detail: window.detail ?? "closed"
|
|
202
177
|
};
|
|
@@ -208,6 +183,18 @@ function editDisabledReason(args) {
|
|
|
208
183
|
};
|
|
209
184
|
}
|
|
210
185
|
|
|
186
|
+
function instanceTerminalReason(instance) {
|
|
187
|
+
const terminal = terminalState(instance);
|
|
188
|
+
if (terminal === "aborted" && instance.abortedAt !== void 0) return {
|
|
189
|
+
kind: "instance-aborted",
|
|
190
|
+
abortedAt: instance.abortedAt
|
|
191
|
+
};
|
|
192
|
+
if (terminal === "completed" && instance.completedAt !== void 0) return {
|
|
193
|
+
kind: "instance-completed",
|
|
194
|
+
completedAt: instance.completedAt
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
211
198
|
function defineWorkflowEvent(options) {
|
|
212
199
|
return {
|
|
213
200
|
type: "log",
|
|
@@ -847,13 +834,13 @@ const checklistLines = checklistLines$1;
|
|
|
847
834
|
function toGroqDescribeContext(ctx) {
|
|
848
835
|
return {
|
|
849
836
|
renderRead: read => renderWorkflowRead(read, ctx),
|
|
850
|
-
renderRequirement: workflowRequirement,
|
|
837
|
+
renderRequirement: requirement => workflowRequirement(requirement, ctx),
|
|
851
838
|
atomPatterns: [ rolesGateText, claimGateText ],
|
|
852
839
|
nowParam: "now"
|
|
853
840
|
};
|
|
854
841
|
}
|
|
855
842
|
|
|
856
|
-
const VAR_LABELS = new Map(CONDITION_VARS.map(entry => [ entry.name, entry.label ]));
|
|
843
|
+
const VAR_LABELS = new Map([ ...START_ALLOWED_VARS, ...CONDITION_VARS ].map(entry => [ entry.name, entry.label ]));
|
|
857
844
|
|
|
858
845
|
function renderWorkflowRead(read, ctx) {
|
|
859
846
|
if (read.variable === "fields" && typeof read.path[0] == "string") {
|
|
@@ -893,7 +880,7 @@ function renderWorkflowRead(read, ctx) {
|
|
|
893
880
|
});
|
|
894
881
|
}
|
|
895
882
|
|
|
896
|
-
function workflowRequirement(requirement) {
|
|
883
|
+
function workflowRequirement(requirement, ctx) {
|
|
897
884
|
const {target: target} = requirement, clause = clauseVarRequirement(requirement);
|
|
898
885
|
if (clause !== void 0) return clause;
|
|
899
886
|
if (target.variable === "assigned" && target.path.length === 0) {
|
|
@@ -906,7 +893,7 @@ function workflowRequirement(requirement) {
|
|
|
906
893
|
text: "you must not be assigned to this activity"
|
|
907
894
|
});
|
|
908
895
|
}
|
|
909
|
-
|
|
896
|
+
return effectStatusPhrase(requirement, ctx);
|
|
910
897
|
}
|
|
911
898
|
|
|
912
899
|
function clauseVarRequirement(requirement) {
|
|
@@ -926,28 +913,40 @@ function clauseVarRequirement(requirement) {
|
|
|
926
913
|
params: {},
|
|
927
914
|
text: "no activity in this stage may have failed"
|
|
928
915
|
});
|
|
916
|
+
if (target.variable === SUBJECT_HAS_IN_FLIGHT_INSTANCE_VAR) return kind === "truthy" ? phrase("requirement.subject-has-in-flight-instance", {
|
|
917
|
+
params: {},
|
|
918
|
+
text: "the subject must already have an in-flight workflow"
|
|
919
|
+
}) : phrase("requirement.subject-has-no-in-flight-instance", {
|
|
920
|
+
params: {},
|
|
921
|
+
text: "the subject must not already have an in-flight workflow"
|
|
922
|
+
});
|
|
929
923
|
}
|
|
930
924
|
}
|
|
931
925
|
|
|
932
|
-
function effectStatusPhrase(requirement,
|
|
933
|
-
const
|
|
926
|
+
function effectStatusPhrase(requirement, ctx) {
|
|
927
|
+
const {target: target} = requirement;
|
|
928
|
+
if (target.variable !== "effectStatus" || typeof target.path[0] != "string") return;
|
|
929
|
+
const effect = target.path[0], title = effectTitleOf(effect, ctx), name = `the ${quoted(title)} automation`;
|
|
934
930
|
if (requirement.kind === "equals" && requirement.value === "done") return phrase("requirement.effect-done", {
|
|
935
931
|
params: {
|
|
936
|
-
effect: effect
|
|
932
|
+
effect: effect,
|
|
933
|
+
title: title
|
|
937
934
|
},
|
|
938
|
-
text: `${name} must have
|
|
935
|
+
text: `${name} must have succeeded`
|
|
939
936
|
});
|
|
940
937
|
if (requirement.kind === "equals" && requirement.value === "failed") return phrase("requirement.effect-failed", {
|
|
941
938
|
params: {
|
|
942
|
-
effect: effect
|
|
939
|
+
effect: effect,
|
|
940
|
+
title: title
|
|
943
941
|
},
|
|
944
942
|
text: `${name} must have failed`
|
|
945
943
|
});
|
|
946
944
|
if (requirement.kind === "defined") return phrase("requirement.effect-settled", {
|
|
947
945
|
params: {
|
|
948
|
-
effect: effect
|
|
946
|
+
effect: effect,
|
|
947
|
+
title: title
|
|
949
948
|
},
|
|
950
|
-
text: `${name} must have
|
|
949
|
+
text: `${name} must have finished`
|
|
951
950
|
});
|
|
952
951
|
}
|
|
953
952
|
|
|
@@ -1235,12 +1234,16 @@ const WAIT_TEXTS = {
|
|
|
1235
1234
|
},
|
|
1236
1235
|
text: `waits on editor content matching ${quoted(wait.condition)}`
|
|
1237
1236
|
}),
|
|
1238
|
-
effect: wait =>
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1237
|
+
effect: (wait, ctx) => {
|
|
1238
|
+
const title = effectTitleOf(wait.effect, ctx);
|
|
1239
|
+
return phrase("autonomy-wait.effect", {
|
|
1240
|
+
params: {
|
|
1241
|
+
effect: wait.effect,
|
|
1242
|
+
title: title
|
|
1243
|
+
},
|
|
1244
|
+
text: `waits on the ${quoted(title)} automation`
|
|
1245
|
+
});
|
|
1246
|
+
},
|
|
1244
1247
|
"start-input": (wait, ctx) => {
|
|
1245
1248
|
const title = fieldTitleOf({
|
|
1246
1249
|
path: [ wait.field ]
|
|
@@ -1353,6 +1356,10 @@ function actionTitle(site, ctx) {
|
|
|
1353
1356
|
return ctx.definition.stages.flatMap(stage => stage.activities ?? []).filter(activity => activity.name === site.activity).flatMap(activity => activity.actions ?? []).find(candidate => candidate.name === site.action)?.title ?? humanize(site.action);
|
|
1354
1357
|
}
|
|
1355
1358
|
|
|
1359
|
+
function effectTitleOf(name, ctx) {
|
|
1360
|
+
return findEffect(ctx.definition, name)?.title ?? name;
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1356
1363
|
function conditionSitesOf(definition) {
|
|
1357
1364
|
const sites = [];
|
|
1358
1365
|
for (const [predicate, condition] of Object.entries(definition.predicates ?? {})) sites.push({
|
|
@@ -1480,25 +1487,17 @@ async function applicableDefinitions(args) {
|
|
|
1480
1487
|
}
|
|
1481
1488
|
|
|
1482
1489
|
async function evaluateStartFilter(args) {
|
|
1483
|
-
const {filter: filter, definition: definition, document: document, scope: scope} = args
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
key: "filter"
|
|
1495
|
-
});
|
|
1496
|
-
}
|
|
1497
|
-
let dataset = [];
|
|
1498
|
-
if (parsed.needsDataset) {
|
|
1499
|
-
if (scope?.fetchDataset === void 0) return !1;
|
|
1500
|
-
dataset = await scope.fetchDataset();
|
|
1501
|
-
}
|
|
1490
|
+
const {filter: filter, definition: definition, document: document, scope: scope} = args, parsed = parseStartFilter({
|
|
1491
|
+
filter: filter,
|
|
1492
|
+
definition: definition
|
|
1493
|
+
});
|
|
1494
|
+
assertFilterSubject({
|
|
1495
|
+
readsSubject: parsed.readsSubject,
|
|
1496
|
+
definition: definition,
|
|
1497
|
+
scope: scope
|
|
1498
|
+
});
|
|
1499
|
+
const dataset = await filterDataset(parsed.needsDataset, scope);
|
|
1500
|
+
if (dataset === void 0) return !1;
|
|
1502
1501
|
try {
|
|
1503
1502
|
const result = await (await evaluate(parsed.tree, {
|
|
1504
1503
|
...document !== void 0 ? {
|
|
@@ -1507,7 +1506,8 @@ async function evaluateStartFilter(args) {
|
|
|
1507
1506
|
dataset: dataset,
|
|
1508
1507
|
params: startContextParams({
|
|
1509
1508
|
definition: definition,
|
|
1510
|
-
scope: scope
|
|
1509
|
+
scope: scope,
|
|
1510
|
+
dataset: dataset
|
|
1511
1511
|
})
|
|
1512
1512
|
})).get();
|
|
1513
1513
|
return isUnevaluable(result) ? !1 : !!result;
|
|
@@ -1520,11 +1520,38 @@ async function evaluateStartFilter(args) {
|
|
|
1520
1520
|
}
|
|
1521
1521
|
}
|
|
1522
1522
|
|
|
1523
|
+
function parseStartFilter(args) {
|
|
1524
|
+
const {filter: filter, definition: definition} = args;
|
|
1525
|
+
try {
|
|
1526
|
+
const readsSubject = readsSubjectHasInFlightInstance(filter);
|
|
1527
|
+
return {
|
|
1528
|
+
tree: parse(filter),
|
|
1529
|
+
needsDataset: analyzeCondition(filter).readsDataset || readsSubject,
|
|
1530
|
+
readsSubject: readsSubject
|
|
1531
|
+
};
|
|
1532
|
+
} catch (err) {
|
|
1533
|
+
rethrowNamingDefinition({
|
|
1534
|
+
err: err,
|
|
1535
|
+
definition: definition,
|
|
1536
|
+
key: "filter"
|
|
1537
|
+
});
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
|
|
1541
|
+
function assertFilterSubject(args) {
|
|
1542
|
+
if (!(!args.readsSubject || args.scope?.subject !== void 0)) throw new ContractViolationError(`start.filter on definition "${args.definition.name ?? "<unnamed>"}" reads $${SUBJECT_HAS_IN_FLIGHT_INSTANCE_VAR}, but the caller supplied no resource-qualified \`scope.subject\` — a loaded document alone cannot identify its project and dataset.`);
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
async function filterDataset(needsDataset, scope) {
|
|
1546
|
+
if (!needsDataset) return [];
|
|
1547
|
+
if (scope?.fetchDataset !== void 0) return scope.fetchDataset();
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1523
1550
|
async function explainStartAllowed(args) {
|
|
1524
1551
|
const {allowed: allowed, definition: definition, fields: fields, scope: scope} = args;
|
|
1525
1552
|
let needsDataset;
|
|
1526
1553
|
try {
|
|
1527
|
-
needsDataset = analyzeCondition(allowed).readsDataset;
|
|
1554
|
+
needsDataset = analyzeCondition(allowed).readsDataset || readsSubjectHasInFlightInstance(allowed);
|
|
1528
1555
|
} catch (err) {
|
|
1529
1556
|
rethrowNamingDefinition({
|
|
1530
1557
|
err: err,
|
|
@@ -1544,7 +1571,8 @@ async function explainStartAllowed(args) {
|
|
|
1544
1571
|
params: startContextParams({
|
|
1545
1572
|
definition: definition,
|
|
1546
1573
|
scope: scope,
|
|
1547
|
-
fields: fields
|
|
1574
|
+
fields: fields,
|
|
1575
|
+
dataset: dataset
|
|
1548
1576
|
})
|
|
1549
1577
|
});
|
|
1550
1578
|
} catch (err) {
|
|
@@ -1560,11 +1588,23 @@ function unboundAllowedReads(allowed, fields) {
|
|
|
1560
1588
|
return [ ...conditionFieldReadNames(allowed) ].filter(name => !Object.hasOwn(fields, name));
|
|
1561
1589
|
}
|
|
1562
1590
|
|
|
1591
|
+
function unboundAllowedReadsWithSubject(args) {
|
|
1592
|
+
const {allowed: allowed, fields: fields, definition: definition} = args, subjectField = (definition.fields ?? []).find(isSubjectEntry)?.name, reads = unboundAllowedReads(allowed, fields);
|
|
1593
|
+
return subjectField !== void 0 && readsSubjectHasInFlightInstance(allowed) && !Object.hasOwn(fields, subjectField) && !reads.includes(subjectField) && reads.push(subjectField),
|
|
1594
|
+
reads;
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1563
1597
|
function rethrowNamingDefinition({err: err, definition: definition, key: key}) {
|
|
1564
|
-
rethrowWithContext(err, `start.${key} on definition "${definition.name ?? "<unnamed>"}" failed to evaluate (
|
|
1598
|
+
rethrowWithContext(err, `start.${key} on definition "${definition.name ?? "<unnamed>"}" failed to evaluate (check the predicate and its evaluation inputs)`);
|
|
1565
1599
|
}
|
|
1566
1600
|
|
|
1567
|
-
function startContextParams({definition: definition, scope: scope, fields: fields}) {
|
|
1601
|
+
function startContextParams({definition: definition, scope: scope, fields: fields, dataset: dataset}) {
|
|
1602
|
+
const subject = fields === void 0 ? scope?.subject : startSubject(definition, fields) ?? scope?.subject, params = baseStartParams(definition, scope);
|
|
1603
|
+
return fields !== void 0 && (params.fields = fields), subject !== void 0 && (params[SUBJECT_HAS_IN_FLIGHT_INSTANCE_VAR] = subjectHasInFlightInstance(dataset, subject)),
|
|
1604
|
+
params;
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
function baseStartParams(definition, scope) {
|
|
1568
1608
|
return {
|
|
1569
1609
|
...definition.name !== void 0 ? {
|
|
1570
1610
|
definition: definition.name
|
|
@@ -1574,13 +1614,33 @@ function startContextParams({definition: definition, scope: scope, fields: field
|
|
|
1574
1614
|
} : {},
|
|
1575
1615
|
...scope?.now !== void 0 ? {
|
|
1576
1616
|
now: scope.now
|
|
1577
|
-
} : {},
|
|
1578
|
-
...fields !== void 0 ? {
|
|
1579
|
-
fields: fields
|
|
1580
1617
|
} : {}
|
|
1581
1618
|
};
|
|
1582
1619
|
}
|
|
1583
1620
|
|
|
1621
|
+
function readsSubjectHasInFlightInstance(groq) {
|
|
1622
|
+
return conditionParameterNames(groq).has(SUBJECT_HAS_IN_FLIGHT_INSTANCE_VAR);
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
function startSubject(definition, fields) {
|
|
1626
|
+
const subjectEntry = (definition.fields ?? []).find(isSubjectEntry);
|
|
1627
|
+
if (subjectEntry === void 0) return;
|
|
1628
|
+
const value = fields[subjectEntry.name];
|
|
1629
|
+
return isGdr(value) ? value.id : void 0;
|
|
1630
|
+
}
|
|
1631
|
+
|
|
1632
|
+
function subjectHasInFlightInstance(dataset, subject) {
|
|
1633
|
+
return dataset.some(row => {
|
|
1634
|
+
if (typeof row != "object" || row === null) return !1;
|
|
1635
|
+
const instance = row;
|
|
1636
|
+
return instance._type !== WORKFLOW_INSTANCE_TYPE || instance.completedAt !== void 0 || !Array.isArray(instance.fields) ? !1 : instance.fields.some(entry => {
|
|
1637
|
+
if (typeof entry != "object" || entry === null) return !1;
|
|
1638
|
+
const field = entry;
|
|
1639
|
+
return field._type === "subject" && isGdr(field.value) && field.value.id === subject;
|
|
1640
|
+
});
|
|
1641
|
+
});
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1584
1644
|
async function evaluateDeclaredStartFilter(args) {
|
|
1585
1645
|
const {definition: definition, document: document, scope: scope} = args, filter = definition.start?.filter;
|
|
1586
1646
|
return filter === void 0 ? !0 : evaluateStartFilter({
|
|
@@ -1716,9 +1776,9 @@ function overlayInstanceInSnapshot(snapshot, instance) {
|
|
|
1716
1776
|
|
|
1717
1777
|
function resolveFieldRead(args) {
|
|
1718
1778
|
const {kind: kind, value: value, path: path, snapshot: snapshot, targetKind: targetKind} = args;
|
|
1719
|
-
if (kind
|
|
1779
|
+
if (isSingleDocRefKind(kind)) {
|
|
1720
1780
|
if (!isGdr(value)) return null;
|
|
1721
|
-
if (path === void 0 && targetKind
|
|
1781
|
+
if (path === void 0 && targetKind !== void 0 && isSingleDocRefKind(targetKind)) return value;
|
|
1722
1782
|
const base = derefBase(value, snapshot);
|
|
1723
1783
|
return path === void 0 ? base : walkDocPath(base, path);
|
|
1724
1784
|
}
|
|
@@ -1891,7 +1951,7 @@ function instanceDocId(tag) {
|
|
|
1891
1951
|
|
|
1892
1952
|
function mapFieldRefValues(args) {
|
|
1893
1953
|
const {entryType: entryType, value: value, fields: fields, of: of, mapRef: mapRef} = args;
|
|
1894
|
-
return value == null ? value : entryType
|
|
1954
|
+
return value == null ? value : isSingleDocRefKind(entryType) || entryType === "release.ref" ? mapRef(value) : entryType === "doc.refs" ? mapItems(value, mapRef) : entryType === "object" ? mapRowRefValues({
|
|
1895
1955
|
row: value,
|
|
1896
1956
|
shapes: fields ?? [],
|
|
1897
1957
|
mapRef: mapRef
|
|
@@ -2097,6 +2157,27 @@ class RequiredFieldNotProvidedError extends WorkflowError {
|
|
|
2097
2157
|
}
|
|
2098
2158
|
}
|
|
2099
2159
|
|
|
2160
|
+
class InitialFieldsInvalidError extends WorkflowError {
|
|
2161
|
+
definition;
|
|
2162
|
+
issues;
|
|
2163
|
+
constructor(args) {
|
|
2164
|
+
const where = args.definition === void 0 ? "" : ` for workflow "${args.definition}"`, lines = args.issues.map(formatInitialFieldIssue).join(`\n`);
|
|
2165
|
+
super("initial-fields-invalid", `Initial fields${where} contain rows the engine cannot consume:\n${lines}\nRemove or correct every listed row before retrying.`),
|
|
2166
|
+
this.name = "InitialFieldsInvalidError", args.definition !== void 0 && (this.definition = args.definition),
|
|
2167
|
+
this.issues = args.issues;
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
|
|
2171
|
+
function formatInitialFieldIssue(issue) {
|
|
2172
|
+
const row = ` - row ${issue.index}: ${issue.name} (${issue.type})`;
|
|
2173
|
+
if (issue.reason === "duplicate") return `${row} duplicates rows ${issue.duplicateIndexes.join(", ")}`;
|
|
2174
|
+
if (issue.reason === "undeclared") return `${row} is not declared by the workflow`;
|
|
2175
|
+
if (issue.reason === "wrong-kind") return `${row} has the wrong kind; expected ${issue.expectedTypes.join(" or ")}`;
|
|
2176
|
+
if (issue.reason === "not-input") return `${row} is ${issue.source}-sourced, not caller-input-sourced`;
|
|
2177
|
+
const scope = issue.scope === "activity" ? `activity "${issue.activity}" in stage "${issue.stage}"` : `stage "${issue.stage}"`;
|
|
2178
|
+
return `${row} targets ${scope}, not workflow scope (declared as ${issue.declaredType})`;
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2100
2181
|
class WorkflowStateDivergedError extends WorkflowError {
|
|
2101
2182
|
instanceId;
|
|
2102
2183
|
guardError;
|
|
@@ -2220,10 +2301,28 @@ function validateActionParams({action: action, activityName: activityName, calle
|
|
|
2220
2301
|
});
|
|
2221
2302
|
continue;
|
|
2222
2303
|
}
|
|
2223
|
-
|
|
2304
|
+
if (!present) continue;
|
|
2305
|
+
if (!checkParamType(value, decl)) {
|
|
2306
|
+
issues.push({
|
|
2307
|
+
param: decl.name,
|
|
2308
|
+
reason: `expected type=${decl.type}, got ${typeof value}`
|
|
2309
|
+
});
|
|
2310
|
+
continue;
|
|
2311
|
+
}
|
|
2312
|
+
const choiceIssues = choiceValueIssues(decl.options, value);
|
|
2313
|
+
choiceIssues !== void 0 && issues.push({
|
|
2224
2314
|
param: decl.name,
|
|
2225
|
-
reason:
|
|
2226
|
-
})
|
|
2315
|
+
reason: choiceIssues.join("; ")
|
|
2316
|
+
});
|
|
2317
|
+
const validationIssues = scalarValidationIssues({
|
|
2318
|
+
entryType: decl.type,
|
|
2319
|
+
validation: decl.validation,
|
|
2320
|
+
value: value
|
|
2321
|
+
});
|
|
2322
|
+
validationIssues !== void 0 && issues.push({
|
|
2323
|
+
param: decl.name,
|
|
2324
|
+
reason: validationIssues.join("; ")
|
|
2325
|
+
});
|
|
2227
2326
|
}
|
|
2228
2327
|
if (issues.length > 0) throw new ActionParamsInvalidError({
|
|
2229
2328
|
action: action.name,
|
|
@@ -2333,7 +2432,10 @@ function validateEffectOps(ops, effectName) {
|
|
|
2333
2432
|
const result = v.safeParse(v.array(StoredFieldOpSchema), ops);
|
|
2334
2433
|
if (!result.success) throw new EffectOpsInvalidError({
|
|
2335
2434
|
effect: effectName,
|
|
2336
|
-
issues: formatIssues(result.issues
|
|
2435
|
+
issues: formatIssues(result.issues, issue => {
|
|
2436
|
+
const path = issue.path;
|
|
2437
|
+
return issue.input === void 0 && path?.at(-2)?.key === "target" && path.at(-1)?.key === "scope" ? "Effect completion field ops require target.scope: 'workflow' | 'stage'." : issue.message;
|
|
2438
|
+
})
|
|
2337
2439
|
});
|
|
2338
2440
|
const activityScoped = result.output.find(op => op.target.scope === "activity");
|
|
2339
2441
|
if (activityScoped !== void 0) throw new EffectOpsInvalidError({
|
|
@@ -2414,9 +2516,16 @@ function entryShape(entry) {
|
|
|
2414
2516
|
fields: entry.fields
|
|
2415
2517
|
} : entry._type === "array" ? {
|
|
2416
2518
|
of: entry.of
|
|
2417
|
-
} : entry
|
|
2519
|
+
} : isSingleDocRefEntry(entry) || entry._type === "doc.refs" ? entry.types !== void 0 ? {
|
|
2418
2520
|
types: entry.types
|
|
2419
|
-
} : {} : {
|
|
2521
|
+
} : {} : {
|
|
2522
|
+
...entry.options !== void 0 ? {
|
|
2523
|
+
options: entry.options
|
|
2524
|
+
} : {},
|
|
2525
|
+
...entry.validation !== void 0 ? {
|
|
2526
|
+
validation: entry.validation
|
|
2527
|
+
} : {}
|
|
2528
|
+
};
|
|
2420
2529
|
}
|
|
2421
2530
|
|
|
2422
2531
|
function entrySlot(entry) {
|
|
@@ -2655,10 +2764,12 @@ function resolveOpValue(args) {
|
|
|
2655
2764
|
}
|
|
2656
2765
|
|
|
2657
2766
|
function readEntryFromMutation(ctx, src) {
|
|
2658
|
-
const scopes = src.scope !== void 0 ? [ src.scope ] : [ "stage", "workflow" ], stageEntry = findOpenStageEntry(ctx.mutation);
|
|
2767
|
+
const scopes = src.scope !== void 0 ? [ src.scope ] : [ "activity", "stage", "workflow" ], stageEntry = findOpenStageEntry(ctx.mutation);
|
|
2659
2768
|
for (const scope of scopes) {
|
|
2660
|
-
|
|
2661
|
-
|
|
2769
|
+
let host;
|
|
2770
|
+
scope === "workflow" ? host = ctx.mutation.fields : scope === "stage" ? host = stageEntry?.fields : host = findCurrentActivityEntry(ctx.mutation, ctx.activityName)?.fields;
|
|
2771
|
+
const entry = host?.find(s => s.name === src.field);
|
|
2772
|
+
if (entry !== void 0) return entry;
|
|
2662
2773
|
}
|
|
2663
2774
|
}
|
|
2664
2775
|
|
|
@@ -3099,53 +3210,6 @@ async function assertInstanceWriteAllowed(args) {
|
|
|
3099
3210
|
});
|
|
3100
3211
|
}
|
|
3101
3212
|
|
|
3102
|
-
const DATA_MODEL_VERSION = 1, DATA_MODEL_MIN_READER = 0, MODEL_STAMP = {
|
|
3103
|
-
modelVersion: DATA_MODEL_VERSION,
|
|
3104
|
-
minReaderModel: DATA_MODEL_MIN_READER
|
|
3105
|
-
};
|
|
3106
|
-
|
|
3107
|
-
function fieldTreeShape(value) {
|
|
3108
|
-
if (Array.isArray(value)) return value.map(fieldTreeShape);
|
|
3109
|
-
if (value === null) return "null";
|
|
3110
|
-
if (typeof value == "object") {
|
|
3111
|
-
const record = value;
|
|
3112
|
-
return Object.fromEntries(Object.keys(record).toSorted().map(key => [ key, fieldTreeShape(record[key]) ]));
|
|
3113
|
-
}
|
|
3114
|
-
return typeof value;
|
|
3115
|
-
}
|
|
3116
|
-
|
|
3117
|
-
function modelVersionOf(doc) {
|
|
3118
|
-
const stamp = doc.modelVersion;
|
|
3119
|
-
return typeof stamp == "number" ? stamp : 0;
|
|
3120
|
-
}
|
|
3121
|
-
|
|
3122
|
-
function minReaderModelOf(doc) {
|
|
3123
|
-
const floor = doc.minReaderModel;
|
|
3124
|
-
return typeof floor == "number" ? floor : modelVersionOf(doc);
|
|
3125
|
-
}
|
|
3126
|
-
|
|
3127
|
-
class ModelVersionAheadError extends WorkflowError {
|
|
3128
|
-
documentId;
|
|
3129
|
-
documentModelVersion;
|
|
3130
|
-
requiredReaderModel;
|
|
3131
|
-
engineModelVersion;
|
|
3132
|
-
constructor(args) {
|
|
3133
|
-
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.`),
|
|
3134
|
-
this.name = "ModelVersionAheadError", this.documentId = args.documentId, this.documentModelVersion = args.documentModelVersion,
|
|
3135
|
-
this.requiredReaderModel = args.requiredReaderModel, this.engineModelVersion = DATA_MODEL_VERSION;
|
|
3136
|
-
}
|
|
3137
|
-
}
|
|
3138
|
-
|
|
3139
|
-
function assertReadableModel(doc) {
|
|
3140
|
-
const requiredReaderModel = minReaderModelOf(doc);
|
|
3141
|
-
if (requiredReaderModel > DATA_MODEL_VERSION) throw new ModelVersionAheadError({
|
|
3142
|
-
documentId: doc._id,
|
|
3143
|
-
documentModelVersion: modelVersionOf(doc),
|
|
3144
|
-
requiredReaderModel: requiredReaderModel
|
|
3145
|
-
});
|
|
3146
|
-
return doc;
|
|
3147
|
-
}
|
|
3148
|
-
|
|
3149
3213
|
async function renderConditionScope(source, opts) {
|
|
3150
3214
|
const {instance: instance, definition: definition, snapshot: snapshot, now: now} = source, base = buildParams({
|
|
3151
3215
|
instance: instance,
|
|
@@ -3519,7 +3583,7 @@ function instanceWatchesDocument(instance, document) {
|
|
|
3519
3583
|
}
|
|
3520
3584
|
|
|
3521
3585
|
function entryDocRefs(entries) {
|
|
3522
|
-
return fieldEntries(entries).flatMap(entry => entry._type
|
|
3586
|
+
return fieldEntries(entries).flatMap(entry => typeof entry._type == "string" && isSingleDocRefKind(entry._type) ? isGdr(entry.value) ? [ entry.value ] : [] : entry._type === "doc.refs" ? Array.isArray(entry.value) ? entry.value.filter(isGdr) : [] : []);
|
|
3523
3587
|
}
|
|
3524
3588
|
|
|
3525
3589
|
function entryReleaseRefs(entries) {
|
|
@@ -3527,7 +3591,7 @@ function entryReleaseRefs(entries) {
|
|
|
3527
3591
|
}
|
|
3528
3592
|
|
|
3529
3593
|
function entrySubjectRefs(entries) {
|
|
3530
|
-
return fieldEntries(entries).flatMap(entry => (entry._type
|
|
3594
|
+
return fieldEntries(entries).flatMap(entry => (typeof entry._type == "string" && isSingleDocRefKind(entry._type) || entry._type === "release.ref") && isGdr(entry.value) ? [ entry.value ] : []);
|
|
3531
3595
|
}
|
|
3532
3596
|
|
|
3533
3597
|
function fieldEntries(entries) {
|
|
@@ -3550,7 +3614,13 @@ function perspectiveFromReleaseValue(value) {
|
|
|
3550
3614
|
|
|
3551
3615
|
async function resolveDeclaredFields(args) {
|
|
3552
3616
|
const {entryDefs: entryDefs, initialFields: initialFields, ctx: ctx, randomKey: randomKey2} = args;
|
|
3553
|
-
if (
|
|
3617
|
+
if (assertInitialFieldsConsumable({
|
|
3618
|
+
workflowFields: entryDefs ?? [],
|
|
3619
|
+
initialFields: initialFields,
|
|
3620
|
+
...ctx.definitionName !== void 0 ? {
|
|
3621
|
+
definitionName: ctx.definitionName
|
|
3622
|
+
} : {}
|
|
3623
|
+
}), entryDefs === void 0 || entryDefs.length === 0) return [];
|
|
3554
3624
|
assertRequiredInputProvided({
|
|
3555
3625
|
entryDefs: entryDefs,
|
|
3556
3626
|
initialFields: initialFields,
|
|
@@ -3572,6 +3642,83 @@ async function resolveDeclaredFields(args) {
|
|
|
3572
3642
|
return out;
|
|
3573
3643
|
}
|
|
3574
3644
|
|
|
3645
|
+
function initialFieldIssues(args) {
|
|
3646
|
+
const duplicateIndexes = /* @__PURE__ */ new Map;
|
|
3647
|
+
return args.initialFields.forEach((field, index) => {
|
|
3648
|
+
const key = `${field.name}\0${field.type}`, indexes = duplicateIndexes.get(key) ?? [];
|
|
3649
|
+
indexes.push(index), duplicateIndexes.set(key, indexes);
|
|
3650
|
+
}), args.initialFields.flatMap((field, index) => {
|
|
3651
|
+
const issues = [], indexes = duplicateIndexes.get(`${field.name}\0${field.type}`) ?? [];
|
|
3652
|
+
indexes.length > 1 && issues.push({
|
|
3653
|
+
index: index,
|
|
3654
|
+
name: field.name,
|
|
3655
|
+
type: field.type,
|
|
3656
|
+
reason: "duplicate",
|
|
3657
|
+
duplicateIndexes: indexes.filter(candidate => candidate !== index)
|
|
3658
|
+
});
|
|
3659
|
+
const workflowMatches = args.workflowFields.filter(entry => entry.name === field.name);
|
|
3660
|
+
if (workflowMatches.length > 0) {
|
|
3661
|
+
const exact = workflowMatches.find(entry => entry.type === field.type);
|
|
3662
|
+
return exact === void 0 ? issues.push({
|
|
3663
|
+
index: index,
|
|
3664
|
+
name: field.name,
|
|
3665
|
+
type: field.type,
|
|
3666
|
+
reason: "wrong-kind",
|
|
3667
|
+
expectedTypes: [ ...new Set(workflowMatches.map(entry => entry.type)) ]
|
|
3668
|
+
}) : exact.initialValue?.type !== "input" && issues.push({
|
|
3669
|
+
index: index,
|
|
3670
|
+
name: field.name,
|
|
3671
|
+
type: field.type,
|
|
3672
|
+
reason: "not-input",
|
|
3673
|
+
source: exact.initialValue?.type ?? "working-memory"
|
|
3674
|
+
}), issues;
|
|
3675
|
+
}
|
|
3676
|
+
const scoped = findNonWorkflowDeclaration(args.stages ?? [], field.name);
|
|
3677
|
+
return scoped !== void 0 ? issues.push({
|
|
3678
|
+
index: index,
|
|
3679
|
+
name: field.name,
|
|
3680
|
+
type: field.type,
|
|
3681
|
+
reason: "invalid-scope",
|
|
3682
|
+
...scoped
|
|
3683
|
+
}) : issues.push({
|
|
3684
|
+
index: index,
|
|
3685
|
+
name: field.name,
|
|
3686
|
+
type: field.type,
|
|
3687
|
+
reason: "undeclared"
|
|
3688
|
+
}), issues;
|
|
3689
|
+
});
|
|
3690
|
+
}
|
|
3691
|
+
|
|
3692
|
+
function findNonWorkflowDeclaration(stages, name) {
|
|
3693
|
+
for (const stage of stages) {
|
|
3694
|
+
const stageField = stage.fields?.find(entry => entry.name === name);
|
|
3695
|
+
if (stageField !== void 0) return {
|
|
3696
|
+
scope: "stage",
|
|
3697
|
+
stage: stage.name,
|
|
3698
|
+
declaredType: stageField.type
|
|
3699
|
+
};
|
|
3700
|
+
for (const activity of stage.activities ?? []) {
|
|
3701
|
+
const activityField = activity.fields?.find(entry => entry.name === name);
|
|
3702
|
+
if (activityField !== void 0) return {
|
|
3703
|
+
scope: "activity",
|
|
3704
|
+
stage: stage.name,
|
|
3705
|
+
activity: activity.name,
|
|
3706
|
+
declaredType: activityField.type
|
|
3707
|
+
};
|
|
3708
|
+
}
|
|
3709
|
+
}
|
|
3710
|
+
}
|
|
3711
|
+
|
|
3712
|
+
function assertInitialFieldsConsumable(args) {
|
|
3713
|
+
const issues = initialFieldIssues(args);
|
|
3714
|
+
if (issues.length !== 0) throw new InitialFieldsInvalidError({
|
|
3715
|
+
...args.definitionName !== void 0 ? {
|
|
3716
|
+
definition: args.definitionName
|
|
3717
|
+
} : {},
|
|
3718
|
+
issues: issues
|
|
3719
|
+
});
|
|
3720
|
+
}
|
|
3721
|
+
|
|
3575
3722
|
function missingRequiredInputs(args) {
|
|
3576
3723
|
return args.entryDefs.filter(entry => entry.required === !0 && isInputSourced(entry)).filter(entry => {
|
|
3577
3724
|
const match = suppliedFieldFor(entry, args.initialFields);
|
|
@@ -3711,6 +3858,12 @@ function buildResolvedEntry({entry: entry, value: value, _key: _key, now: now})
|
|
|
3711
3858
|
...entry.description !== void 0 ? {
|
|
3712
3859
|
description: entry.description
|
|
3713
3860
|
} : {},
|
|
3861
|
+
...entry.options !== void 0 ? {
|
|
3862
|
+
options: entry.options
|
|
3863
|
+
} : {},
|
|
3864
|
+
...entry.validation !== void 0 ? {
|
|
3865
|
+
validation: entry.validation
|
|
3866
|
+
} : {},
|
|
3714
3867
|
value: value,
|
|
3715
3868
|
...entry.initialValue?.type === "query" ? {
|
|
3716
3869
|
resolvedAt: now
|
|
@@ -3740,7 +3893,9 @@ async function resolveOneEntry({entry: entry, initialFields: initialFields, ctx:
|
|
|
3740
3893
|
value: value,
|
|
3741
3894
|
types: entry.types,
|
|
3742
3895
|
fields: entry.fields,
|
|
3743
|
-
of: entry.of
|
|
3896
|
+
of: entry.of,
|
|
3897
|
+
options: entry.options,
|
|
3898
|
+
validation: entry.validation
|
|
3744
3899
|
});
|
|
3745
3900
|
return issues !== void 0 ? (ctx.recordDiscard?.({
|
|
3746
3901
|
field: entry.name,
|
|
@@ -3763,7 +3918,9 @@ async function resolveOneEntry({entry: entry, initialFields: initialFields, ctx:
|
|
|
3763
3918
|
value: value,
|
|
3764
3919
|
types: entry.types,
|
|
3765
3920
|
fields: entry.fields,
|
|
3766
|
-
of: entry.of
|
|
3921
|
+
of: entry.of,
|
|
3922
|
+
options: entry.options,
|
|
3923
|
+
validation: entry.validation
|
|
3767
3924
|
}), entry.initialValue?.type === "input" && ctx.inputProvenance !== "definition" && assertRefsWithinSurface({
|
|
3768
3925
|
entryType: entry.type,
|
|
3769
3926
|
entryName: entry.name,
|
|
@@ -3786,8 +3943,8 @@ function fieldMapFromResolved(entries) {
|
|
|
3786
3943
|
}
|
|
3787
3944
|
|
|
3788
3945
|
function assertInputValueShape(entry, value) {
|
|
3789
|
-
if (entry.type
|
|
3790
|
-
assertGdrShape(value, `field entry "${entry.name}" (
|
|
3946
|
+
if (isSingleDocRefKind(entry.type)) {
|
|
3947
|
+
assertGdrShape(value, `field entry "${entry.name}" (${entry.type})`);
|
|
3791
3948
|
return;
|
|
3792
3949
|
}
|
|
3793
3950
|
if (entry.type === "release.ref") {
|
|
@@ -3810,7 +3967,7 @@ function assertGdrShape(value, context) {
|
|
|
3810
3967
|
}
|
|
3811
3968
|
|
|
3812
3969
|
function normalizeQueryResult({entryType: entryType, raw: raw, workflowResource: workflowResource}) {
|
|
3813
|
-
return raw == null ? raw : entryType
|
|
3970
|
+
return raw == null ? raw : isSingleDocRefKind(entryType) ? coerceToGdr(raw, workflowResource) : entryType === "doc.refs" ? Array.isArray(raw) ? raw.map(item => coerceToGdr(item, workflowResource)).filter(v2 => v2 !== null) : [] : raw;
|
|
3814
3971
|
}
|
|
3815
3972
|
|
|
3816
3973
|
function coerceGdrShape(raw, workflowResource) {
|
|
@@ -3841,11 +3998,21 @@ function coerceToGdr(raw, workflowResource) {
|
|
|
3841
3998
|
} : null;
|
|
3842
3999
|
}
|
|
3843
4000
|
|
|
3844
|
-
const EFFECT_RUN_STATUSES = [ "done", "failed", "cancelled" ], NonEmpty = NonEmptyString, UnknownRecord = v.record(v.string(), v.unknown()),
|
|
4001
|
+
const EFFECT_RUN_STATUSES = [ "done", "failed", "cancelled" ], NonEmpty = NonEmptyString, UnknownRecord = v.record(v.string(), v.unknown()), PersistedChoiceOptionsSchema = v.looseObject({
|
|
4002
|
+
list: v.array(v.looseObject({
|
|
4003
|
+
title: v.string(),
|
|
4004
|
+
value: v.union([ v.string(), v.number() ])
|
|
4005
|
+
}))
|
|
4006
|
+
}), PersistedScalarValidationSchema = v.looseObject({
|
|
4007
|
+
min: v.optional(v.number()),
|
|
4008
|
+
max: v.optional(v.number())
|
|
4009
|
+
}), PersistedFieldShapeSchema = v.lazy(() => v.looseObject(tolerantEntries()({
|
|
3845
4010
|
type: v.picklist(FIELD_VALUE_KINDS),
|
|
3846
4011
|
name: NonEmpty,
|
|
3847
4012
|
title: v.optional(v.string()),
|
|
3848
4013
|
description: v.optional(v.string()),
|
|
4014
|
+
options: v.optional(PersistedChoiceOptionsSchema),
|
|
4015
|
+
validation: v.optional(PersistedScalarValidationSchema),
|
|
3849
4016
|
fields: v.optional(v.array(PersistedFieldShapeSchema)),
|
|
3850
4017
|
of: v.optional(v.array(PersistedFieldShapeSchema))
|
|
3851
4018
|
}))), ExecutionContextSchema = tolerantObject()({
|
|
@@ -3875,6 +4042,8 @@ function fieldArm(kind, value) {
|
|
|
3875
4042
|
name: NonEmpty,
|
|
3876
4043
|
title: v.exactOptional(v.string()),
|
|
3877
4044
|
description: v.exactOptional(v.string()),
|
|
4045
|
+
options: v.exactOptional(PersistedChoiceOptionsSchema),
|
|
4046
|
+
validation: v.exactOptional(PersistedScalarValidationSchema),
|
|
3878
4047
|
value: value,
|
|
3879
4048
|
resolvedAt: v.exactOptional(IsoTimestamp)
|
|
3880
4049
|
};
|
|
@@ -3886,6 +4055,9 @@ const OptionalRefTypes = v.exactOptional(v.array(v.string())), ResolvedFieldEntr
|
|
|
3886
4055
|
})), v.looseObject(tolerantEntries()({
|
|
3887
4056
|
...fieldArm("doc.refs", fieldValueSchemas["doc.refs"]),
|
|
3888
4057
|
types: OptionalRefTypes
|
|
4058
|
+
})), v.looseObject(tolerantEntries()({
|
|
4059
|
+
...fieldArm("subject", fieldValueSchemas.subject),
|
|
4060
|
+
types: OptionalRefTypes
|
|
3889
4061
|
})), v.looseObject(tolerantEntries()(fieldArm("release.ref", fieldValueSchemas["release.ref"]))), v.looseObject(tolerantEntries()(fieldArm("string", fieldValueSchemas.string))), v.looseObject(tolerantEntries()(fieldArm("text", fieldValueSchemas.text))), v.looseObject(tolerantEntries()(fieldArm("number", fieldValueSchemas.number))), v.looseObject(tolerantEntries()(fieldArm("boolean", fieldValueSchemas.boolean))), v.looseObject(tolerantEntries()(fieldArm("date", fieldValueSchemas.date))), v.looseObject(tolerantEntries()(fieldArm("datetime", fieldValueSchemas.datetime))), v.looseObject(tolerantEntries()(fieldArm("url", fieldValueSchemas.url))), v.looseObject(tolerantEntries()(fieldArm("actor", fieldValueSchemas.actor))), v.looseObject(tolerantEntries()(fieldArm("assignee", fieldValueSchemas.assignee))), v.looseObject(tolerantEntries()(fieldArm("assignees", fieldValueSchemas.assignees))), v.looseObject(tolerantEntries()({
|
|
3890
4062
|
...fieldArm("object", v.union([ v.null(), UnknownRecord ])),
|
|
3891
4063
|
fields: v.array(PersistedFieldShapeSchema)
|
|
@@ -4211,14 +4383,12 @@ function buildInstanceBase(args) {
|
|
|
4211
4383
|
...actor !== void 0 ? {
|
|
4212
4384
|
actor: actor
|
|
4213
4385
|
} : {}
|
|
4214
|
-
}, ...args.extraHistory ?? [] ], history = executionContext !== void 0 ? stampHistoryEntries(entries, executionContext) : entries
|
|
4215
|
-
return {
|
|
4386
|
+
}, ...args.extraHistory ?? [] ], history = executionContext !== void 0 ? stampHistoryEntries(entries, executionContext) : entries, body = {
|
|
4216
4387
|
_id: id,
|
|
4217
4388
|
_type: WORKFLOW_INSTANCE_TYPE,
|
|
4218
4389
|
_rev: "",
|
|
4219
4390
|
_createdAt: now,
|
|
4220
4391
|
_updatedAt: now,
|
|
4221
|
-
...MODEL_STAMP,
|
|
4222
4392
|
tag: args.tag,
|
|
4223
4393
|
workflowResource: args.workflowResource,
|
|
4224
4394
|
definition: args.definitionName,
|
|
@@ -4242,6 +4412,13 @@ function buildInstanceBase(args) {
|
|
|
4242
4412
|
startedAt: now,
|
|
4243
4413
|
lastChangedAt: now
|
|
4244
4414
|
};
|
|
4415
|
+
return {
|
|
4416
|
+
...body,
|
|
4417
|
+
...modelStampFor({
|
|
4418
|
+
documentType: "instance",
|
|
4419
|
+
document: body
|
|
4420
|
+
})
|
|
4421
|
+
};
|
|
4245
4422
|
}
|
|
4246
4423
|
|
|
4247
4424
|
async function hydrateSnapshot(args) {
|
|
@@ -4922,11 +5099,7 @@ function dedupById(guards) {
|
|
|
4922
5099
|
return [ ...new Map(guards.map(g => [ g._id, g ])).values() ].sort((a, b) => a._id.localeCompare(b._id));
|
|
4923
5100
|
}
|
|
4924
5101
|
|
|
4925
|
-
const GUARD_OWNER = "robot:workflow-engine"
|
|
4926
|
-
|
|
4927
|
-
function isGuardLifted(guard) {
|
|
4928
|
-
return guard.predicate === GUARD_LIFTED_PREDICATE;
|
|
4929
|
-
}
|
|
5102
|
+
const GUARD_OWNER = "robot:workflow-engine";
|
|
4930
5103
|
|
|
4931
5104
|
function resolveGuardRoute(guard, ctx) {
|
|
4932
5105
|
const targets = resolveIdRefTargets(guard.match.idRefs, ctx);
|
|
@@ -5054,15 +5227,30 @@ async function deployStageGuards(args) {
|
|
|
5054
5227
|
}
|
|
5055
5228
|
|
|
5056
5229
|
async function retractStageGuards(args) {
|
|
5230
|
+
const observed = [];
|
|
5231
|
+
for (const {client: client, guardId: guardId} of resolvedStageGuardRoutes(args)) {
|
|
5232
|
+
const guard = await client.getDocument(guardId, {
|
|
5233
|
+
tag: REQUEST_TAG.guardRetract
|
|
5234
|
+
});
|
|
5235
|
+
if (guard) {
|
|
5236
|
+
if (guard._rev === void 0) throw new Error(`Cannot retract guard ${guardId}: persisted document has no revision`);
|
|
5237
|
+
observed.push({
|
|
5238
|
+
client: client,
|
|
5239
|
+
guardId: guardId,
|
|
5240
|
+
predicate: guard.predicate,
|
|
5241
|
+
revision: guard._rev
|
|
5242
|
+
});
|
|
5243
|
+
}
|
|
5244
|
+
}
|
|
5057
5245
|
const live = await committedInstance(args);
|
|
5058
|
-
if (live !== void 0 && !(live.currentStage === args.stageName && live.abortedAt === void 0)) for (const {client: client, guardId: guardId} of
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
}
|
|
5246
|
+
if (live !== void 0 && !(live.currentStage === args.stageName && live.abortedAt === void 0)) for (const {client: client, guardId: guardId, predicate: predicate, revision: revision} of observed) {
|
|
5247
|
+
const revisionCheck = client.patch(guardId).set({
|
|
5248
|
+
predicate: predicate
|
|
5249
|
+
}).ifRevisionId(revision);
|
|
5250
|
+
await client.transaction().patch(revisionCheck).delete(guardId).commit({
|
|
5251
|
+
tag: REQUEST_TAG.guardRetract
|
|
5252
|
+
});
|
|
5253
|
+
}
|
|
5066
5254
|
}
|
|
5067
5255
|
|
|
5068
5256
|
async function deleteOrphanedDefinitionGuards(args) {
|
|
@@ -5592,7 +5780,7 @@ async function resolveContextHandoff({ctx: ctx, sub: sub, parentScope: parentSco
|
|
|
5592
5780
|
}
|
|
5593
5781
|
|
|
5594
5782
|
function canonicaliseSpawnValue({kind: kind, value: value, cx: cx}) {
|
|
5595
|
-
return kind
|
|
5783
|
+
return isSingleDocRefKind(kind) ? coerceSpawnGdr(value, cx) : kind === "doc.refs" && Array.isArray(value) ? value.map(v2 => coerceSpawnGdr(v2, cx)).filter(v2 => v2 !== null) : value;
|
|
5596
5784
|
}
|
|
5597
5785
|
|
|
5598
5786
|
function assertBareIdRootsCorrectly(id, cx) {
|
|
@@ -6318,6 +6506,9 @@ async function evaluateEditableFields(args) {
|
|
|
6318
6506
|
...site.title !== void 0 ? {
|
|
6319
6507
|
title: site.title
|
|
6320
6508
|
} : {},
|
|
6509
|
+
...site.validation !== void 0 ? {
|
|
6510
|
+
validation: site.validation
|
|
6511
|
+
} : {},
|
|
6321
6512
|
value: value,
|
|
6322
6513
|
editable: reason === void 0,
|
|
6323
6514
|
...reason !== void 0 ? {
|
|
@@ -6543,7 +6734,7 @@ function triggeredActionVerdict({action: action, when: when, insights: insights}
|
|
|
6543
6734
|
},
|
|
6544
6735
|
...insights
|
|
6545
6736
|
}) : {
|
|
6546
|
-
action
|
|
6737
|
+
...actionEvaluationIdentity(action),
|
|
6547
6738
|
allowed: !1,
|
|
6548
6739
|
triggered: !0,
|
|
6549
6740
|
disabledReason: {
|
|
@@ -6589,12 +6780,21 @@ function fireableActionVerdict({args: args, insights: insights}) {
|
|
|
6589
6780
|
},
|
|
6590
6781
|
...insights
|
|
6591
6782
|
}) : {
|
|
6592
|
-
action
|
|
6783
|
+
...actionEvaluationIdentity(action),
|
|
6593
6784
|
allowed: !0,
|
|
6594
6785
|
...insights
|
|
6595
6786
|
};
|
|
6596
6787
|
}
|
|
6597
6788
|
|
|
6789
|
+
function actionEvaluationIdentity(action) {
|
|
6790
|
+
return {
|
|
6791
|
+
action: action,
|
|
6792
|
+
...action.semantics !== void 0 ? {
|
|
6793
|
+
semantics: action.semantics
|
|
6794
|
+
} : {}
|
|
6795
|
+
};
|
|
6796
|
+
}
|
|
6797
|
+
|
|
6598
6798
|
async function instanceGuardReason({instance: instance, actor: actor, guards: guards}) {
|
|
6599
6799
|
if (guards === void 0 || guards.length === 0) return;
|
|
6600
6800
|
const denied = await instanceWriteDenials({
|
|
@@ -6609,15 +6809,8 @@ async function instanceGuardReason({instance: instance, actor: actor, guards: gu
|
|
|
6609
6809
|
}
|
|
6610
6810
|
|
|
6611
6811
|
function lifecycleReason({instance: instance, status: status, stageHasExits: stageHasExits}) {
|
|
6612
|
-
const
|
|
6613
|
-
if (
|
|
6614
|
-
kind: "instance-aborted",
|
|
6615
|
-
abortedAt: instance.abortedAt
|
|
6616
|
-
};
|
|
6617
|
-
if (terminal === "completed" && instance.completedAt !== void 0) return {
|
|
6618
|
-
kind: "instance-completed",
|
|
6619
|
-
completedAt: instance.completedAt
|
|
6620
|
-
};
|
|
6812
|
+
const terminalReason = instanceTerminalReason(instance);
|
|
6813
|
+
if (terminalReason !== void 0) return terminalReason;
|
|
6621
6814
|
if (!stageHasExits) return {
|
|
6622
6815
|
kind: "stage-terminal",
|
|
6623
6816
|
stage: instance.currentStage
|
|
@@ -6630,7 +6823,7 @@ function lifecycleReason({instance: instance, status: status, stageHasExits: sta
|
|
|
6630
6823
|
|
|
6631
6824
|
function disabled(args) {
|
|
6632
6825
|
return {
|
|
6633
|
-
|
|
6826
|
+
...actionEvaluationIdentity(args.action),
|
|
6634
6827
|
allowed: !1,
|
|
6635
6828
|
disabledReason: args.reason,
|
|
6636
6829
|
...args.insight !== void 0 ? {
|
|
@@ -7441,9 +7634,7 @@ function startMutation(instance) {
|
|
|
7441
7634
|
}
|
|
7442
7635
|
|
|
7443
7636
|
function instanceStateFields(src) {
|
|
7444
|
-
const
|
|
7445
|
-
modelVersion: DATA_MODEL_VERSION,
|
|
7446
|
-
minReaderModel: Math.max(src.minReaderModel, DATA_MODEL_MIN_READER),
|
|
7637
|
+
const state = {
|
|
7447
7638
|
currentStage: src.currentStage,
|
|
7448
7639
|
fields: src.fields,
|
|
7449
7640
|
stages: src.stages,
|
|
@@ -7452,10 +7643,22 @@ function instanceStateFields(src) {
|
|
|
7452
7643
|
effectHistory: src.effectHistory,
|
|
7453
7644
|
context: src.context,
|
|
7454
7645
|
history: src.history,
|
|
7455
|
-
processedRequests: src.processedRequests ?? []
|
|
7646
|
+
processedRequests: src.processedRequests ?? [],
|
|
7647
|
+
...src.completedAt !== void 0 ? {
|
|
7648
|
+
completedAt: src.completedAt
|
|
7649
|
+
} : {},
|
|
7650
|
+
...src.abortedAt !== void 0 ? {
|
|
7651
|
+
abortedAt: src.abortedAt
|
|
7652
|
+
} : {}
|
|
7653
|
+
};
|
|
7654
|
+
return {
|
|
7655
|
+
...modelStampFor({
|
|
7656
|
+
documentType: "instance",
|
|
7657
|
+
document: state,
|
|
7658
|
+
storedMinReaderModel: src.minReaderModel
|
|
7659
|
+
}),
|
|
7660
|
+
...state
|
|
7456
7661
|
};
|
|
7457
|
-
return src.completedAt !== void 0 && (fields.completedAt = src.completedAt), src.abortedAt !== void 0 && (fields.abortedAt = src.abortedAt),
|
|
7458
|
-
fields;
|
|
7459
7662
|
}
|
|
7460
7663
|
|
|
7461
7664
|
function liveViewContext(ctx, mutation) {
|
|
@@ -7667,16 +7870,7 @@ function validateEffectOutputs(args) {
|
|
|
7667
7870
|
issues.push(`"${key}" is not a declared output`);
|
|
7668
7871
|
continue;
|
|
7669
7872
|
}
|
|
7670
|
-
const shapeIssues =
|
|
7671
|
-
entryType: shape.type,
|
|
7672
|
-
value: value,
|
|
7673
|
-
...shape.fields !== void 0 ? {
|
|
7674
|
-
fields: shape.fields
|
|
7675
|
-
} : {},
|
|
7676
|
-
...shape.of !== void 0 ? {
|
|
7677
|
-
of: shape.of
|
|
7678
|
-
} : {}
|
|
7679
|
-
});
|
|
7873
|
+
const shapeIssues = effectOutputIssues(shape, value);
|
|
7680
7874
|
shapeIssues !== void 0 && issues.push(...shapeIssues.map(i => `"${key}": ${i}`));
|
|
7681
7875
|
}
|
|
7682
7876
|
if (issues.length > 0) throw new EffectOutputsInvalidError({
|
|
@@ -7685,6 +7879,25 @@ function validateEffectOutputs(args) {
|
|
|
7685
7879
|
});
|
|
7686
7880
|
}
|
|
7687
7881
|
|
|
7882
|
+
function effectOutputIssues(shape, value) {
|
|
7883
|
+
return checkFieldValue({
|
|
7884
|
+
entryType: shape.type,
|
|
7885
|
+
value: value,
|
|
7886
|
+
...shape.fields !== void 0 ? {
|
|
7887
|
+
fields: shape.fields
|
|
7888
|
+
} : {},
|
|
7889
|
+
...shape.of !== void 0 ? {
|
|
7890
|
+
of: shape.of
|
|
7891
|
+
} : {},
|
|
7892
|
+
...shape.options !== void 0 ? {
|
|
7893
|
+
options: shape.options
|
|
7894
|
+
} : {},
|
|
7895
|
+
...shape.validation !== void 0 ? {
|
|
7896
|
+
validation: shape.validation
|
|
7897
|
+
} : {}
|
|
7898
|
+
});
|
|
7899
|
+
}
|
|
7900
|
+
|
|
7688
7901
|
function requirePendingEffect(instance, effectKey) {
|
|
7689
7902
|
const pending = instance.pendingEffects.find(e => e._key === effectKey);
|
|
7690
7903
|
if (pending !== void 0) return pending;
|
|
@@ -7905,7 +8118,7 @@ async function sortByDependencies({client: client, definitions: definitions, tag
|
|
|
7905
8118
|
byName.set(def.name, def);
|
|
7906
8119
|
}
|
|
7907
8120
|
const findInBatch = ref => findRefInBatch(byName, ref);
|
|
7908
|
-
return await
|
|
8121
|
+
return await assertSpawnContracts({
|
|
7909
8122
|
client: client,
|
|
7910
8123
|
definitions: definitions,
|
|
7911
8124
|
ctx: {
|
|
@@ -7921,31 +8134,76 @@ function findRefInBatch(byName, ref) {
|
|
|
7921
8134
|
if (typeof ref.version != "number") return byName.get(ref.name);
|
|
7922
8135
|
}
|
|
7923
8136
|
|
|
7924
|
-
async function
|
|
7925
|
-
const missing = [];
|
|
7926
|
-
for (const def of definitions) for (const
|
|
7927
|
-
|
|
7928
|
-
const label = await resolveDeployedRefLabel({
|
|
8137
|
+
async function assertSpawnContracts({client: client, definitions: definitions, ctx: ctx}) {
|
|
8138
|
+
const missing = [], invalidHandoffs = [];
|
|
8139
|
+
for (const def of definitions) for (const spawn of spawnSitesOf(def)) {
|
|
8140
|
+
const ref = logicalRefOf(spawn.definition), child = ctx.findInBatch(ref) ?? await resolveDeployedRef({
|
|
7929
8141
|
client: client,
|
|
7930
8142
|
ref: ref,
|
|
7931
8143
|
tag: ctx.tag
|
|
7932
8144
|
});
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
|
|
7936
|
-
|
|
8145
|
+
if (child === void 0) {
|
|
8146
|
+
missing.push({
|
|
8147
|
+
reason: "unresolved-definition",
|
|
8148
|
+
from: def.name,
|
|
8149
|
+
ref: logicalRefLabel(ref)
|
|
8150
|
+
});
|
|
8151
|
+
continue;
|
|
8152
|
+
}
|
|
8153
|
+
for (const field of Object.keys(spawn.with ?? {})) {
|
|
8154
|
+
const entry = (child.fields ?? []).find(candidate => candidate.name === field);
|
|
8155
|
+
entry === void 0 ? invalidHandoffs.push({
|
|
8156
|
+
reason: "unconsumable-field",
|
|
8157
|
+
from: def.name,
|
|
8158
|
+
child: child.name,
|
|
8159
|
+
field: field,
|
|
8160
|
+
detail: "the child declares no workflow field with that name"
|
|
8161
|
+
}) : isInputSourced(entry) || invalidHandoffs.push({
|
|
8162
|
+
reason: "unconsumable-field",
|
|
8163
|
+
from: def.name,
|
|
8164
|
+
child: child.name,
|
|
8165
|
+
field: field,
|
|
8166
|
+
detail: `the child field is ${entry.initialValue?.type ?? "working-memory"}-sourced, not input-sourced`
|
|
8167
|
+
});
|
|
8168
|
+
}
|
|
7937
8169
|
}
|
|
7938
|
-
if (missing.length === 0) return;
|
|
7939
|
-
|
|
7940
|
-
|
|
8170
|
+
if (missing.length === 0 && invalidHandoffs.length === 0) return;
|
|
8171
|
+
if (invalidHandoffs.length === 0) {
|
|
8172
|
+
const lines2 = missing.map(issue => ` - ${issue.from} → ${issue.ref} (neither in batch nor deployed)`);
|
|
8173
|
+
throw new Error(`workflow.deployDefinitions: ${missing.length} unresolved reference${missing.length === 1 ? "" : "s"}:\n` + lines2.join(`\n`));
|
|
8174
|
+
}
|
|
8175
|
+
const issues = [ ...missing, ...invalidHandoffs ], lines = [ ...missing.map(m => ` - ${m.from} → ${m.ref} (neither in batch nor deployed)`), ...invalidHandoffs.map(issue => ` - ${issue.from} → ${issue.child}: spawn.with["${issue.field}"] cannot be consumed — ${issue.detail}`) ], count = lines.length;
|
|
8176
|
+
throw new SpawnContractsInvalidError({
|
|
8177
|
+
issues: issues,
|
|
8178
|
+
message: `workflow.deployDefinitions: ${count} invalid spawn contract${count === 1 ? "" : "s"}:\n` + lines.join(`\n`)
|
|
8179
|
+
});
|
|
7941
8180
|
}
|
|
7942
8181
|
|
|
7943
|
-
async function
|
|
8182
|
+
async function resolveDeployedRef({client: client, ref: ref, tag: tag}) {
|
|
7944
8183
|
const wantsExplicit = typeof ref.version == "number", params = {
|
|
7945
8184
|
definition: ref.name,
|
|
7946
8185
|
tag: tag
|
|
7947
8186
|
};
|
|
7948
|
-
|
|
8187
|
+
wantsExplicit && (params.version = ref.version);
|
|
8188
|
+
const doc = await client.fetch(definitionLookupGroq(wantsExplicit), params);
|
|
8189
|
+
if (doc) return parseDefinitionInput(doc, `workflow.deployDefinitions child "${logicalRefLabel(ref)}"`);
|
|
8190
|
+
}
|
|
8191
|
+
|
|
8192
|
+
function logicalRefLabel(ref) {
|
|
8193
|
+
return typeof ref.version == "number" ? `${ref.name} v${ref.version}` : ref.name;
|
|
8194
|
+
}
|
|
8195
|
+
|
|
8196
|
+
function logicalRefOf(ref) {
|
|
8197
|
+
return {
|
|
8198
|
+
name: ref.name,
|
|
8199
|
+
...ref.version !== void 0 ? {
|
|
8200
|
+
version: ref.version
|
|
8201
|
+
} : {}
|
|
8202
|
+
};
|
|
8203
|
+
}
|
|
8204
|
+
|
|
8205
|
+
function spawnSitesOf(def) {
|
|
8206
|
+
return def.stages.flatMap(stage => stage.activities ?? []).flatMap(activity => activity.actions ?? []).flatMap(action => action.spawn === void 0 ? [] : [ action.spawn ]);
|
|
7949
8207
|
}
|
|
7950
8208
|
|
|
7951
8209
|
function topoSortDefinitions(definitions, ctx) {
|
|
@@ -7965,12 +8223,7 @@ function topoSortDefinitions(definitions, ctx) {
|
|
|
7965
8223
|
}
|
|
7966
8224
|
|
|
7967
8225
|
function refsOf(def) {
|
|
7968
|
-
return def
|
|
7969
|
-
name: ref.name,
|
|
7970
|
-
...ref.version !== void 0 ? {
|
|
7971
|
-
version: ref.version
|
|
7972
|
-
} : {}
|
|
7973
|
-
}));
|
|
8226
|
+
return spawnSitesOf(def).map(spawn => logicalRefOf(spawn.definition));
|
|
7974
8227
|
}
|
|
7975
8228
|
|
|
7976
8229
|
function hashDefinitionContent(def) {
|
|
@@ -7993,7 +8246,10 @@ function planDefinitionDeploy({def: def, latest: latest, target: target}) {
|
|
|
7993
8246
|
tag: target.tag,
|
|
7994
8247
|
version: version,
|
|
7995
8248
|
contentHash: contentHash,
|
|
7996
|
-
...
|
|
8249
|
+
...modelStampFor({
|
|
8250
|
+
documentType: "definition",
|
|
8251
|
+
document: expanded
|
|
8252
|
+
})
|
|
7997
8253
|
};
|
|
7998
8254
|
return {
|
|
7999
8255
|
status: unchanged ? "unchanged" : "create",
|
|
@@ -8836,6 +9092,7 @@ async function fetchStartSlice(args) {
|
|
|
8836
9092
|
|
|
8837
9093
|
const workflow = {
|
|
8838
9094
|
deployDefinitions: async rawArgs => {
|
|
9095
|
+
assertReaderModelAcknowledgement(rawArgs.expectedMinReaderModel);
|
|
8839
9096
|
const args = taggedScope(rawArgs, REQUEST_TAG.deploy), {client: client, tag: tag, resourceAliases: resourceAliases} = args;
|
|
8840
9097
|
validateTag(tag);
|
|
8841
9098
|
const definitions = args.definitions.map(def => parseDefinitionInput(def, "workflow.deployDefinitions"));
|
|
@@ -8908,8 +9165,8 @@ const workflow = {
|
|
|
8908
9165
|
}), result;
|
|
8909
9166
|
},
|
|
8910
9167
|
startInstance: async rawArgs => {
|
|
8911
|
-
const args = taggedScope(rawArgs, REQUEST_TAG.start), {client: client, tag: tag,
|
|
8912
|
-
|
|
9168
|
+
const args = taggedScope(rawArgs, REQUEST_TAG.start), {client: client, tag: tag, definition: definitionName, version: version, initialFields: initialFields, instanceId: instanceId, executionContext: executionContext} = args, operationContext = await resolveOperationContext(args), {actor: actor, clientForGdr: clientForGdr, refSurface: refSurface} = operationContext, clock = args.clock ?? wallClock, seedFields = initialFields ?? [], existing = instanceId !== void 0 ? await getInstanceDocument(client, instanceId) : void 0;
|
|
9169
|
+
return existing !== void 0 && existing.tag === tag ? resumeStart({
|
|
8913
9170
|
client: client,
|
|
8914
9171
|
tag: tag,
|
|
8915
9172
|
existing: existing,
|
|
@@ -8926,105 +9183,12 @@ const workflow = {
|
|
|
8926
9183
|
...args.telemetry !== void 0 ? {
|
|
8927
9184
|
telemetry: args.telemetry
|
|
8928
9185
|
} : {}
|
|
8929
|
-
})
|
|
8930
|
-
|
|
8931
|
-
|
|
8932
|
-
definition: definitionName,
|
|
8933
|
-
version: version,
|
|
8934
|
-
tag: tag
|
|
8935
|
-
}), id = instanceId ?? instanceDocId(tag);
|
|
8936
|
-
if (definition.stages.find(s => s.name === definition.initialStage) === void 0) throw new Error(`Initial stage "${definition.initialStage}" missing in ${definitionName} v${definition.version}`);
|
|
8937
|
-
const now = clock();
|
|
8938
|
-
if (assertRequiredInputProvided({
|
|
8939
|
-
entryDefs: definition.fields ?? [],
|
|
8940
|
-
initialFields: seedFields,
|
|
8941
|
-
definitionName: definition.name
|
|
8942
|
-
}), definition.start?.allowed !== void 0) {
|
|
8943
|
-
const insight = await startAllowedInsight({
|
|
8944
|
-
client: client,
|
|
8945
|
-
tag: tag,
|
|
8946
|
-
definition: definition,
|
|
8947
|
-
allowed: definition.start.allowed,
|
|
8948
|
-
initialFields: seedFields,
|
|
8949
|
-
now: now
|
|
8950
|
-
});
|
|
8951
|
-
if (insight.outcome !== "satisfied") throw new StartNotAllowedError({
|
|
8952
|
-
definition: definition.name,
|
|
8953
|
-
insight: insight
|
|
8954
|
-
});
|
|
8955
|
-
}
|
|
8956
|
-
const contextEntries = context ? toContextEntries(context) : [], fieldDiscards = [], resolvedFields = await resolveDeclaredFields({
|
|
8957
|
-
entryDefs: definition.fields ?? [],
|
|
8958
|
-
initialFields: seedFields,
|
|
8959
|
-
ctx: {
|
|
8960
|
-
client: client,
|
|
8961
|
-
now: now,
|
|
8962
|
-
selfId: id,
|
|
8963
|
-
tag: tag,
|
|
8964
|
-
workflowResource: workflowResource,
|
|
8965
|
-
definitionName: definition.name,
|
|
8966
|
-
refSurface: refSurface,
|
|
8967
|
-
...perspective !== void 0 ? {
|
|
8968
|
-
perspective: perspective
|
|
8969
|
-
} : {},
|
|
8970
|
-
recordDiscard: recordFieldDiscards({
|
|
8971
|
-
target: fieldDiscards,
|
|
8972
|
-
scope: "workflow",
|
|
8973
|
-
at: now
|
|
8974
|
-
})
|
|
8975
|
-
},
|
|
8976
|
-
randomKey: randomKey
|
|
8977
|
-
}), effectivePerspective = perspective ?? derivePerspectiveFromFields(resolvedFields), base = buildInstanceBase({
|
|
8978
|
-
id: id,
|
|
8979
|
-
now: now,
|
|
8980
|
-
tag: tag,
|
|
8981
|
-
workflowResource: workflowResource,
|
|
8982
|
-
definitionName: definition.name,
|
|
8983
|
-
pinnedVersion: definition.version,
|
|
8984
|
-
pinnedContentHash: definition.contentHash,
|
|
8985
|
-
definition: definition,
|
|
8986
|
-
fields: resolvedFields,
|
|
8987
|
-
context: contextEntries,
|
|
8988
|
-
ancestors: ancestors ?? [],
|
|
8989
|
-
perspective: effectivePerspective,
|
|
8990
|
-
initialStage: definition.initialStage,
|
|
8991
|
-
actor: actor,
|
|
8992
|
-
executionContext: resolveExecutionContext(executionContext),
|
|
8993
|
-
...fieldDiscards.length > 0 ? {
|
|
8994
|
-
extraHistory: fieldDiscards
|
|
8995
|
-
} : {}
|
|
8996
|
-
});
|
|
8997
|
-
await client.create(base, SYNC_COMMIT);
|
|
8998
|
-
const cascaded = await settleStart({
|
|
8999
|
-
client: client,
|
|
9000
|
-
tag: tag,
|
|
9001
|
-
instanceId: id,
|
|
9002
|
-
actor: actor,
|
|
9003
|
-
clientForGdr: clientForGdr,
|
|
9004
|
-
refSurface: refSurface,
|
|
9186
|
+
}) : startFreshInstance({
|
|
9187
|
+
args: args,
|
|
9188
|
+
operationContext: operationContext,
|
|
9005
9189
|
clock: clock,
|
|
9006
|
-
|
|
9007
|
-
executionContext: executionContext
|
|
9008
|
-
} : {},
|
|
9009
|
-
...args.telemetry !== void 0 ? {
|
|
9010
|
-
telemetry: args.telemetry
|
|
9011
|
-
} : {},
|
|
9012
|
-
emitStarted: () => resolveTelemetry(args.telemetry).log(WorkflowInstanceStarted, instanceStartedData({
|
|
9013
|
-
definition: definition,
|
|
9014
|
-
instanceId: id,
|
|
9015
|
-
initialFieldCount: seedFields.length,
|
|
9016
|
-
viaSpawn: !1
|
|
9017
|
-
}))
|
|
9190
|
+
seedFields: seedFields
|
|
9018
9191
|
});
|
|
9019
|
-
return {
|
|
9020
|
-
instance: await reload({
|
|
9021
|
-
client: client,
|
|
9022
|
-
instanceId: id,
|
|
9023
|
-
tag: tag
|
|
9024
|
-
}),
|
|
9025
|
-
cascaded: cascaded,
|
|
9026
|
-
changed: !0
|
|
9027
|
-
};
|
|
9028
9192
|
},
|
|
9029
9193
|
fireAction: async rawArgs => {
|
|
9030
9194
|
const args = taggedScope(rawArgs, REQUEST_TAG.fireAction), {client: client, tag: tag, workflowResource: workflowResource, instanceId: instanceId, activity: activity, action: action, params: params, idempotent: idempotent, resourceClients: resourceClients, grantsFromPath: grantsFromPath, executionContext: executionContext} = args;
|
|
@@ -9510,7 +9674,7 @@ const workflow = {
|
|
|
9510
9674
|
return (await client.fetch(query, params)).map(readInstanceDoc).filter(instance => instanceWatchesDocument(instance, document));
|
|
9511
9675
|
},
|
|
9512
9676
|
definitionsForDocument: async rawArgs => {
|
|
9513
|
-
const args = taggedScope(rawArgs, REQUEST_TAG.definitionsForDocument), {client: client, tag: tag, document: document} = args;
|
|
9677
|
+
const args = taggedScope(rawArgs, REQUEST_TAG.definitionsForDocument), {client: client, tag: tag, document: document, subject: subject} = args;
|
|
9514
9678
|
validateTag(tag);
|
|
9515
9679
|
const clock = args.clock ?? wallClock, deployed = (await client.fetch(definitionsListGroq("desc"), {
|
|
9516
9680
|
tag: tag
|
|
@@ -9522,6 +9686,9 @@ const workflow = {
|
|
|
9522
9686
|
scope: {
|
|
9523
9687
|
tag: tag,
|
|
9524
9688
|
now: clock(),
|
|
9689
|
+
...subject !== void 0 ? {
|
|
9690
|
+
subject: subject
|
|
9691
|
+
} : {},
|
|
9525
9692
|
fetchDataset: () => slice ??= fetchStartSlice({
|
|
9526
9693
|
client: client,
|
|
9527
9694
|
tag: tag
|
|
@@ -9537,20 +9704,29 @@ const workflow = {
|
|
|
9537
9704
|
definition: definitionName,
|
|
9538
9705
|
version: version,
|
|
9539
9706
|
tag: tag
|
|
9707
|
+
}), invalidInitialFields = initialFieldIssues({
|
|
9708
|
+
workflowFields: definition.fields ?? [],
|
|
9709
|
+
stages: definition.stages,
|
|
9710
|
+
initialFields: initialFields ?? []
|
|
9540
9711
|
}), missingRequired = missingRequiredInputs({
|
|
9541
9712
|
entryDefs: definition.fields ?? [],
|
|
9542
9713
|
initialFields: initialFields ?? []
|
|
9543
9714
|
}), allowed = definition.start?.allowed;
|
|
9544
9715
|
if (allowed === void 0) return {
|
|
9545
|
-
allowed:
|
|
9716
|
+
allowed: invalidInitialFields.length === 0,
|
|
9546
9717
|
outcome: "satisfied",
|
|
9547
9718
|
unboundReads: [],
|
|
9548
|
-
missingRequired: missingRequired
|
|
9719
|
+
missingRequired: missingRequired,
|
|
9720
|
+
invalidInitialFields: invalidInitialFields
|
|
9549
9721
|
};
|
|
9550
|
-
const unboundReads =
|
|
9551
|
-
|
|
9552
|
-
|
|
9553
|
-
|
|
9722
|
+
const unboundReads = unboundAllowedReadsWithSubject({
|
|
9723
|
+
allowed: allowed,
|
|
9724
|
+
fields: startFieldsParam({
|
|
9725
|
+
entryDefs: definition.fields ?? [],
|
|
9726
|
+
initialFields: initialFields ?? []
|
|
9727
|
+
}),
|
|
9728
|
+
definition: definition
|
|
9729
|
+
}), insight = await startAllowedInsight({
|
|
9554
9730
|
client: client,
|
|
9555
9731
|
tag: tag,
|
|
9556
9732
|
definition: definition,
|
|
@@ -9559,11 +9735,12 @@ const workflow = {
|
|
|
9559
9735
|
now: clock()
|
|
9560
9736
|
}), provisional = unboundReads.length > 0;
|
|
9561
9737
|
return {
|
|
9562
|
-
allowed: !provisional && insight.outcome === "satisfied",
|
|
9738
|
+
allowed: invalidInitialFields.length === 0 && !provisional && insight.outcome === "satisfied",
|
|
9563
9739
|
outcome: provisional ? "unevaluable" : insight.outcome,
|
|
9564
9740
|
insight: insight,
|
|
9565
9741
|
unboundReads: unboundReads,
|
|
9566
|
-
missingRequired: missingRequired
|
|
9742
|
+
missingRequired: missingRequired,
|
|
9743
|
+
invalidInitialFields: invalidInitialFields
|
|
9567
9744
|
};
|
|
9568
9745
|
},
|
|
9569
9746
|
permissions: {
|
|
@@ -9593,6 +9770,239 @@ async function startAllowedInsight(args) {
|
|
|
9593
9770
|
});
|
|
9594
9771
|
}
|
|
9595
9772
|
|
|
9773
|
+
async function startFreshInstance(args) {
|
|
9774
|
+
const {args: startArgs, operationContext: operationContext, clock: clock, seedFields: seedFields} = args, {client: client, tag: tag, workflowResource: workflowResource, definition: definitionName, version: version, ancestors: ancestors, context: context, instanceId: instanceId, perspective: perspective, executionContext: executionContext} = startArgs, {actor: actor, clientForGdr: clientForGdr, refSurface: refSurface} = operationContext, definition = await loadDefinition({
|
|
9775
|
+
client: client,
|
|
9776
|
+
definition: definitionName,
|
|
9777
|
+
version: version,
|
|
9778
|
+
tag: tag
|
|
9779
|
+
}), id = instanceId ?? instanceDocId(tag);
|
|
9780
|
+
assertInitialStageExists(definition);
|
|
9781
|
+
const now = clock();
|
|
9782
|
+
await assertStartInputs({
|
|
9783
|
+
client: client,
|
|
9784
|
+
tag: tag,
|
|
9785
|
+
definition: definition,
|
|
9786
|
+
initialFields: seedFields,
|
|
9787
|
+
now: now
|
|
9788
|
+
});
|
|
9789
|
+
const {resolvedFields: resolvedFields, fieldDiscards: fieldDiscards} = await resolveStartFields({
|
|
9790
|
+
client: client,
|
|
9791
|
+
tag: tag,
|
|
9792
|
+
workflowResource: workflowResource,
|
|
9793
|
+
definition: definition,
|
|
9794
|
+
initialFields: seedFields,
|
|
9795
|
+
id: id,
|
|
9796
|
+
now: now,
|
|
9797
|
+
refSurface: refSurface,
|
|
9798
|
+
...perspective !== void 0 ? {
|
|
9799
|
+
perspective: perspective
|
|
9800
|
+
} : {}
|
|
9801
|
+
}), base = buildInstanceBase({
|
|
9802
|
+
id: id,
|
|
9803
|
+
now: now,
|
|
9804
|
+
tag: tag,
|
|
9805
|
+
workflowResource: workflowResource,
|
|
9806
|
+
definitionName: definition.name,
|
|
9807
|
+
pinnedVersion: definition.version,
|
|
9808
|
+
pinnedContentHash: definition.contentHash,
|
|
9809
|
+
definition: definition,
|
|
9810
|
+
fields: resolvedFields,
|
|
9811
|
+
context: context ? toContextEntries(context) : [],
|
|
9812
|
+
ancestors: ancestors ?? [],
|
|
9813
|
+
perspective: perspective ?? derivePerspectiveFromFields(resolvedFields),
|
|
9814
|
+
initialStage: definition.initialStage,
|
|
9815
|
+
actor: actor,
|
|
9816
|
+
executionContext: resolveExecutionContext(executionContext),
|
|
9817
|
+
...fieldDiscards.length > 0 ? {
|
|
9818
|
+
extraHistory: fieldDiscards
|
|
9819
|
+
} : {}
|
|
9820
|
+
});
|
|
9821
|
+
await client.create(base, SYNC_COMMIT);
|
|
9822
|
+
const cascaded = await settleStart({
|
|
9823
|
+
client: client,
|
|
9824
|
+
tag: tag,
|
|
9825
|
+
instanceId: id,
|
|
9826
|
+
actor: actor,
|
|
9827
|
+
clientForGdr: clientForGdr,
|
|
9828
|
+
refSurface: refSurface,
|
|
9829
|
+
clock: clock,
|
|
9830
|
+
...executionContext !== void 0 ? {
|
|
9831
|
+
executionContext: executionContext
|
|
9832
|
+
} : {},
|
|
9833
|
+
...startArgs.telemetry !== void 0 ? {
|
|
9834
|
+
telemetry: startArgs.telemetry
|
|
9835
|
+
} : {},
|
|
9836
|
+
emitStarted: () => resolveTelemetry(startArgs.telemetry).log(WorkflowInstanceStarted, instanceStartedData({
|
|
9837
|
+
definition: definition,
|
|
9838
|
+
instanceId: id,
|
|
9839
|
+
initialFieldCount: seedFields.length,
|
|
9840
|
+
viaSpawn: !1
|
|
9841
|
+
}))
|
|
9842
|
+
});
|
|
9843
|
+
return {
|
|
9844
|
+
instance: await reload({
|
|
9845
|
+
client: client,
|
|
9846
|
+
instanceId: id,
|
|
9847
|
+
tag: tag
|
|
9848
|
+
}),
|
|
9849
|
+
cascaded: cascaded,
|
|
9850
|
+
changed: !0
|
|
9851
|
+
};
|
|
9852
|
+
}
|
|
9853
|
+
|
|
9854
|
+
function assertInitialStageExists(definition) {
|
|
9855
|
+
if (!definition.stages.some(stage => stage.name === definition.initialStage)) throw new Error(`Initial stage "${definition.initialStage}" missing in ${definition.name} v${definition.version}`);
|
|
9856
|
+
}
|
|
9857
|
+
|
|
9858
|
+
async function assertStartInputs(args) {
|
|
9859
|
+
const {client: client, tag: tag, definition: definition, initialFields: initialFields, now: now} = args;
|
|
9860
|
+
assertInitialFieldsConsumable({
|
|
9861
|
+
workflowFields: definition.fields ?? [],
|
|
9862
|
+
stages: definition.stages,
|
|
9863
|
+
initialFields: initialFields,
|
|
9864
|
+
definitionName: definition.name
|
|
9865
|
+
}), assertRequiredInputProvided({
|
|
9866
|
+
entryDefs: definition.fields ?? [],
|
|
9867
|
+
initialFields: initialFields,
|
|
9868
|
+
definitionName: definition.name
|
|
9869
|
+
});
|
|
9870
|
+
const allowed = definition.start?.allowed;
|
|
9871
|
+
if (allowed === void 0) return;
|
|
9872
|
+
const insight = await startAllowedInsight({
|
|
9873
|
+
client: client,
|
|
9874
|
+
tag: tag,
|
|
9875
|
+
definition: definition,
|
|
9876
|
+
allowed: allowed,
|
|
9877
|
+
initialFields: initialFields,
|
|
9878
|
+
now: now
|
|
9879
|
+
});
|
|
9880
|
+
if (insight.outcome !== "satisfied") throw new StartNotAllowedError({
|
|
9881
|
+
definition: definition.name,
|
|
9882
|
+
insight: insight
|
|
9883
|
+
});
|
|
9884
|
+
}
|
|
9885
|
+
|
|
9886
|
+
async function resolveStartFields(args) {
|
|
9887
|
+
const {client: client, tag: tag, workflowResource: workflowResource, definition: definition, initialFields: initialFields, id: id, now: now, refSurface: refSurface, perspective: perspective} = args, fieldDiscards = [];
|
|
9888
|
+
return {
|
|
9889
|
+
resolvedFields: await resolveDeclaredFields({
|
|
9890
|
+
entryDefs: definition.fields ?? [],
|
|
9891
|
+
initialFields: initialFields,
|
|
9892
|
+
ctx: {
|
|
9893
|
+
client: client,
|
|
9894
|
+
now: now,
|
|
9895
|
+
selfId: id,
|
|
9896
|
+
tag: tag,
|
|
9897
|
+
workflowResource: workflowResource,
|
|
9898
|
+
definitionName: definition.name,
|
|
9899
|
+
refSurface: refSurface,
|
|
9900
|
+
...perspective !== void 0 ? {
|
|
9901
|
+
perspective: perspective
|
|
9902
|
+
} : {},
|
|
9903
|
+
recordDiscard: recordFieldDiscards({
|
|
9904
|
+
target: fieldDiscards,
|
|
9905
|
+
scope: "workflow",
|
|
9906
|
+
at: now
|
|
9907
|
+
})
|
|
9908
|
+
},
|
|
9909
|
+
randomKey: randomKey
|
|
9910
|
+
}),
|
|
9911
|
+
fieldDiscards: fieldDiscards
|
|
9912
|
+
};
|
|
9913
|
+
}
|
|
9914
|
+
|
|
9915
|
+
function isRecord(value) {
|
|
9916
|
+
return typeof value == "object" && value !== null && !Array.isArray(value);
|
|
9917
|
+
}
|
|
9918
|
+
|
|
9919
|
+
function isClientProjectUser(value) {
|
|
9920
|
+
return isRecord(value) && typeof value.id == "string" && (value.displayName === void 0 || typeof value.displayName == "string") && (value.email === void 0 || typeof value.email == "string") && (value.imageUrl === void 0 || value.imageUrl === null || typeof value.imageUrl == "string");
|
|
9921
|
+
}
|
|
9922
|
+
|
|
9923
|
+
function objectProperty(value, property) {
|
|
9924
|
+
if (!isRecord(value)) return;
|
|
9925
|
+
const propertyValue = value[property];
|
|
9926
|
+
return typeof propertyValue == "object" && propertyValue !== null ? propertyValue : void 0;
|
|
9927
|
+
}
|
|
9928
|
+
|
|
9929
|
+
function stringProperty(value, property) {
|
|
9930
|
+
const propertyValue = Reflect.get(value, property);
|
|
9931
|
+
return typeof propertyValue == "string" ? propertyValue : void 0;
|
|
9932
|
+
}
|
|
9933
|
+
|
|
9934
|
+
function apiErrorType(error) {
|
|
9935
|
+
const response = objectProperty(error, "response"), body = objectProperty(response, "body");
|
|
9936
|
+
if (!body) return;
|
|
9937
|
+
const nestedError = objectProperty(body, "error");
|
|
9938
|
+
return (nestedError ? stringProperty(nestedError, "type") : void 0) ?? stringProperty(body, "type");
|
|
9939
|
+
}
|
|
9940
|
+
|
|
9941
|
+
function isProjectUserNotFoundError(error) {
|
|
9942
|
+
return apiErrorType(error) === "projectUserNotFoundError";
|
|
9943
|
+
}
|
|
9944
|
+
|
|
9945
|
+
function clientProjectUserDirectory(client, projectId) {
|
|
9946
|
+
return {
|
|
9947
|
+
findById: async id => {
|
|
9948
|
+
if (!client.request) return {
|
|
9949
|
+
status: "inaccessible",
|
|
9950
|
+
cause: new Error("Project-user resolution requires WorkflowClient.request")
|
|
9951
|
+
};
|
|
9952
|
+
try {
|
|
9953
|
+
const response = await client.request({
|
|
9954
|
+
uri: `/projects/${encodeURIComponent(projectId)}/users/${encodeURIComponent(id)}`
|
|
9955
|
+
}), candidate = Array.isArray(response) ? response[0] : response;
|
|
9956
|
+
return candidate == null ? {
|
|
9957
|
+
status: "missing"
|
|
9958
|
+
} : isClientProjectUser(candidate) ? {
|
|
9959
|
+
status: "resolved",
|
|
9960
|
+
user: candidate
|
|
9961
|
+
} : {
|
|
9962
|
+
status: "inaccessible",
|
|
9963
|
+
cause: new Error("Project-user response had an invalid shape")
|
|
9964
|
+
};
|
|
9965
|
+
} catch (cause) {
|
|
9966
|
+
return isProjectUserNotFoundError(cause) ? {
|
|
9967
|
+
status: "missing"
|
|
9968
|
+
} : {
|
|
9969
|
+
status: "inaccessible",
|
|
9970
|
+
cause: cause
|
|
9971
|
+
};
|
|
9972
|
+
}
|
|
9973
|
+
}
|
|
9974
|
+
};
|
|
9975
|
+
}
|
|
9976
|
+
|
|
9977
|
+
function resolveClientActor(client, args) {
|
|
9978
|
+
return resolveActor(clientProjectUserDirectory(client, args.projectId), args.actor);
|
|
9979
|
+
}
|
|
9980
|
+
|
|
9981
|
+
async function resolveActor(directory, actor) {
|
|
9982
|
+
if (actor.kind !== "person") return {
|
|
9983
|
+
status: "not-person",
|
|
9984
|
+
actor: actor
|
|
9985
|
+
};
|
|
9986
|
+
const personActor = {
|
|
9987
|
+
...actor,
|
|
9988
|
+
kind: "person"
|
|
9989
|
+
}, lookup = await directory.findById(personActor.id);
|
|
9990
|
+
return lookup.status === "resolved" ? {
|
|
9991
|
+
status: "resolved",
|
|
9992
|
+
actor: personActor,
|
|
9993
|
+
user: lookup.user
|
|
9994
|
+
} : lookup.status === "inaccessible" ? {
|
|
9995
|
+
status: "inaccessible",
|
|
9996
|
+
actor: personActor,
|
|
9997
|
+
...lookup.cause === void 0 ? {} : {
|
|
9998
|
+
cause: lookup.cause
|
|
9999
|
+
}
|
|
10000
|
+
} : {
|
|
10001
|
+
status: "missing",
|
|
10002
|
+
actor: personActor
|
|
10003
|
+
};
|
|
10004
|
+
}
|
|
10005
|
+
|
|
9596
10006
|
const DEFAULT_EFFECT_LEASE_MS = 300 * 1e3;
|
|
9597
10007
|
|
|
9598
10008
|
function isClaimExpired(claim, now) {
|
|
@@ -9735,11 +10145,11 @@ function isCancelledCompletion(data) {
|
|
|
9735
10145
|
}
|
|
9736
10146
|
|
|
9737
10147
|
async function drainEffectsInternal(args) {
|
|
9738
|
-
const {tag: tag, workflowResource: workflowResource, instanceId: instanceId, effectHandlers: effectHandlers, missingHandler: missingHandler, logger: logger} = args, cascadeTelemetry = drainCascadeTelemetry(args.telemetry), {client: client, resourceClients: resourceClients} = taggedScope(args, REQUEST_TAG.drain), leaseMs = args.leaseMs ?? DEFAULT_EFFECT_LEASE_MS, clock = args.clock ?? wallClock, routeGdr = buildClientForGdr({
|
|
9739
|
-
client:
|
|
10148
|
+
const {tag: tag, workflowResource: workflowResource, instanceId: instanceId, effectHandlers: effectHandlers, missingHandler: missingHandler, logger: logger, handlerClient: handlerClient, handlerResourceClients: handlerResourceClients} = args, cascadeTelemetry = drainCascadeTelemetry(args.telemetry), {client: client, resourceClients: resourceClients} = taggedScope(args, REQUEST_TAG.drain), leaseMs = args.leaseMs ?? DEFAULT_EFFECT_LEASE_MS, clock = args.clock ?? wallClock, routeGdr = buildClientForGdr({
|
|
10149
|
+
client: handlerClient,
|
|
9740
10150
|
workflowResource: workflowResource,
|
|
9741
|
-
resourceClients:
|
|
9742
|
-
}), clientFor = ref =>
|
|
10151
|
+
resourceClients: handlerResourceClients
|
|
10152
|
+
}), clientFor = ref => routeGdr(parseGdr(typeof ref == "string" ? ref : ref.id)), {actor: drainerActor} = await resolveAccess(client), executionContext = args.executionContext;
|
|
9743
10153
|
validateTag(tag);
|
|
9744
10154
|
const log = logger("drainEffects"), buckets = {
|
|
9745
10155
|
drained: [],
|
|
@@ -9785,6 +10195,7 @@ async function drainEffectsInternal(args) {
|
|
|
9785
10195
|
handler: handler,
|
|
9786
10196
|
candidate: candidate,
|
|
9787
10197
|
client: client,
|
|
10198
|
+
handlerClient: handlerClient,
|
|
9788
10199
|
tag: tag,
|
|
9789
10200
|
workflowResource: workflowResource,
|
|
9790
10201
|
...resourceClients !== void 0 ? {
|
|
@@ -9810,11 +10221,11 @@ function findClaimableCandidate({instance: instance, now: now, skippedKeys: skip
|
|
|
9810
10221
|
}
|
|
9811
10222
|
|
|
9812
10223
|
async function dispatchAndReport(args) {
|
|
9813
|
-
const {handler: handler, candidate: candidate, client: client, tag: tag, workflowResource: workflowResource, resourceClients: resourceClients, instanceId: instanceId, clientFor: clientFor, logger: logger, log: log, executionContext: executionContext, telemetry: telemetry, clock: clock} = args, {outputs: outputs, ops: ops, dispatchError: dispatchError} = await dispatchEffect({
|
|
10224
|
+
const {handler: handler, candidate: candidate, client: client, handlerClient: handlerClient, tag: tag, workflowResource: workflowResource, resourceClients: resourceClients, instanceId: instanceId, clientFor: clientFor, logger: logger, log: log, executionContext: executionContext, telemetry: telemetry, clock: clock} = args, {outputs: outputs, ops: ops, dispatchError: dispatchError} = await dispatchEffect({
|
|
9814
10225
|
handler: handler,
|
|
9815
10226
|
candidate: candidate,
|
|
9816
10227
|
ctx: {
|
|
9817
|
-
client:
|
|
10228
|
+
client: handlerClient,
|
|
9818
10229
|
clientFor: clientFor,
|
|
9819
10230
|
instanceId: instanceId,
|
|
9820
10231
|
logger: logger
|
|
@@ -9920,7 +10331,7 @@ async function claimPendingEffect({client: client, instance: instance, candidate
|
|
|
9920
10331
|
async function dispatchEffect({handler: handler, candidate: candidate, ctx: ctx}) {
|
|
9921
10332
|
try {
|
|
9922
10333
|
const result = await handler(candidate.params, {
|
|
9923
|
-
client:
|
|
10334
|
+
client: ctx.client,
|
|
9924
10335
|
clientFor: ctx.clientFor,
|
|
9925
10336
|
instanceId: ctx.instanceId,
|
|
9926
10337
|
effectKey: candidate._key,
|
|
@@ -10414,6 +10825,7 @@ function createEngine(args) {
|
|
|
10414
10825
|
missingHandler: missingHandler,
|
|
10415
10826
|
logger: logger,
|
|
10416
10827
|
telemetry: telemetry,
|
|
10828
|
+
resolveActor: rest => resolveClientActor(client, rest),
|
|
10417
10829
|
deployDefinitions: async rest => {
|
|
10418
10830
|
const result = await workflow.deployDefinitions(withScope(rest));
|
|
10419
10831
|
return logDeployWarnings(result, logger), result;
|
|
@@ -10454,6 +10866,10 @@ function createEngine(args) {
|
|
|
10454
10866
|
drainEffects: async ({instanceId: instanceId}) => {
|
|
10455
10867
|
const result = await drainEffectsInternal({
|
|
10456
10868
|
client: client,
|
|
10869
|
+
handlerClient: args.client,
|
|
10870
|
+
...args.resourceClients !== void 0 ? {
|
|
10871
|
+
handlerResourceClients: args.resourceClients
|
|
10872
|
+
} : {},
|
|
10457
10873
|
tag: tag,
|
|
10458
10874
|
workflowResource: workflowResource,
|
|
10459
10875
|
...optionalScope,
|
|
@@ -10650,6 +11066,7 @@ function attributeMember({member: member, group: group, chain: chain}) {
|
|
|
10650
11066
|
}
|
|
10651
11067
|
|
|
10652
11068
|
function diffEntry({def: rawDef, latestRaw: latestRaw, target: target}) {
|
|
11069
|
+
assertReaderModelAcknowledgement(target.expectedMinReaderModel);
|
|
10653
11070
|
const def = parseDefinitionInput(rawDef, "diffEntry"), plan = planDefinitionDeploy({
|
|
10654
11071
|
def: def,
|
|
10655
11072
|
latest: asLatest(latestRaw),
|
|
@@ -10681,6 +11098,7 @@ function asLatest(raw) {
|
|
|
10681
11098
|
}
|
|
10682
11099
|
|
|
10683
11100
|
async function computeDiffEntries({client: client, defs: defs, target: target}) {
|
|
11101
|
+
assertReaderModelAcknowledgement(target.expectedMinReaderModel);
|
|
10684
11102
|
const entries = [];
|
|
10685
11103
|
for (const rawDef of defs) {
|
|
10686
11104
|
const def = parseDefinitionInput(rawDef, "computeDiffEntries"), latest = await loadLatestDeployed({
|
|
@@ -10702,7 +11120,7 @@ function buildInitialFields({declared: declared, values: values}) {
|
|
|
10702
11120
|
return Object.entries(values).map(([name, value]) => {
|
|
10703
11121
|
const entry = declared.find(f => f.name === name);
|
|
10704
11122
|
if (entry === void 0) throw new Error(settable.length > 0 ? `workflow declares no field "${name}" — input fields: ${settableNames}` : `workflow declares no field "${name}" — it declares no input fields at all`);
|
|
10705
|
-
if (entry.initialValue?.type !== "input") throw new Error(`field "${name}" is not input-sourced
|
|
11123
|
+
if (entry.initialValue?.type !== "input") throw new Error(`field "${name}" is not input-sourced and cannot accept a start value. ` + (settable.length > 0 ? `Input fields: ${settableNames}` : "This workflow has no input fields."));
|
|
10706
11124
|
return {
|
|
10707
11125
|
type: entry.type,
|
|
10708
11126
|
name: name,
|
|
@@ -10766,7 +11184,7 @@ const HISTORY_DISPLAY = {
|
|
|
10766
11184
|
},
|
|
10767
11185
|
aborted: {
|
|
10768
11186
|
title: "Instance aborted",
|
|
10769
|
-
description: "An admin hard-stopped the instance — pending effects cancelled, stage guards
|
|
11187
|
+
description: "An admin hard-stopped the instance — pending effects cancelled, stage guards removed, abortedAt + completedAt stamped."
|
|
10770
11188
|
},
|
|
10771
11189
|
opApplied: {
|
|
10772
11190
|
title: "Op applied",
|
|
@@ -10802,6 +11220,10 @@ const HISTORY_DISPLAY = {
|
|
|
10802
11220
|
title: "Document references",
|
|
10803
11221
|
description: "Ordered list of GDR pointers — multi-doc selection."
|
|
10804
11222
|
},
|
|
11223
|
+
subject: {
|
|
11224
|
+
title: "Subject document",
|
|
11225
|
+
description: "Single GDR pointer at THE document the workflow is about — same value as a document reference, elevated so surfaces identify the subject by kind."
|
|
11226
|
+
},
|
|
10805
11227
|
"release.ref": {
|
|
10806
11228
|
title: "Content Release reference",
|
|
10807
11229
|
description: "GDR pointer at a Content Release system doc, carrying the release name the engine derives the instance's read perspective from."
|
|
@@ -10980,4 +11402,4 @@ function displayDescription(typeKey) {
|
|
|
10980
11402
|
if (typeKey) return DISPLAY[typeKey]?.description;
|
|
10981
11403
|
}
|
|
10982
11404
|
|
|
10983
|
-
export { ACTIVITY_KINDS, ACTIVITY_KIND_DISPLAY, ACTOR_KINDS, AUTHORING_DISPLAY, ActionDisabledError, ActionParamsInvalidError, CONDITION_VARS, CONTEXT_ENTRY_DISPLAY, CascadeLimitError, ConcurrentCompleteEffectError, ConcurrentEditFieldError, ConcurrentFireActionError, ContractViolationError, DATA_MODEL_MIN_READER, DATA_MODEL_VERSION, DEFAULT_CONTENT_PERSPECTIVE, DEFAULT_EFFECT_LEASE_MS, DEFAULT_IDEMPOTENCY_TTL_MS, DEFAULT_TRANSITION_WHEN, DISPLAY, DRIVER_KINDS, DRIVER_KIND_DISPLAY, DefinitionInUseError, DefinitionNotFoundError, ENGINE_API_VERSION, EXECUTION_KINDS, EXECUTOR_CLASSIFICATIONS, EXECUTOR_CLASSIFICATION_DISPLAY, EditFieldDeniedError, EffectNotFoundError, EffectOpsInvalidError, EffectOutputsInvalidError, FIELD_KIND_DISPLAY, FILTER_SCOPE_VARS, FieldValueShapeError, GROUP_KINDS, GROUP_KIND_DISPLAY, GUARD_DOC_TYPE,
|
|
11405
|
+
export { ACTION_SEMANTICS, ACTIVITY_KINDS, ACTIVITY_KIND_DISPLAY, ACTOR_KINDS, AUTHORING_DISPLAY, ActionDisabledError, ActionParamsInvalidError, CONDITION_VARS, CONTEXT_ENTRY_DISPLAY, CascadeLimitError, ConcurrentCompleteEffectError, ConcurrentEditFieldError, ConcurrentFireActionError, ContractViolationError, DATA_MODEL_CHANGES, DATA_MODEL_MIN_READER, DATA_MODEL_VERSION, DEFAULT_CONTENT_PERSPECTIVE, DEFAULT_EFFECT_LEASE_MS, DEFAULT_IDEMPOTENCY_TTL_MS, DEFAULT_TRANSITION_WHEN, DISPLAY, DRIVER_KINDS, DRIVER_KIND_DISPLAY, DefinitionInUseError, DefinitionNotFoundError, ENGINE_API_VERSION, EXECUTION_KINDS, EXECUTOR_CLASSIFICATIONS, EXECUTOR_CLASSIFICATION_DISPLAY, EditFieldDeniedError, EffectNotFoundError, EffectOpsInvalidError, EffectOutputsInvalidError, FIELD_KIND_DISPLAY, FILTER_SCOPE_VARS, FieldValueShapeError, GROUP_KINDS, GROUP_KIND_DISPLAY, GUARD_DOC_TYPE, GUARD_OWNER, GUARD_PREDICATE_VARS, HISTORY_DISPLAY, InitialFieldsInvalidError, InstanceNotFoundError, MAX_COUNTERFACTUAL_INDEX2 as MAX_COUNTERFACTUAL_INDEX, MissingHandlerError, ModelVersionAheadError, MutationGuardDeniedError, MutationGuardDocSchema, OP_DISPLAY, PartialGuardDeployError, PersistedDocShapeError, READER_MODEL_ROLLOUT_URL, RESERVED_CONDITION_VARS, ReaderModelAcknowledgementError, RefResourceUndeclaredError, RequiredFieldNotProvidedError, START_ALLOWED_VARS, START_FILTER_VARS, SpawnContractsInvalidError, StartNotAllowedError, StartNotPrimedError, StartNotSettledError, WORKFLOW_DEFINITION_TYPE, WORKFLOW_INSTANCE_TYPE, WorkflowActionFired, WorkflowDefinitionDeleted, WorkflowDefinitionDeployed, WorkflowEffectCompleted, WorkflowEffectsDrained, WorkflowError, WorkflowFieldEdited, WorkflowInstanceAborted, WorkflowInstanceSchema, WorkflowInstanceStarted, WorkflowInstanceTicked, WorkflowStageSet, WorkflowStageTransitioned, WorkflowStateDivergedError, abortReason, acceptsDocumentType, aclPathForResource, actionDisabledDetail, actionRendering, actionVerdict, activityAutonomyOf, analyzeCondition2 as analyzeCondition, applicableDefinitions, assertReadableModel, assertReaderModelAcknowledgement, atomReadsDataset2 as atomReadsDataset, autonomySummary, availableActions, buildInitialFields, buildSnapshot, checklistLines, clientConfigFromResource, clientProjectUserDirectory, compileGuard, computeDiffEntries, conditionFieldReadNames, conditionSitesOf, contentDocQuery, contentDraftFallback, contentReleaseName, contextMap, createEngine, createTelemetryIntake, datasetResourceParts, defaultLoggerFactory, definitionDeployedData, definitionLookupGroq, definitionsListGroq, deniedGuardLabels, deniedGuardRefs, denyingGuards, deployStageGuards, deriveActivityKind, deriveExecutorClassification, deriveWorkflowAutonomy, describeAtom, describeAutonomyWait, describeCondition, describeDefinition, describeFieldInsight, describeNode, describeSite, describeSiteHeading, diagnoseInputFromEvaluation, diagnoseInstance, diffEntry, displayDescription, displayTitle, documentActionDenials, documentPrefilter, driverKind, effectOutputsMap, entryDocRefs, errorMessage, evaluateFromSnapshot, evaluateMutationGuard, evaluateStartFilter, expandResourceAliases, explainCondition2 as explainCondition, explainStartAllowed, extractDocumentId, fieldTreeShape, findCurrentActivityEntry, findOpenStageEntry, formatRead, gdrFromResource, gdrRef, gdrUri, groupMembershipNames, groupSitesOf, guardMatches, guardsForDefinition, guardsForInstance, guardsForResource, guillemets2 as guillemets, hashDefinitionContent, humanize2 as humanize, inFlightFilter, initialFieldIssues, instanceDocId, instanceGuardQuery, instanceWatchesDocument, instancesQuery, isCascadeFired, isClaimExpired, isComparisonOp, isDefinitionApplicable, isFilterScopedOut, isGdr, isInputSourced, isNotesEntry, isProjectUserNotFoundError, isSingleDocRefEntry, isSingleDocRefKind, isStartableDefinition, isSubjectEntry, isTelemetryEnvDenied, isTerminalActivityStatus, isTerminalStage, isTodoListEntry, isTodoListItem, isUnprimed, lakeGuardId, latestDeployedDefinitions, lintEffectOutputs, minReaderModelOf, missingRequiredInputs, modelVersionOf, narrateAutonomyWaits, noopTelemetry, parentRef, parseDefinitionInput, parseDefinitionSnapshot, parseGdr, parseGuardDocument, parseInstanceDocument, parseResourceGdr, processShellUserProperties, projectToWatchRef, quoted2 as quoted, readInstanceDoc, readsRaw, readsRootDocument, refCanvas, refDashboard, refDataset, refKindAcceptsTypes, refMediaLibrary, refsOf, rejectedRefTypes, releaseDocId, releaseRef, remediationsFor, requiredModelFeatures, requiredReaderModel, resolveAccess, resolveActor, resolveClientActor, resolveFieldEntry$1 as resolveFieldEntry, resourceAliasesToMap, resourceFromParsed, resourceGdr, retractStageGuards, sameResource, scalarValidationIssues, schemaTreeShape, sentenceCase, silentLogger, stageAutonomyOf, startFieldsParam, startKindOf, startRefusal, stripSystemFields, subjectDenialLabels, subscriptionDocument, subscriptionDocumentsForInstance, sweepStaleClaims, tagScopeFilter, terminalState, toBareId, tryParseGdr, unboundAllowedReads, unsatisfiedTransitionSummaries, validateDefinition, validateTag, verdictGuardsForInstance, wallClock, whatIfCondition2 as whatIfCondition, withAssignment, workflow };
|