@sanity/workflow-engine 0.31.0 → 0.33.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 +323 -0
- package/DATAMODEL.md +223 -9
- package/README.md +2 -2
- package/dist/_chunks-cjs/invariants.cjs +2113 -1940
- package/dist/_chunks-es/invariants.js +2041 -1918
- package/dist/define.cjs +6 -3
- package/dist/define.d.cts +668 -1195
- package/dist/define.d.ts +668 -1195
- package/dist/define.js +7 -4
- package/dist/index.cjs +2513 -968
- package/dist/index.d.cts +1467 -1923
- package/dist/index.d.ts +1467 -1923
- package/dist/index.js +2432 -927
- package/package.json +6 -4
package/dist/index.js
CHANGED
|
@@ -1,45 +1,16 @@
|
|
|
1
|
-
import { rethrowWithContext, andConditions, deriveActivityKind, CALLER_BOUND_VARS, START_REQUIREMENT_VARS, CONDITION_VARS, isSubjectEntry, isUnevaluable, ContractViolationError, isStartableDefinition, conditionFieldReadNames, isGdr, errorMessage, conditionParameterNames, START_FILTER_VARS, gdrFromResource, selfGdr,
|
|
1
|
+
import { rethrowWithContext, findOpenStageEntry, findCurrentActivityEntry, andConditions, deriveActivityKind, CALLER_BOUND_VARS, START_REQUIREMENT_VARS, CONDITION_VARS, isSubjectEntry, isUnevaluable, ContractViolationError, isStartableDefinition, conditionFieldReadNames, isGdr, errorMessage, conditionParameterNames, START_FILTER_VARS, WorkflowError, gdrFromResource, selfGdr, isGdrUri, MUTATION_GUARD_ID_SPACES, mutationGuardActionIdSpace, LAKE_MUTATION_GUARD_ACTIONS, isBareDocumentId, toBareId, normalizeAssignmentMembers, isSingleDocRefKind, actorMatchesAssignment, assignmentMembers, resolvedChildStatus, sameResource, resourceFromParsed, tryParseGdr, isTerminalActivityStatus, FieldValueShapeError, validateFieldValue, validateFieldAppendItem, isAssignmentFieldEntry, isAlwaysArrayFieldKind, evaluateCondition, isRecord, isSingleDocRefEntry, choiceValueIssues, scalarValidationIssues, StoredFieldOpSchema, formatIssues, conditionSyntaxIssues, checkWorkflowInvariants, formatIssuePath, isGuardReadExpr, GUARD_ACTIONS_REQUIRED_MESSAGE, conditionEffectReads, EFFECTS_READ, datasetResourceParts, refKindAcceptsTypes, mutationGuardRequiresSplitEmission, MUTATION_GUARD_ACTIONS, assignmentKindAcceptsRoles, evaluatePredicates, WORKFLOW_DEFINITION_TYPE, tagScopeFilter, liveSubworkflows, isCascadeFired, classifyPrincipalId, directoryBridgeId, parseGdr, gdrRef, isInputSourced, parseFieldValue, VersionSpecificDatasetGdrError, toPhysicalGdr, isBareSeedId, isParseableInstant, NonEmptyString, tolerantEntries, FIELD_VALUE_KINDS, tolerantObject, ActorShape, IsoTimestamp, ACTIVITY_STATUSES, GdrShape, DRIVER_KINDS, FIELD_SCOPES, fieldValueSchemas, parsePersistedDoc, ACTOR_KINDS, InstanceNotFoundError, evaluateConditionOutcome, runGroq as runGroq$1, FIELD_READ, resourceGdr, condemnSubworkflow, resourceFromGdrUri, refTypeIssues, rejectedRefTypes, DOCUMENT_VALUE_PERMISSIONS, lakePrincipalId, firstCarriedGlobalId, driverKind, actorFulfillsRole, condemnedSubworkflows, EffectNotFoundError, isFilterScopedOut, deriveExecutorClassification, validateTag, activeAssignmentMembers, extractDocumentId, UNIVERSAL_ROLE_ALIAS_KEY, parseStoredDefinition, validateResourceAliasName, labelFor, DefinitionNotFoundError, definitionDocId, SpawnContractsInvalidError, gdrResourcePrefix, RESOURCE_ALIAS_NAME_SOURCE, DefinitionInUseError, effectBackoffMs, groupMembershipNames } from "./_chunks-es/invariants.js";
|
|
2
2
|
|
|
3
|
-
import { ACTION_SEMANTICS, ACTIVITY_KINDS, ANONYMOUS_IDENTITY, DECISION_SEMANTICS, DEFAULT_TRANSITION_WHEN, EXECUTOR_CLASSIFICATIONS, FILTER_SCOPE_VARS, GROUP_KINDS, GUARD_PREDICATE_VARS, PersistedDocShapeError, RESERVED_CONDITION_VARS, SIGNAL_SEMANTICS, SYSTEM_IDENTITY, clientConfigFromResource, gdrUri, isNotesEntry, isTodoListEntry, isTodoListItem, parseResourceGdr, readsRootDocument, refCanvas, refDashboard, refDataset,
|
|
3
|
+
import { ACTION_SEMANTICS, ACTIVITY_KINDS, ANONYMOUS_IDENTITY, DECISION_SEMANTICS, DEFAULT_TRANSITION_WHEN, EXECUTOR_CLASSIFICATIONS, FILTER_SCOPE_VARS, GROUP_KINDS, GUARD_PREDICATE_VARS, PersistedDocShapeError, RESERVED_CONDITION_VARS, SIGNAL_SEMANTICS, SYSTEM_IDENTITY, assignmentMatch, assignmentState, assignmentStateCounts, clientConfigFromResource, gdrUri, identityMatchesAssignment, instanceAssignmentStateCounts, isNotesEntry, isTodoListEntry, isTodoListItem, openActivityAssignments, parseResourceGdr, readsRootDocument, refCanvas, refDashboard, refDataset, refMediaLibrary, releaseDocId, releaseRef, resourceAliasesToMap, schemaTreeShape, startKindOf } from "./_chunks-es/invariants.js";
|
|
4
4
|
|
|
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,
|
|
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, _exhaustiveOptions, whatIfCondition, runGroq, formatRead, dedupeReads, MAX_COUNTERFACTUAL_INDEX } from "@sanity/groq-condition-describe";
|
|
6
6
|
|
|
7
|
-
import { MAX_COUNTERFACTUAL_INDEX as MAX_COUNTERFACTUAL_INDEX2, NEUTRAL_MARK, OUTCOME_MARKS, 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";
|
|
7
|
+
import { MAX_COUNTERFACTUAL_INDEX as MAX_COUNTERFACTUAL_INDEX2, NEUTRAL_MARK, OUTCOME_MARKS, analyzeCondition as analyzeCondition2, atomReadsDataset as atomReadsDataset2, explainCondition as explainCondition2, formatRead as formatRead2, guillemets as guillemets2, humanize as humanize2, isComparisonOp, quoted as quoted2, sentenceCase, whatIfCondition as whatIfCondition2, withAssignment } from "@sanity/groq-condition-describe";
|
|
8
8
|
|
|
9
9
|
import { evaluate, parse } from "groq-js";
|
|
10
10
|
|
|
11
|
-
import
|
|
12
|
-
|
|
13
|
-
function isFilterScopedOut(entry) {
|
|
14
|
-
return entry.status === "skipped" && entry.startedAt === void 0;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function liveSubworkflows(host) {
|
|
18
|
-
return (host.subworkflows ?? []).filter(row => row.resolved === void 0);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function resolvedChildStatus(child) {
|
|
22
|
-
if (!(child.completedAt === void 0 || child.completedAt === null)) return child.abortedAt !== void 0 && child.abortedAt !== null ? "aborted" : "done";
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function condemnedSubworkflows(host) {
|
|
26
|
-
return liveSubworkflows(host).filter(row => row.abortPending !== void 0);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function condemnSubworkflow(row, owed) {
|
|
30
|
-
row.abortPending !== void 0 || row.resolved !== void 0 || (row.abortPending = {
|
|
31
|
-
at: owed.at,
|
|
32
|
-
reason: owed.reason
|
|
33
|
-
});
|
|
34
|
-
}
|
|
11
|
+
import { getPublishedId, getDraftId, DocumentId } from "@sanity/id-utils";
|
|
35
12
|
|
|
36
|
-
|
|
37
|
-
return host.stages.find(s => s.name === host.currentStage && s.exitedAt === void 0);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function findCurrentActivityEntry(host, activityName) {
|
|
41
|
-
return findOpenStageEntry(host)?.activities.find(a => a.name === activityName);
|
|
42
|
-
}
|
|
13
|
+
import * as v from "valibot";
|
|
43
14
|
|
|
44
15
|
const WORKFLOW_INSTANCE_TYPE = "sanity.workflow.instance";
|
|
45
16
|
|
|
@@ -98,6 +69,10 @@ function effectiveEditable(baseline, override) {
|
|
|
98
69
|
|
|
99
70
|
const DEFAULT_EDIT_MODE = "set";
|
|
100
71
|
|
|
72
|
+
function _fieldTargetLabel(target) {
|
|
73
|
+
return [ target.scope, target.activity, target.field ].filter(Boolean).join(".");
|
|
74
|
+
}
|
|
75
|
+
|
|
101
76
|
function fieldSites(definition, stage) {
|
|
102
77
|
const sites = [];
|
|
103
78
|
for (const entry of definition.fields ?? []) sites.push({
|
|
@@ -458,6 +433,11 @@ function rolesGateOf(atom) {
|
|
|
458
433
|
if (!(count.type !== "FuncCall" || count.name !== "count" || count.args.length !== 1)) return rolesFilterList(count.args[0]);
|
|
459
434
|
}
|
|
460
435
|
|
|
436
|
+
function isAssignedGate(atom) {
|
|
437
|
+
const node = atomNode(atom);
|
|
438
|
+
return !atom.negated && node.type === "Parameter" && node.name === "assigned";
|
|
439
|
+
}
|
|
440
|
+
|
|
461
441
|
function rolesFilterList(node) {
|
|
462
442
|
if (node.type !== "Filter") return;
|
|
463
443
|
const base = node.base;
|
|
@@ -1069,6 +1049,7 @@ function describeSiteHeading(address, ctx) {
|
|
|
1069
1049
|
}
|
|
1070
1050
|
|
|
1071
1051
|
case "action":
|
|
1052
|
+
case "action-when":
|
|
1072
1053
|
{
|
|
1073
1054
|
const title = actionTitle(address, ctx), activity = activityTitle(address.activity, ctx);
|
|
1074
1055
|
return phrase(id, {
|
|
@@ -1076,7 +1057,7 @@ function describeSiteHeading(address, ctx) {
|
|
|
1076
1057
|
title: title,
|
|
1077
1058
|
activity: activity
|
|
1078
1059
|
},
|
|
1079
|
-
text: `action ${quoted(title)} on ${quoted(activity)} is available when:`
|
|
1060
|
+
text: `action ${quoted(title)} on ${quoted(activity)} ${address.kind === "action" ? "is available" : "fires automatically"} when:`
|
|
1080
1061
|
});
|
|
1081
1062
|
}
|
|
1082
1063
|
|
|
@@ -1103,18 +1084,6 @@ function describeSiteHeading(address, ctx) {
|
|
|
1103
1084
|
});
|
|
1104
1085
|
}
|
|
1105
1086
|
|
|
1106
|
-
case "action-when":
|
|
1107
|
-
{
|
|
1108
|
-
const title = actionTitle(address, ctx), activity = activityTitle(address.activity, ctx);
|
|
1109
|
-
return phrase(id, {
|
|
1110
|
-
params: {
|
|
1111
|
-
title: title,
|
|
1112
|
-
activity: activity
|
|
1113
|
-
},
|
|
1114
|
-
text: `action ${quoted(title)} on ${quoted(activity)} fires automatically when:`
|
|
1115
|
-
});
|
|
1116
|
-
}
|
|
1117
|
-
|
|
1118
1087
|
case "editable-field":
|
|
1119
1088
|
{
|
|
1120
1089
|
const field = fieldTitleOf({
|
|
@@ -1158,6 +1127,7 @@ function describeSite(site, ctx) {
|
|
|
1158
1127
|
}
|
|
1159
1128
|
|
|
1160
1129
|
case "action":
|
|
1130
|
+
case "action-when":
|
|
1161
1131
|
{
|
|
1162
1132
|
const title = actionTitle(site, ctx), activity = activityTitle(site.activity, ctx);
|
|
1163
1133
|
return phrase(id, {
|
|
@@ -1165,7 +1135,7 @@ function describeSite(site, ctx) {
|
|
|
1165
1135
|
title: title,
|
|
1166
1136
|
activity: activity
|
|
1167
1137
|
},
|
|
1168
|
-
text: `the ${quoted(title)} action on ${quoted(activity)}`
|
|
1138
|
+
text: `the ${quoted(title)} ${site.kind === "action" ? "action" : "trigger"} on ${quoted(activity)}`
|
|
1169
1139
|
});
|
|
1170
1140
|
}
|
|
1171
1141
|
|
|
@@ -1181,18 +1151,6 @@ function describeSite(site, ctx) {
|
|
|
1181
1151
|
});
|
|
1182
1152
|
}
|
|
1183
1153
|
|
|
1184
|
-
case "action-when":
|
|
1185
|
-
{
|
|
1186
|
-
const title = actionTitle(site, ctx), activity = activityTitle(site.activity, ctx);
|
|
1187
|
-
return phrase(id, {
|
|
1188
|
-
params: {
|
|
1189
|
-
title: title,
|
|
1190
|
-
activity: activity
|
|
1191
|
-
},
|
|
1192
|
-
text: `the ${quoted(title)} trigger on ${quoted(activity)}`
|
|
1193
|
-
});
|
|
1194
|
-
}
|
|
1195
|
-
|
|
1196
1154
|
case "activity-filter":
|
|
1197
1155
|
{
|
|
1198
1156
|
const activity = activityTitle(site.activity, ctx);
|
|
@@ -1250,10 +1208,10 @@ function unsatisfiedTransitionSummaries(evaluation) {
|
|
|
1250
1208
|
definition: evaluation.definition
|
|
1251
1209
|
};
|
|
1252
1210
|
return evaluation.currentStage.transitions.filter(t => !t.whenSatisfied).flatMap(t => {
|
|
1253
|
-
const
|
|
1254
|
-
return
|
|
1211
|
+
const summary2 = describeCondition(t.insight, ctx).summary;
|
|
1212
|
+
return summary2 !== void 0 ? [ {
|
|
1255
1213
|
transition: t.transition.name,
|
|
1256
|
-
summary:
|
|
1214
|
+
summary: summary2
|
|
1257
1215
|
} ] : [];
|
|
1258
1216
|
});
|
|
1259
1217
|
}
|
|
@@ -1746,27 +1704,38 @@ function startContextSyntaxIssues({groq: groq, vars: vars, context: context, ski
|
|
|
1746
1704
|
return [ ...conditionParameterNames(groq) ].filter(name => !bound.has(name) && name !== skip).map(name => `reads $${name}, which the ${context} context does not bind (an unbound variable is GROQ null, so the predicate silently misevaluates — failing closed or passing vacuously by shape). Bound here: ` + vars.map(v2 => `$${v2.name}`).join(", "));
|
|
1747
1705
|
}
|
|
1748
1706
|
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1707
|
+
const GUARD_DOC_TYPE = "temp.system.guard";
|
|
1708
|
+
|
|
1709
|
+
class MutationGuardDeniedError extends WorkflowError {
|
|
1710
|
+
denied;
|
|
1711
|
+
documentId;
|
|
1712
|
+
action;
|
|
1713
|
+
constructor(args) {
|
|
1714
|
+
const ids = args.denied.map(d => d.guardId).join(", ");
|
|
1715
|
+
super("mutation-guard-denied", `Mutation on "${args.documentId}" (${args.action}) denied by guard(s) [${ids}]`),
|
|
1716
|
+
this.name = "MutationGuardDeniedError", this.denied = args.denied, this.documentId = args.documentId,
|
|
1717
|
+
this.action = args.action;
|
|
1718
|
+
}
|
|
1719
|
+
static fromGuards(args) {
|
|
1720
|
+
return new MutationGuardDeniedError({
|
|
1721
|
+
documentId: args.documentId,
|
|
1722
|
+
action: args.action,
|
|
1723
|
+
denied: deniedGuardRefs(args.guards)
|
|
1724
|
+
});
|
|
1754
1725
|
}
|
|
1755
|
-
return current;
|
|
1756
1726
|
}
|
|
1757
1727
|
|
|
1758
|
-
function
|
|
1759
|
-
return
|
|
1728
|
+
function deniedGuardRefs(guards) {
|
|
1729
|
+
return guards.map(g => ({
|
|
1730
|
+
guardId: g._id,
|
|
1731
|
+
...g.name !== void 0 ? {
|
|
1732
|
+
name: g.name
|
|
1733
|
+
} : {}
|
|
1734
|
+
}));
|
|
1760
1735
|
}
|
|
1761
1736
|
|
|
1762
|
-
function
|
|
1763
|
-
|
|
1764
|
-
for (const part of path.split(".")) {
|
|
1765
|
-
if (current == null || typeof current != "object") return;
|
|
1766
|
-
if (isReferenceObject(current) && !Object.hasOwn(current, part)) throw new Error(`cannot read "${part}" through the nested reference "${current._ref}" — the workflow only loads references it declares. Declare it as a doc.ref field entry to read through it.`);
|
|
1767
|
-
current = current[part];
|
|
1768
|
-
}
|
|
1769
|
-
return current;
|
|
1737
|
+
function deniedGuardLabels(denied) {
|
|
1738
|
+
return denied.map(d => d.name ?? d.guardId);
|
|
1770
1739
|
}
|
|
1771
1740
|
|
|
1772
1741
|
function mapJsonStrings(value, transform) {
|
|
@@ -1835,179 +1804,638 @@ function overlayInstanceInSnapshot(snapshot, instance) {
|
|
|
1835
1804
|
};
|
|
1836
1805
|
}
|
|
1837
1806
|
|
|
1838
|
-
function
|
|
1839
|
-
|
|
1840
|
-
if (isSingleDocRefKind(kind)) {
|
|
1841
|
-
if (!isGdr(value)) return null;
|
|
1842
|
-
if (path === void 0 && targetKind !== void 0 && isSingleDocRefKind(targetKind)) return value;
|
|
1843
|
-
const base = derefBase(value, snapshot);
|
|
1844
|
-
return path === void 0 ? base : walkDocPath(base, path);
|
|
1845
|
-
}
|
|
1846
|
-
return path !== void 0 ? getPath(value, path) : value;
|
|
1807
|
+
function lakeGuardId(args) {
|
|
1808
|
+
return `${GUARD_DOC_TYPE}.${args.instanceDocId}.${args.guardName}`;
|
|
1847
1809
|
}
|
|
1848
1810
|
|
|
1849
|
-
function
|
|
1850
|
-
return
|
|
1851
|
-
_id: ref.id,
|
|
1852
|
-
_type: ref.type
|
|
1853
|
-
};
|
|
1811
|
+
function lakeActionsFor(action) {
|
|
1812
|
+
return action === "publish" ? [ "create", "update" ] : action === "unpublish" ? [ "delete" ] : [ action ];
|
|
1854
1813
|
}
|
|
1855
1814
|
|
|
1856
|
-
function
|
|
1857
|
-
const
|
|
1858
|
-
return
|
|
1859
|
-
self: selfGdr(instance),
|
|
1860
|
-
fields: renderedFields(instance.fields ?? [], snapshot),
|
|
1861
|
-
parent: parentRef(instance)?.id ?? null,
|
|
1862
|
-
ancestors: instance.ancestors.map(a => a.id),
|
|
1863
|
-
stage: instance.currentStage,
|
|
1864
|
-
now: now,
|
|
1865
|
-
context: contextMap(instance),
|
|
1866
|
-
effects: effectOutputsMap(instance),
|
|
1867
|
-
effectStatus: effectStatusMap(instance),
|
|
1868
|
-
activities: currentActivities2,
|
|
1869
|
-
subworkflows: subworkflowVarRows(instance, snapshot),
|
|
1870
|
-
...activityGateParams(currentActivities2),
|
|
1871
|
-
...extra
|
|
1872
|
-
};
|
|
1815
|
+
function lakeGuardActions(actions) {
|
|
1816
|
+
const requested = new Set(actions.flatMap(lakeActionsFor));
|
|
1817
|
+
return LAKE_MUTATION_GUARD_ACTIONS.filter(action => requested.has(action));
|
|
1873
1818
|
}
|
|
1874
1819
|
|
|
1875
|
-
function
|
|
1876
|
-
|
|
1877
|
-
return (instance.subworkflows ?? []).map(row => ({
|
|
1878
|
-
_id: toBareId(row.ref.id),
|
|
1879
|
-
rowKey: row.rowKey,
|
|
1880
|
-
activity: row.activity,
|
|
1881
|
-
action: row.action,
|
|
1882
|
-
definition: row.definition,
|
|
1883
|
-
current: row.stageEntry === openKey,
|
|
1884
|
-
...childState(row, snapshot),
|
|
1885
|
-
spawnedAt: row.spawnedAt
|
|
1886
|
-
}));
|
|
1820
|
+
function publishedId(id) {
|
|
1821
|
+
return getPublishedId(id);
|
|
1887
1822
|
}
|
|
1888
1823
|
|
|
1889
|
-
function
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1824
|
+
function draftId(id) {
|
|
1825
|
+
return getDraftId(id);
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
function versionPatternTranslation(pattern) {
|
|
1829
|
+
const documentSeparator = pattern.indexOf(".", 9);
|
|
1830
|
+
if (documentSeparator === -1) return {
|
|
1831
|
+
issue: `id pattern "${pattern}" does not identify a versioned document`
|
|
1893
1832
|
};
|
|
1894
|
-
const
|
|
1895
|
-
return
|
|
1896
|
-
|
|
1897
|
-
status: "active"
|
|
1833
|
+
const documentPattern = pattern.slice(documentSeparator + 1);
|
|
1834
|
+
return /^\*+$/.test(documentPattern) ? {
|
|
1835
|
+
issue: `id pattern "${pattern}" cannot be translated from a release version to a draft or published document without broadening it to every document`
|
|
1898
1836
|
} : {
|
|
1899
|
-
|
|
1900
|
-
status: resolvedChildStatus(doc) ?? "active"
|
|
1837
|
+
documentPattern: documentPattern
|
|
1901
1838
|
};
|
|
1902
1839
|
}
|
|
1903
1840
|
|
|
1904
|
-
function
|
|
1905
|
-
return
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1841
|
+
function publishedPattern(pattern) {
|
|
1842
|
+
if (pattern.startsWith("drafts.")) return pattern.slice(7);
|
|
1843
|
+
if (!pattern.startsWith("versions.")) return pattern;
|
|
1844
|
+
const translated = versionPatternTranslation(pattern);
|
|
1845
|
+
if (translated.issue !== void 0) throw new Error(translated.issue);
|
|
1846
|
+
return translated.documentPattern;
|
|
1909
1847
|
}
|
|
1910
1848
|
|
|
1911
|
-
function
|
|
1912
|
-
|
|
1913
|
-
for (const entry of entries) out[entry.name] = renderedValue(entry, snapshot);
|
|
1914
|
-
return out;
|
|
1849
|
+
function draftPattern(pattern) {
|
|
1850
|
+
return `drafts.${publishedPattern(pattern)}`;
|
|
1915
1851
|
}
|
|
1916
1852
|
|
|
1917
|
-
function
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
value: entry.value,
|
|
1921
|
-
path: void 0,
|
|
1922
|
-
snapshot: snapshot
|
|
1923
|
-
});
|
|
1853
|
+
function idsInSpace(args) {
|
|
1854
|
+
const {ids: ids, space: space, sourceInstanceId: sourceInstanceId} = args;
|
|
1855
|
+
return space === "authored" ? ids : space === "published" ? [ ...new Set(ids.map(publishedId)) ] : [ ...new Set(ids.map(id => publishedId(id) === sourceInstanceId ? publishedId(id) : draftId(id))) ];
|
|
1924
1856
|
}
|
|
1925
1857
|
|
|
1926
|
-
function
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1858
|
+
function patternsInSpace(patterns, space) {
|
|
1859
|
+
if (space === "authored") return patterns;
|
|
1860
|
+
const convert = space === "edit" ? draftPattern : publishedPattern;
|
|
1861
|
+
return [ ...new Set(patterns.map(convert)) ];
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1864
|
+
function matchInSpace(args) {
|
|
1865
|
+
const {match: match, actions: actions, space: space, sourceInstanceId: sourceInstanceId} = args;
|
|
1930
1866
|
return {
|
|
1931
|
-
...
|
|
1932
|
-
...
|
|
1867
|
+
...match,
|
|
1868
|
+
...match.idRefs !== void 0 ? {
|
|
1869
|
+
idRefs: idsInSpace({
|
|
1870
|
+
ids: match.idRefs,
|
|
1871
|
+
space: space,
|
|
1872
|
+
sourceInstanceId: sourceInstanceId
|
|
1873
|
+
})
|
|
1874
|
+
} : {},
|
|
1875
|
+
...match.idPatterns !== void 0 ? {
|
|
1876
|
+
idPatterns: patternsInSpace(match.idPatterns, space)
|
|
1877
|
+
} : {},
|
|
1878
|
+
actions: lakeGuardActions(actions)
|
|
1933
1879
|
};
|
|
1934
1880
|
}
|
|
1935
1881
|
|
|
1936
|
-
function
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1882
|
+
function guardPartitions(id, actions) {
|
|
1883
|
+
return MUTATION_GUARD_ID_SPACES.flatMap(space => {
|
|
1884
|
+
const matchingActions = actions.filter(action => mutationGuardActionIdSpace(action) === space);
|
|
1885
|
+
return matchingActions.length > 0 ? [ {
|
|
1886
|
+
space: space,
|
|
1887
|
+
actions: matchingActions
|
|
1888
|
+
} ] : [];
|
|
1889
|
+
}).map((partition, index) => ({
|
|
1890
|
+
...partition,
|
|
1891
|
+
id: index === 0 ? id : `${id}.${partition.space}`
|
|
1943
1892
|
}));
|
|
1944
1893
|
}
|
|
1945
1894
|
|
|
1946
|
-
function
|
|
1947
|
-
|
|
1948
|
-
if (entry === void 0) return {};
|
|
1949
|
-
const out = {};
|
|
1950
|
-
for (const run of instance.effectHistory) run.stageEntryKey === entry._key && (out[run.name] = run.status);
|
|
1951
|
-
return out;
|
|
1895
|
+
function compiledGuardIds(args) {
|
|
1896
|
+
return guardPartitions(args.id, args.actions).map(partition => partition.id);
|
|
1952
1897
|
}
|
|
1953
1898
|
|
|
1954
|
-
function
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1899
|
+
function lakeGuardDocument(args) {
|
|
1900
|
+
return {
|
|
1901
|
+
_id: args.id,
|
|
1902
|
+
_type: GUARD_DOC_TYPE,
|
|
1903
|
+
resourceType: args.resourceType,
|
|
1904
|
+
resourceId: args.resourceId,
|
|
1905
|
+
owner: args.owner,
|
|
1906
|
+
sourceInstanceId: args.sourceInstanceId,
|
|
1907
|
+
sourceDefinition: args.sourceDefinition,
|
|
1908
|
+
sourceStage: args.sourceStage,
|
|
1909
|
+
...args.name !== void 0 ? {
|
|
1910
|
+
name: args.name
|
|
1911
|
+
} : {},
|
|
1912
|
+
...args.description !== void 0 ? {
|
|
1913
|
+
description: args.description
|
|
1914
|
+
} : {},
|
|
1915
|
+
match: args.match,
|
|
1916
|
+
predicate: args.predicate,
|
|
1917
|
+
metadata: args.metadata
|
|
1918
|
+
};
|
|
1958
1919
|
}
|
|
1959
1920
|
|
|
1960
|
-
function
|
|
1961
|
-
const
|
|
1962
|
-
|
|
1963
|
-
|
|
1921
|
+
function compileGuards(args) {
|
|
1922
|
+
const [first, ...remaining] = guardPartitions(args.id, args.match.actions);
|
|
1923
|
+
if (first === void 0) throw new Error("cannot compile a guard without an action");
|
|
1924
|
+
const compilePartition = ({space: space, actions: actions, id: id}) => lakeGuardDocument({
|
|
1925
|
+
...args,
|
|
1926
|
+
id: id,
|
|
1927
|
+
match: matchInSpace({
|
|
1928
|
+
match: args.match,
|
|
1929
|
+
actions: actions,
|
|
1930
|
+
space: space,
|
|
1931
|
+
sourceInstanceId: args.sourceInstanceId
|
|
1932
|
+
})
|
|
1933
|
+
});
|
|
1934
|
+
return [ compilePartition(first), ...remaining.map(compilePartition) ];
|
|
1964
1935
|
}
|
|
1965
1936
|
|
|
1966
|
-
function
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1970
|
-
rethrowWithContext(err, `context entry "${entry.name}" holds unparseable JSON`);
|
|
1971
|
-
}
|
|
1937
|
+
function globMatch(pattern, value) {
|
|
1938
|
+
if (!pattern.includes("*")) return pattern === value;
|
|
1939
|
+
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
1940
|
+
return new RegExp(`^${escaped}$`).test(value);
|
|
1972
1941
|
}
|
|
1973
1942
|
|
|
1974
|
-
function
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
parent: typeof params.parent == "string" ? toBareId(params.parent) : params.parent,
|
|
1979
|
-
ancestors: Array.isArray(params.ancestors) ? params.ancestors.map(a => typeof a == "string" ? toBareId(a) : a) : params.ancestors,
|
|
1980
|
-
fields: stripFieldsForLake(params.fields)
|
|
1981
|
-
};
|
|
1943
|
+
function guardIdPatternIssue(pattern, actions) {
|
|
1944
|
+
const representativeId = pattern.replaceAll("*", "x");
|
|
1945
|
+
if (!isBareDocumentId(representativeId)) return `id pattern "${pattern}" is not a valid resource-local document-id glob; use document-id characters (letters, digits, \`_\`, \`-\`, \`.\`) and \`*\` wildcards`;
|
|
1946
|
+
if (actions?.some(action => mutationGuardActionIdSpace(action) !== "authored") && pattern.startsWith("versions.")) return versionPatternTranslation(pattern).issue;
|
|
1982
1947
|
}
|
|
1983
1948
|
|
|
1984
|
-
function
|
|
1985
|
-
return
|
|
1949
|
+
function parseGuardPredicate(predicate) {
|
|
1950
|
+
return parse(predicate, {
|
|
1951
|
+
mode: "delta"
|
|
1952
|
+
});
|
|
1986
1953
|
}
|
|
1987
1954
|
|
|
1988
|
-
function
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
return
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
return ctx.params?.[src.param];
|
|
1995
|
-
|
|
1996
|
-
case "actor":
|
|
1997
|
-
return ctx.actor;
|
|
1998
|
-
|
|
1999
|
-
case "now":
|
|
2000
|
-
return ctx.now;
|
|
1955
|
+
function guardPredicateSyntaxIssue(predicate) {
|
|
1956
|
+
try {
|
|
1957
|
+
parseGuardPredicate(predicate);
|
|
1958
|
+
return;
|
|
1959
|
+
} catch (error) {
|
|
1960
|
+
return errorMessage(error);
|
|
2001
1961
|
}
|
|
2002
1962
|
}
|
|
2003
1963
|
|
|
2004
|
-
function
|
|
2005
|
-
|
|
2006
|
-
return globalThis.crypto.getRandomValues(bytes), [ ...bytes ].map(b => b.toString(16).padStart(2, "0")).join("").slice(0, length);
|
|
1964
|
+
function lakeDocumentIdForAction(id, action) {
|
|
1965
|
+
return mutationGuardActionIdSpace(action) === "published" ? publishedId(id) : id;
|
|
2007
1966
|
}
|
|
2008
1967
|
|
|
2009
|
-
function
|
|
2010
|
-
|
|
1968
|
+
function legacyLifecycleActionFor(action, documentId) {
|
|
1969
|
+
if (publishedId(documentId) === documentId) {
|
|
1970
|
+
if (action === "create" || action === "update") return "publish";
|
|
1971
|
+
if (action === "delete") return "unpublish";
|
|
1972
|
+
}
|
|
1973
|
+
}
|
|
1974
|
+
|
|
1975
|
+
function guardMatches({guard: guard, doc: doc, action: action}) {
|
|
1976
|
+
const m = guard.match, documentId = lakeDocumentIdForAction(doc.id, action), legacyLifecycleAction = legacyLifecycleActionFor(action, documentId);
|
|
1977
|
+
if (![ action, ...lakeActionsFor(action), legacyLifecycleAction ].some(candidate => candidate !== void 0 && m.actions.includes(candidate)) || m.types && m.types.length > 0 && !(doc.type !== void 0 && m.types.includes(doc.type))) return !1;
|
|
1978
|
+
if (m.idRefs && m.idRefs.length > 0 || m.idPatterns && m.idPatterns.length > 0) {
|
|
1979
|
+
const byRef = m.idRefs?.includes(documentId) ?? !1, byPattern = m.idPatterns?.some(p => globMatch(p, documentId)) ?? !1;
|
|
1980
|
+
if (!byRef && !byPattern) return !1;
|
|
1981
|
+
}
|
|
1982
|
+
return !0;
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1985
|
+
function guardPredicateRoot(args) {
|
|
1986
|
+
return {
|
|
1987
|
+
document: {
|
|
1988
|
+
before: args.context.before,
|
|
1989
|
+
after: args.context.after
|
|
1990
|
+
},
|
|
1991
|
+
guard: args.guard,
|
|
1992
|
+
mutation: {
|
|
1993
|
+
action: args.context.action
|
|
1994
|
+
}
|
|
1995
|
+
};
|
|
1996
|
+
}
|
|
1997
|
+
|
|
1998
|
+
function lakeShapedSnapshotDocument(doc) {
|
|
1999
|
+
return mapJsonStrings(doc, (value, key) => key === "_id" || key === "_ref" ? toBareId(value) : value);
|
|
2000
|
+
}
|
|
2001
|
+
|
|
2002
|
+
function snapshotGuardDereference(args) {
|
|
2003
|
+
return ({_ref: _ref}) => {
|
|
2004
|
+
const lookupId = isGdrUri(_ref) ? _ref : gdrFromResource(args.resource, _ref), found = findSnapshotDoc(args.snapshot, lookupId);
|
|
2005
|
+
return Promise.resolve(found === void 0 ? null : lakeShapedSnapshotDocument(found));
|
|
2006
|
+
};
|
|
2007
|
+
}
|
|
2008
|
+
|
|
2009
|
+
async function evaluateMutationGuard(args) {
|
|
2010
|
+
const {guard: guard, context: context, dereference: dereference} = args;
|
|
2011
|
+
if (guard.predicate === "") return !1;
|
|
2012
|
+
const root = guardPredicateRoot({
|
|
2013
|
+
guard: guard,
|
|
2014
|
+
context: context
|
|
2015
|
+
});
|
|
2016
|
+
let dereferenceFailed = !1;
|
|
2017
|
+
const guardedDereference = dereference === void 0 ? void 0 : async ref => {
|
|
2018
|
+
try {
|
|
2019
|
+
return await dereference(ref);
|
|
2020
|
+
} catch (error) {
|
|
2021
|
+
throw dereferenceFailed = !0, error;
|
|
2022
|
+
}
|
|
2023
|
+
};
|
|
2024
|
+
try {
|
|
2025
|
+
const tree = parseGuardPredicate(guard.predicate);
|
|
2026
|
+
return await (await evaluate(tree, {
|
|
2027
|
+
root: root,
|
|
2028
|
+
before: context.before,
|
|
2029
|
+
after: context.after,
|
|
2030
|
+
...guardedDereference !== void 0 ? {
|
|
2031
|
+
dataset: [],
|
|
2032
|
+
dereference: guardedDereference
|
|
2033
|
+
} : {},
|
|
2034
|
+
...context.identity !== void 0 ? {
|
|
2035
|
+
identity: context.identity
|
|
2036
|
+
} : {}
|
|
2037
|
+
})).get() === !0;
|
|
2038
|
+
} catch (error) {
|
|
2039
|
+
if (dereferenceFailed) throw error;
|
|
2040
|
+
return !1;
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
|
|
2044
|
+
async function denyingGuards(args) {
|
|
2045
|
+
const {guards: guards, doc: doc, context: context, dereference: dereference} = args, denied = [];
|
|
2046
|
+
for (const guard of guards) guardMatches({
|
|
2047
|
+
guard: guard,
|
|
2048
|
+
doc: doc,
|
|
2049
|
+
action: context.action
|
|
2050
|
+
}) && (await evaluateMutationGuard({
|
|
2051
|
+
guard: guard,
|
|
2052
|
+
context: context,
|
|
2053
|
+
...dereference !== void 0 ? {
|
|
2054
|
+
dereference: dereference
|
|
2055
|
+
} : {}
|
|
2056
|
+
}) || denied.push(guard));
|
|
2057
|
+
return denied;
|
|
2058
|
+
}
|
|
2059
|
+
|
|
2060
|
+
async function documentActionDenials(args) {
|
|
2061
|
+
const {mutation: mutation, resource: resource, guards: guards, identity: identity, dereference: dereference} = args, document = mutation.after ?? mutation.before;
|
|
2062
|
+
if (document === null) throw new Error("a concrete Lake mutation requires a before or after image");
|
|
2063
|
+
const enforceable = guards.filter(g => g.resourceType === resource.type && g.resourceId === resource.id);
|
|
2064
|
+
return enforceable.length === 0 ? [] : denyingGuards({
|
|
2065
|
+
guards: enforceable,
|
|
2066
|
+
doc: {
|
|
2067
|
+
id: document._id,
|
|
2068
|
+
type: document._type
|
|
2069
|
+
},
|
|
2070
|
+
context: {
|
|
2071
|
+
...mutation,
|
|
2072
|
+
...identity !== void 0 ? {
|
|
2073
|
+
identity: identity
|
|
2074
|
+
} : {}
|
|
2075
|
+
},
|
|
2076
|
+
...dereference !== void 0 ? {
|
|
2077
|
+
dereference: dereference
|
|
2078
|
+
} : {}
|
|
2079
|
+
});
|
|
2080
|
+
}
|
|
2081
|
+
|
|
2082
|
+
async function instanceWriteDenials(args) {
|
|
2083
|
+
const {instance: instance, guards: guards, identity: identity, dereference: dereference} = args;
|
|
2084
|
+
return documentActionDenials({
|
|
2085
|
+
mutation: {
|
|
2086
|
+
before: instance,
|
|
2087
|
+
after: instance,
|
|
2088
|
+
action: "update"
|
|
2089
|
+
},
|
|
2090
|
+
resource: instance.workflowResource,
|
|
2091
|
+
guards: guards,
|
|
2092
|
+
dereference: dereference,
|
|
2093
|
+
...identity !== void 0 ? {
|
|
2094
|
+
identity: identity
|
|
2095
|
+
} : {}
|
|
2096
|
+
});
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2099
|
+
async function assertInstanceWriteAllowed(args) {
|
|
2100
|
+
const denied = await instanceWriteDenials(args);
|
|
2101
|
+
if (denied.length !== 0) throw MutationGuardDeniedError.fromGuards({
|
|
2102
|
+
documentId: args.instance._id,
|
|
2103
|
+
action: "update",
|
|
2104
|
+
guards: denied
|
|
2105
|
+
});
|
|
2106
|
+
}
|
|
2107
|
+
|
|
2108
|
+
const ASSIGNMENT_LIST_MODEL = 9;
|
|
2109
|
+
|
|
2110
|
+
function isAssignmentListModel(modelVersion) {
|
|
2111
|
+
return modelVersion >= ASSIGNMENT_LIST_MODEL;
|
|
2112
|
+
}
|
|
2113
|
+
|
|
2114
|
+
function definitionAssignmentModel(instance) {
|
|
2115
|
+
if (typeof instance.definitionSnapshot != "string") return 0;
|
|
2116
|
+
const modelVersion = parseDefinitionSnapshotValue({
|
|
2117
|
+
_id: instance._id,
|
|
2118
|
+
definitionSnapshot: instance.definitionSnapshot
|
|
2119
|
+
}).modelVersion;
|
|
2120
|
+
return typeof modelVersion == "number" ? modelVersion : 0;
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
function writesSingularAssignmentLists(instance) {
|
|
2124
|
+
return isAssignmentListModel(definitionAssignmentModel(instance));
|
|
2125
|
+
}
|
|
2126
|
+
|
|
2127
|
+
function legacyAssigneeValue(entry) {
|
|
2128
|
+
return legacyAssignmentValue(entry.value, entry.name);
|
|
2129
|
+
}
|
|
2130
|
+
|
|
2131
|
+
function legacyAssignmentValue(value, path) {
|
|
2132
|
+
if (!Array.isArray(value)) return value;
|
|
2133
|
+
if (value.length === 0) return null;
|
|
2134
|
+
if (value.length === 1) return value[0];
|
|
2135
|
+
throw new ContractViolationError(`legacy assignee field "${path}" cannot persist ${value.length} members; redeploy the definition at the assignment-list reader model before writing a role pool`);
|
|
2136
|
+
}
|
|
2137
|
+
|
|
2138
|
+
function mapShapeValues(args) {
|
|
2139
|
+
const {value: value, shapes: shapes, mapAssignee: mapAssignee, parentPath: parentPath} = args;
|
|
2140
|
+
if (typeof value != "object" || value === null || Array.isArray(value)) return value;
|
|
2141
|
+
const source = value, next = {
|
|
2142
|
+
...source
|
|
2143
|
+
};
|
|
2144
|
+
for (const shape of shapes) {
|
|
2145
|
+
if (!Object.hasOwn(source, shape.name)) continue;
|
|
2146
|
+
const path = `${parentPath}.${shape.name}`, member = source[shape.name], mapped = mappedShapeValue({
|
|
2147
|
+
shape: shape,
|
|
2148
|
+
member: member,
|
|
2149
|
+
mapAssignee: mapAssignee,
|
|
2150
|
+
path: path
|
|
2151
|
+
});
|
|
2152
|
+
mapped.found && defineOwnValue(next, {
|
|
2153
|
+
name: shape.name,
|
|
2154
|
+
value: mapped.value
|
|
2155
|
+
});
|
|
2156
|
+
}
|
|
2157
|
+
return next;
|
|
2158
|
+
}
|
|
2159
|
+
|
|
2160
|
+
function mappedShapeValue(args) {
|
|
2161
|
+
const {shape: shape, member: member, mapAssignee: mapAssignee, path: path} = args;
|
|
2162
|
+
return shape.type === "assignee" ? {
|
|
2163
|
+
found: !0,
|
|
2164
|
+
value: mapAssignee(member, path)
|
|
2165
|
+
} : shape.type === "object" ? {
|
|
2166
|
+
found: !0,
|
|
2167
|
+
value: mapShapeValues({
|
|
2168
|
+
value: member,
|
|
2169
|
+
shapes: shape.fields ?? [],
|
|
2170
|
+
mapAssignee: mapAssignee,
|
|
2171
|
+
parentPath: path
|
|
2172
|
+
})
|
|
2173
|
+
} : shape.type !== "array" || !Array.isArray(member) ? {
|
|
2174
|
+
found: !1
|
|
2175
|
+
} : {
|
|
2176
|
+
found: !0,
|
|
2177
|
+
value: member.map((row, index) => mapShapeValues({
|
|
2178
|
+
value: row,
|
|
2179
|
+
shapes: shape.of ?? [],
|
|
2180
|
+
mapAssignee: mapAssignee,
|
|
2181
|
+
parentPath: `${path}[${index}]`
|
|
2182
|
+
}))
|
|
2183
|
+
};
|
|
2184
|
+
}
|
|
2185
|
+
|
|
2186
|
+
function defineOwnValue(target, property) {
|
|
2187
|
+
Object.defineProperty(target, property.name, {
|
|
2188
|
+
configurable: !0,
|
|
2189
|
+
enumerable: !0,
|
|
2190
|
+
value: property.value,
|
|
2191
|
+
writable: !0
|
|
2192
|
+
});
|
|
2193
|
+
}
|
|
2194
|
+
|
|
2195
|
+
function mapNestedAssignmentValues(entry, mapAssignee) {
|
|
2196
|
+
return entry._type === "object" ? mapShapeValues({
|
|
2197
|
+
value: entry.value,
|
|
2198
|
+
shapes: entry.fields,
|
|
2199
|
+
mapAssignee: mapAssignee,
|
|
2200
|
+
parentPath: entry.name
|
|
2201
|
+
}) : entry.value.map((row, index) => mapShapeValues({
|
|
2202
|
+
value: row,
|
|
2203
|
+
shapes: entry.of,
|
|
2204
|
+
mapAssignee: mapAssignee,
|
|
2205
|
+
parentPath: `${entry.name}[${index}]`
|
|
2206
|
+
}));
|
|
2207
|
+
}
|
|
2208
|
+
|
|
2209
|
+
function normalizeNestedAssignmentValues(entry) {
|
|
2210
|
+
return entry._type !== "object" && entry._type !== "array" ? entry : {
|
|
2211
|
+
...entry,
|
|
2212
|
+
value: mapNestedAssignmentValues(entry, normalizeAssignmentMembers)
|
|
2213
|
+
};
|
|
2214
|
+
}
|
|
2215
|
+
|
|
2216
|
+
function assignmentFieldsForWrite(fields, listModel) {
|
|
2217
|
+
return listModel ? fields : fields.map(entry => entry._type === "assignee" ? {
|
|
2218
|
+
...entry,
|
|
2219
|
+
value: legacyAssigneeValue(entry)
|
|
2220
|
+
} : entry._type === "object" || entry._type === "array" ? {
|
|
2221
|
+
...entry,
|
|
2222
|
+
value: mapNestedAssignmentValues(entry, legacyAssignmentValue)
|
|
2223
|
+
} : entry);
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2226
|
+
function activityForWrite(activity, listModel) {
|
|
2227
|
+
return {
|
|
2228
|
+
...activity,
|
|
2229
|
+
...activity.fields === void 0 ? {} : {
|
|
2230
|
+
fields: assignmentFieldsForWrite(activity.fields, listModel)
|
|
2231
|
+
}
|
|
2232
|
+
};
|
|
2233
|
+
}
|
|
2234
|
+
|
|
2235
|
+
function assignmentStagesForWrite(stages, listModel) {
|
|
2236
|
+
return stages.map(stage => ({
|
|
2237
|
+
...stage,
|
|
2238
|
+
fields: assignmentFieldsForWrite(stage.fields ?? [], listModel),
|
|
2239
|
+
activities: stage.activities.map(activity => activityForWrite(activity, listModel))
|
|
2240
|
+
}));
|
|
2241
|
+
}
|
|
2242
|
+
|
|
2243
|
+
function getPath(value, path) {
|
|
2244
|
+
let current = value;
|
|
2245
|
+
for (const part of path.split(".")) {
|
|
2246
|
+
if (current == null || typeof current != "object") return;
|
|
2247
|
+
current = current[part];
|
|
2248
|
+
}
|
|
2249
|
+
return current;
|
|
2250
|
+
}
|
|
2251
|
+
|
|
2252
|
+
function isReferenceObject(value) {
|
|
2253
|
+
return typeof value._ref == "string";
|
|
2254
|
+
}
|
|
2255
|
+
|
|
2256
|
+
function walkDocPath(value, path) {
|
|
2257
|
+
let current = value;
|
|
2258
|
+
for (const part of path.split(".")) {
|
|
2259
|
+
if (current == null || typeof current != "object") return;
|
|
2260
|
+
if (isReferenceObject(current) && !Object.hasOwn(current, part)) throw new Error(`cannot read "${part}" through the nested reference "${current._ref}" — the workflow only loads references it declares. Declare it as a doc.ref field entry to read through it.`);
|
|
2261
|
+
current = current[part];
|
|
2262
|
+
}
|
|
2263
|
+
return current;
|
|
2264
|
+
}
|
|
2265
|
+
|
|
2266
|
+
function resolveFieldRead(args) {
|
|
2267
|
+
const {kind: kind, value: value, path: path, snapshot: snapshot, targetKind: targetKind} = args;
|
|
2268
|
+
if (isSingleDocRefKind(kind)) {
|
|
2269
|
+
if (!isGdr(value)) return null;
|
|
2270
|
+
if (path === void 0 && targetKind !== void 0 && isSingleDocRefKind(targetKind)) return value;
|
|
2271
|
+
const base = derefBase(value, snapshot);
|
|
2272
|
+
return path === void 0 ? base : walkDocPath(base, path);
|
|
2273
|
+
}
|
|
2274
|
+
return path !== void 0 ? getPath(value, path) : value;
|
|
2275
|
+
}
|
|
2276
|
+
|
|
2277
|
+
function derefBase(ref, snapshot) {
|
|
2278
|
+
return (snapshot !== void 0 ? findSnapshotDoc(snapshot, ref.id) : void 0) ?? {
|
|
2279
|
+
_id: ref.id,
|
|
2280
|
+
_type: ref.type
|
|
2281
|
+
};
|
|
2282
|
+
}
|
|
2283
|
+
|
|
2284
|
+
function buildParams(args) {
|
|
2285
|
+
const {instance: instance, now: now, snapshot: snapshot, extra: extra} = args, currentActivities2 = findOpenStageEntry(instance)?.activities ?? [];
|
|
2286
|
+
return {
|
|
2287
|
+
self: selfGdr(instance),
|
|
2288
|
+
fields: renderedFields(instance.fields ?? [], snapshot),
|
|
2289
|
+
parent: parentRef(instance)?.id ?? null,
|
|
2290
|
+
ancestors: instance.ancestors.map(a => a.id),
|
|
2291
|
+
stage: instance.currentStage,
|
|
2292
|
+
now: now,
|
|
2293
|
+
context: contextMap(instance),
|
|
2294
|
+
effects: effectOutputsMap(instance),
|
|
2295
|
+
effectStatus: effectStatusMap(instance),
|
|
2296
|
+
activities: currentActivities2,
|
|
2297
|
+
subworkflows: subworkflowVarRows(instance, snapshot),
|
|
2298
|
+
...activityGateParams(currentActivities2),
|
|
2299
|
+
...extra
|
|
2300
|
+
};
|
|
2301
|
+
}
|
|
2302
|
+
|
|
2303
|
+
function subworkflowVarRows(instance, snapshot) {
|
|
2304
|
+
const openKey = findOpenStageEntry(instance)?._key;
|
|
2305
|
+
return (instance.subworkflows ?? []).map(row => ({
|
|
2306
|
+
_id: toBareId(row.ref.id),
|
|
2307
|
+
rowKey: row.rowKey,
|
|
2308
|
+
activity: row.activity,
|
|
2309
|
+
action: row.action,
|
|
2310
|
+
definition: row.definition,
|
|
2311
|
+
current: row.stageEntry === openKey,
|
|
2312
|
+
...childState(row, snapshot),
|
|
2313
|
+
spawnedAt: row.spawnedAt
|
|
2314
|
+
}));
|
|
2315
|
+
}
|
|
2316
|
+
|
|
2317
|
+
function childState(row, snapshot) {
|
|
2318
|
+
if (row.resolved !== void 0) return {
|
|
2319
|
+
stage: row.resolved.stage ?? null,
|
|
2320
|
+
status: row.resolved.aborted === !0 ? "aborted" : "done"
|
|
2321
|
+
};
|
|
2322
|
+
const doc = snapshot === void 0 ? void 0 : findSnapshotDoc(snapshot, row.ref.id);
|
|
2323
|
+
return doc === void 0 ? {
|
|
2324
|
+
stage: null,
|
|
2325
|
+
status: "active"
|
|
2326
|
+
} : {
|
|
2327
|
+
stage: doc.currentStage ?? null,
|
|
2328
|
+
status: resolvedChildStatus(doc) ?? "active"
|
|
2329
|
+
};
|
|
2330
|
+
}
|
|
2331
|
+
|
|
2332
|
+
function activityGateParams(activities) {
|
|
2333
|
+
return {
|
|
2334
|
+
allActivitiesDone: activities.every(activity => activity.status === "done" || activity.status === "skipped"),
|
|
2335
|
+
anyActivityFailed: activities.some(activity => activity.status === "failed")
|
|
2336
|
+
};
|
|
2337
|
+
}
|
|
2338
|
+
|
|
2339
|
+
function renderedFields(entries, snapshot) {
|
|
2340
|
+
const out = {};
|
|
2341
|
+
for (const entry of entries) out[entry.name] = renderedValue(entry, snapshot);
|
|
2342
|
+
return out;
|
|
2343
|
+
}
|
|
2344
|
+
|
|
2345
|
+
function renderedValue(entry, snapshot) {
|
|
2346
|
+
return resolveFieldRead({
|
|
2347
|
+
kind: entry._type,
|
|
2348
|
+
value: entry.value,
|
|
2349
|
+
path: void 0,
|
|
2350
|
+
snapshot: snapshot
|
|
2351
|
+
});
|
|
2352
|
+
}
|
|
2353
|
+
|
|
2354
|
+
function scopedFieldOverlay({instance: instance, snapshot: snapshot, activityName: activityName}) {
|
|
2355
|
+
const stageEntry = findOpenStageEntry(instance);
|
|
2356
|
+
if (stageEntry === void 0) return {};
|
|
2357
|
+
const stageFields = renderedFields(stageEntry.fields ?? [], snapshot), activity = activityName ? stageEntry.activities.find(t => t.name === activityName) : void 0;
|
|
2358
|
+
return {
|
|
2359
|
+
...stageFields,
|
|
2360
|
+
...renderedFields(activity?.fields ?? [], snapshot)
|
|
2361
|
+
};
|
|
2362
|
+
}
|
|
2363
|
+
|
|
2364
|
+
function assignedFor({instance: instance, activityName: activityName, actor: actor, roleAliases: roleAliases}) {
|
|
2365
|
+
if (actor === void 0) return !1;
|
|
2366
|
+
const activity = findCurrentActivityEntry(instance, activityName);
|
|
2367
|
+
return actorMatchesAssignment({
|
|
2368
|
+
actor: actor,
|
|
2369
|
+
members: assignmentMembers(activity?.fields ?? []),
|
|
2370
|
+
roleAliases: roleAliases
|
|
2371
|
+
});
|
|
2372
|
+
}
|
|
2373
|
+
|
|
2374
|
+
function effectStatusMap(instance) {
|
|
2375
|
+
const entry = findOpenStageEntry(instance);
|
|
2376
|
+
if (entry === void 0) return {};
|
|
2377
|
+
const out = {};
|
|
2378
|
+
for (const run of instance.effectHistory) run.stageEntryKey === entry._key && (out[run.name] = run.status);
|
|
2379
|
+
return out;
|
|
2380
|
+
}
|
|
2381
|
+
|
|
2382
|
+
function contextMap(instance) {
|
|
2383
|
+
const out = {};
|
|
2384
|
+
for (const entry of instance.context) out[entry.name] = entry._type === "context.json" ? parseJsonContextEntry(entry) : entry.value;
|
|
2385
|
+
return out;
|
|
2386
|
+
}
|
|
2387
|
+
|
|
2388
|
+
function effectOutputsMap(instance) {
|
|
2389
|
+
const out = {};
|
|
2390
|
+
for (const run of instance.effectHistory) run.outputs !== void 0 && (out[run.name] = run.outputs);
|
|
2391
|
+
return out;
|
|
2392
|
+
}
|
|
2393
|
+
|
|
2394
|
+
function parseJsonContextEntry(entry) {
|
|
2395
|
+
try {
|
|
2396
|
+
return JSON.parse(entry.value);
|
|
2397
|
+
} catch (err) {
|
|
2398
|
+
rethrowWithContext(err, `context entry "${entry.name}" holds unparseable JSON`);
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
|
|
2402
|
+
function paramsForLake(params) {
|
|
2403
|
+
return {
|
|
2404
|
+
...params,
|
|
2405
|
+
self: typeof params.self == "string" ? toBareId(params.self) : params.self,
|
|
2406
|
+
parent: typeof params.parent == "string" ? toBareId(params.parent) : params.parent,
|
|
2407
|
+
ancestors: Array.isArray(params.ancestors) ? params.ancestors.map(a => typeof a == "string" ? toBareId(a) : a) : params.ancestors,
|
|
2408
|
+
fields: stripFieldsForLake(params.fields)
|
|
2409
|
+
};
|
|
2410
|
+
}
|
|
2411
|
+
|
|
2412
|
+
function stripFieldsForLake(value) {
|
|
2413
|
+
return mapJsonStrings(value, s => toBareId(s));
|
|
2414
|
+
}
|
|
2415
|
+
|
|
2416
|
+
function resolveStaticValueExpr(src, ctx) {
|
|
2417
|
+
switch (src.type) {
|
|
2418
|
+
case "literal":
|
|
2419
|
+
return src.value;
|
|
2420
|
+
|
|
2421
|
+
case "param":
|
|
2422
|
+
return ctx.params?.[src.param];
|
|
2423
|
+
|
|
2424
|
+
case "actor":
|
|
2425
|
+
return ctx.actor;
|
|
2426
|
+
|
|
2427
|
+
case "now":
|
|
2428
|
+
return ctx.now;
|
|
2429
|
+
}
|
|
2430
|
+
}
|
|
2431
|
+
|
|
2432
|
+
function randomKey(length = 12) {
|
|
2433
|
+
const bytes = new Uint8Array(length);
|
|
2434
|
+
return globalThis.crypto.getRandomValues(bytes), [ ...bytes ].map(b => b.toString(16).padStart(2, "0")).join("").slice(0, length);
|
|
2435
|
+
}
|
|
2436
|
+
|
|
2437
|
+
function instanceDocId(tag) {
|
|
2438
|
+
return `${tag}.wf-instance.${randomKey()}`;
|
|
2011
2439
|
}
|
|
2012
2440
|
|
|
2013
2441
|
function mapFieldRefValues(args) {
|
|
@@ -2254,10 +2682,12 @@ class WorkflowStateDivergedError extends WorkflowError {
|
|
|
2254
2682
|
class PartialGuardDeployError extends WorkflowError {
|
|
2255
2683
|
stageName;
|
|
2256
2684
|
deployed;
|
|
2685
|
+
rollbackError;
|
|
2257
2686
|
constructor(args) {
|
|
2258
2687
|
super("partial-guard-deploy", `Partial guard deploy on stage "${args.stageName}": ${args.deployed} guard(s) deployed before a later one failed.`, {
|
|
2259
2688
|
cause: args.cause
|
|
2260
|
-
}), this.name = "PartialGuardDeployError", this.stageName = args.stageName, this.deployed = args.deployed
|
|
2689
|
+
}), this.name = "PartialGuardDeployError", this.stageName = args.stageName, this.deployed = args.deployed,
|
|
2690
|
+
args.rollbackError !== void 0 && (this.rollbackError = args.rollbackError);
|
|
2261
2691
|
}
|
|
2262
2692
|
}
|
|
2263
2693
|
|
|
@@ -2298,7 +2728,23 @@ function staleClaimDetail(reason) {
|
|
|
2298
2728
|
const CONCURRENT_COMMIT_MAX_ATTEMPTS = 3;
|
|
2299
2729
|
|
|
2300
2730
|
function lostRaceMessage(args) {
|
|
2301
|
-
return `${args.what} (instance ${args.instanceId}) lost the optimistic-locking race ${args.attempts} attempts running — a concurrent writer kept committing first. Re-read and retry, or investigate a write storm on this instance
|
|
2731
|
+
return `${args.what} (instance ${args.instanceId}) lost the optimistic-locking race ${args.attempts} attempts running — a concurrent writer kept committing first. ${args.recovery ?? "Re-read and retry, or investigate a write storm on this instance."}`;
|
|
2732
|
+
}
|
|
2733
|
+
|
|
2734
|
+
function isRetryableInstanceCommitConflict(error) {
|
|
2735
|
+
return !isCreateIdCollision(error) && isRevisionConflict(error);
|
|
2736
|
+
}
|
|
2737
|
+
|
|
2738
|
+
class InstanceCommitRevisionConflict extends Error {
|
|
2739
|
+
constructor(cause) {
|
|
2740
|
+
super("Instance commit lost its revision fence", {
|
|
2741
|
+
cause: cause
|
|
2742
|
+
}), this.name = "InstanceCommitRevisionConflict";
|
|
2743
|
+
}
|
|
2744
|
+
}
|
|
2745
|
+
|
|
2746
|
+
function isInstanceCommitRevisionConflict(error) {
|
|
2747
|
+
return error instanceof InstanceCommitRevisionConflict;
|
|
2302
2748
|
}
|
|
2303
2749
|
|
|
2304
2750
|
class ConcurrentFireActionError extends WorkflowError {
|
|
@@ -2316,6 +2762,19 @@ class ConcurrentFireActionError extends WorkflowError {
|
|
|
2316
2762
|
}
|
|
2317
2763
|
}
|
|
2318
2764
|
|
|
2765
|
+
class ConcurrentCascadeError extends WorkflowError {
|
|
2766
|
+
instanceId;
|
|
2767
|
+
attempts;
|
|
2768
|
+
constructor(args) {
|
|
2769
|
+
super("concurrent-cascade", lostRaceMessage({
|
|
2770
|
+
what: "Automatic cascade",
|
|
2771
|
+
instanceId: args.instanceId,
|
|
2772
|
+
attempts: args.attempts,
|
|
2773
|
+
recovery: "An earlier verb commit may already have landed. Re-read the instance and invoke tick after contention subsides."
|
|
2774
|
+
})), this.name = "ConcurrentCascadeError", this.instanceId = args.instanceId, this.attempts = args.attempts;
|
|
2775
|
+
}
|
|
2776
|
+
}
|
|
2777
|
+
|
|
2319
2778
|
function isRevisionConflict(error) {
|
|
2320
2779
|
if (typeof error != "object" || error === null) return !1;
|
|
2321
2780
|
const {statusCode: statusCode, message: message} = error;
|
|
@@ -2382,8 +2841,8 @@ class CascadeLimitError extends WorkflowError {
|
|
|
2382
2841
|
|
|
2383
2842
|
const FIELD_OP_TYPES = [ "field.set", "field.setIfMissing", "field.unset", "field.append", "field.inc", "field.dec", "field.updateWhere", "field.removeWhere" ];
|
|
2384
2843
|
|
|
2385
|
-
function isFieldOp(
|
|
2386
|
-
return FIELD_OP_TYPES.includes(
|
|
2844
|
+
function isFieldOp(summary2) {
|
|
2845
|
+
return FIELD_OP_TYPES.includes(summary2.opType);
|
|
2387
2846
|
}
|
|
2388
2847
|
|
|
2389
2848
|
function validateActionParams({action: action, activityName: activityName, callerParams: callerParams}) {
|
|
@@ -2462,7 +2921,7 @@ async function runOps(args) {
|
|
|
2462
2921
|
if (ops === void 0 || ops.length === 0) return [];
|
|
2463
2922
|
const opsFromDefinition = origin.effect === void 0 && origin.edit !== !0, summaries = [];
|
|
2464
2923
|
for (const op of ops) {
|
|
2465
|
-
const
|
|
2924
|
+
const summary2 = await applyOp(op, {
|
|
2466
2925
|
mutation: mutation,
|
|
2467
2926
|
stage: stage,
|
|
2468
2927
|
activityName: origin.activity,
|
|
@@ -2476,9 +2935,9 @@ async function runOps(args) {
|
|
|
2476
2935
|
memberRoles: args.memberRoles,
|
|
2477
2936
|
roleAliases: args.roleAliases
|
|
2478
2937
|
});
|
|
2479
|
-
summaries.push(
|
|
2938
|
+
summaries.push(summary2), shouldRecordOpApplied(summary2) && mutation.history.push(opAppliedEntry({
|
|
2480
2939
|
origin: origin,
|
|
2481
|
-
summary:
|
|
2940
|
+
summary: summary2,
|
|
2482
2941
|
stage: stage,
|
|
2483
2942
|
actor: actor,
|
|
2484
2943
|
now: now
|
|
@@ -2487,12 +2946,12 @@ async function runOps(args) {
|
|
|
2487
2946
|
return summaries;
|
|
2488
2947
|
}
|
|
2489
2948
|
|
|
2490
|
-
function shouldRecordOpApplied(
|
|
2491
|
-
return
|
|
2949
|
+
function shouldRecordOpApplied(summary2) {
|
|
2950
|
+
return summary2.opType !== "field.setIfMissing" || summary2.resolved !== void 0;
|
|
2492
2951
|
}
|
|
2493
2952
|
|
|
2494
2953
|
function opAppliedEntry(args) {
|
|
2495
|
-
const {origin: origin, summary:
|
|
2954
|
+
const {origin: origin, summary: summary2, stage: stage, actor: actor, now: now} = args;
|
|
2496
2955
|
return {
|
|
2497
2956
|
_key: randomKey(),
|
|
2498
2957
|
_type: "opApplied",
|
|
@@ -2510,12 +2969,12 @@ function opAppliedEntry(args) {
|
|
|
2510
2969
|
...origin.effect !== void 0 ? {
|
|
2511
2970
|
effect: origin.effect
|
|
2512
2971
|
} : {},
|
|
2513
|
-
opType:
|
|
2514
|
-
...
|
|
2515
|
-
target:
|
|
2972
|
+
opType: summary2.opType,
|
|
2973
|
+
...summary2.target !== void 0 ? {
|
|
2974
|
+
target: summary2.target
|
|
2516
2975
|
} : {},
|
|
2517
|
-
...
|
|
2518
|
-
resolved:
|
|
2976
|
+
...summary2.resolved !== void 0 ? {
|
|
2977
|
+
resolved: summary2.resolved
|
|
2519
2978
|
} : {},
|
|
2520
2979
|
...actor !== void 0 ? {
|
|
2521
2980
|
actor: actor
|
|
@@ -2598,11 +3057,15 @@ function applyFieldSet(op, ctx) {
|
|
|
2598
3057
|
...entryShape(entry),
|
|
2599
3058
|
memberRoles: ctx.memberRoles,
|
|
2600
3059
|
roleAliases: ctx.roleAliases,
|
|
2601
|
-
...entry._type === "assignees" || entry._type === "object" || entry._type === "array" ? {
|
|
3060
|
+
...entry._type === "assignee" || entry._type === "assignees" || entry._type === "object" || entry._type === "array" ? {
|
|
2602
3061
|
previousValue: entry.value
|
|
2603
3062
|
} : {}
|
|
2604
3063
|
});
|
|
2605
|
-
return
|
|
3064
|
+
return assertLegacySingularValue({
|
|
3065
|
+
entry: entry,
|
|
3066
|
+
value: validated,
|
|
3067
|
+
ctx: ctx
|
|
3068
|
+
}), assertRuntimeRefsWithinSurface({
|
|
2606
3069
|
entryType: entry._type,
|
|
2607
3070
|
entryName: entry.name,
|
|
2608
3071
|
value: validated,
|
|
@@ -2619,12 +3082,12 @@ function applyFieldSet(op, ctx) {
|
|
|
2619
3082
|
}
|
|
2620
3083
|
|
|
2621
3084
|
function applyFieldSetIfMissing(op, ctx) {
|
|
2622
|
-
const entry = locateEntry(ctx, op.target);
|
|
2623
|
-
return isAlwaysArrayFieldKind(entry._type) && rejectFieldOpTarget({
|
|
3085
|
+
const entry = locateEntry(ctx, op.target), legacySingular = isLegacySingularAssignment(entry, ctx);
|
|
3086
|
+
return isAlwaysArrayFieldKind(entry._type) && !legacySingular && rejectFieldOpTarget({
|
|
2624
3087
|
entry: entry,
|
|
2625
3088
|
op: op,
|
|
2626
3089
|
issue: "is always array-valued — setIfMissing applies to nullable entries only; an empty array entry already holds []"
|
|
2627
|
-
}), entry.value !== null && entry.value !== void 0 ? {
|
|
3090
|
+
}), entry.value !== null && entry.value !== void 0 && !(legacySingular && Array.isArray(entry.value) && entry.value.length === 0) ? {
|
|
2628
3091
|
opType: op.type,
|
|
2629
3092
|
target: op.target
|
|
2630
3093
|
} : {
|
|
@@ -2744,7 +3207,14 @@ function applyFieldUnset(op, ctx) {
|
|
|
2744
3207
|
}
|
|
2745
3208
|
|
|
2746
3209
|
function applyFieldAppend(op, ctx) {
|
|
2747
|
-
const entry = locateEntry(ctx, op.target)
|
|
3210
|
+
const entry = locateEntry(ctx, op.target);
|
|
3211
|
+
if (isLegacySingularAssignment(entry, ctx)) throw new FieldValueShapeError({
|
|
3212
|
+
entryType: entry._type,
|
|
3213
|
+
entryName: entry.name,
|
|
3214
|
+
mode: "item",
|
|
3215
|
+
issues: [ "legacy singular assignee entries do not support append; redeploy at model 9" ]
|
|
3216
|
+
});
|
|
3217
|
+
const slot = appendItemSlot(entry), item = resolveOpValue({
|
|
2748
3218
|
src: op.value,
|
|
2749
3219
|
ctx: ctx,
|
|
2750
3220
|
...slot !== void 0 ? {
|
|
@@ -2756,7 +3226,7 @@ function applyFieldAppend(op, ctx) {
|
|
|
2756
3226
|
item: item,
|
|
2757
3227
|
types: entry._type === "doc.refs" ? entry.types : void 0,
|
|
2758
3228
|
of: entry._type === "array" ? entry.of : void 0,
|
|
2759
|
-
roles: entry
|
|
3229
|
+
roles: isAssignmentFieldEntry(entry) ? entry.roles : void 0,
|
|
2760
3230
|
memberRoles: ctx.memberRoles,
|
|
2761
3231
|
roleAliases: ctx.roleAliases
|
|
2762
3232
|
});
|
|
@@ -2768,8 +3238,16 @@ function applyFieldAppend(op, ctx) {
|
|
|
2768
3238
|
ctx: ctx,
|
|
2769
3239
|
src: op.value
|
|
2770
3240
|
});
|
|
2771
|
-
const
|
|
2772
|
-
|
|
3241
|
+
const next = [ ...Array.isArray(entry.value) ? entry.value : [], withRowKey(validated) ], value = isAssignmentFieldEntry(entry) ? validateFieldValue({
|
|
3242
|
+
entryType: entry._type,
|
|
3243
|
+
entryName: entry.name,
|
|
3244
|
+
value: next,
|
|
3245
|
+
...assignmentEntryShape(entry),
|
|
3246
|
+
previousValue: entry.value,
|
|
3247
|
+
memberRoles: ctx.memberRoles,
|
|
3248
|
+
roleAliases: ctx.roleAliases
|
|
3249
|
+
}) : next;
|
|
3250
|
+
return setEntryValue(entry, value), {
|
|
2773
3251
|
opType: op.type,
|
|
2774
3252
|
target: op.target,
|
|
2775
3253
|
resolved: {
|
|
@@ -2846,7 +3324,14 @@ async function applyFieldUpdateWhere(op, ctx) {
|
|
|
2846
3324
|
}
|
|
2847
3325
|
|
|
2848
3326
|
async function applyFieldRemoveWhere(op, ctx) {
|
|
2849
|
-
const entry = locateEntry(ctx, op.target)
|
|
3327
|
+
const entry = locateEntry(ctx, op.target);
|
|
3328
|
+
if (isLegacySingularAssignment(entry, ctx)) throw new FieldValueShapeError({
|
|
3329
|
+
entryType: entry._type,
|
|
3330
|
+
entryName: entry.name,
|
|
3331
|
+
mode: "value",
|
|
3332
|
+
issues: [ "legacy singular assignee entries do not support removeWhere; redeploy at model 9" ]
|
|
3333
|
+
});
|
|
3334
|
+
const rows = requireArrayValue(entry, op), matches = await rowMatches({
|
|
2850
3335
|
where: op.where,
|
|
2851
3336
|
rows: rows,
|
|
2852
3337
|
ctx: ctx
|
|
@@ -2857,6 +3342,20 @@ async function applyFieldRemoveWhere(op, ctx) {
|
|
|
2857
3342
|
};
|
|
2858
3343
|
}
|
|
2859
3344
|
|
|
3345
|
+
function assertLegacySingularValue(args) {
|
|
3346
|
+
const {entry: entry, value: value, ctx: ctx} = args;
|
|
3347
|
+
if (!(!isLegacySingularAssignment(entry, ctx) || !Array.isArray(value) || value.length <= 1)) throw new FieldValueShapeError({
|
|
3348
|
+
entryType: entry._type,
|
|
3349
|
+
entryName: entry.name,
|
|
3350
|
+
mode: "value",
|
|
3351
|
+
issues: [ "legacy singular assignee entries accept at most one total member; redeploy at model 9" ]
|
|
3352
|
+
});
|
|
3353
|
+
}
|
|
3354
|
+
|
|
3355
|
+
function isLegacySingularAssignment(entry, ctx) {
|
|
3356
|
+
return entry._type === "assignee" && !isAssignmentListModel(ctx.mutation.definitionModelVersion);
|
|
3357
|
+
}
|
|
3358
|
+
|
|
2860
3359
|
function requireMergeableRows(entry, op) {
|
|
2861
3360
|
if (entry._type !== "array") throw new Error(`${op.type} target ${op.target.scope}:"${op.target.field}" is a ${entry._type} entry — updateWhere merges declared row sub-fields, so it targets \`array\` entries only`);
|
|
2862
3361
|
}
|
|
@@ -3032,6 +3531,14 @@ function whereParams(ctx) {
|
|
|
3032
3531
|
|
|
3033
3532
|
function validateDefinition(definition) {
|
|
3034
3533
|
const v2 = createDefinitionValidator();
|
|
3534
|
+
validateConditionSites(v2, definition), validateStart(v2, definition);
|
|
3535
|
+
for (const entry of definition.fields ?? []) v2.checkEntry(entry, `workflow.fields "${entry.name}"`);
|
|
3536
|
+
for (const stage of definition.stages) validateStage(v2, stage);
|
|
3537
|
+
for (const issue of checkWorkflowInvariants(definition)) v2.errors.push(` · ${formatIssuePath(issue.path)}: ${issue.message}`);
|
|
3538
|
+
if (v2.errors.length > 0) throw new Error(`validateDefinition("${definition.name}"): ${v2.errors.length} deploy-time validation error${v2.errors.length === 1 ? "" : "s"}:\n` + v2.errors.join(`\n`));
|
|
3539
|
+
}
|
|
3540
|
+
|
|
3541
|
+
function validateConditionSites(v2, definition) {
|
|
3035
3542
|
for (const site of conditionSitesOf(definition)) {
|
|
3036
3543
|
const {address: address} = site;
|
|
3037
3544
|
address.kind !== "editable-field" && v2.checkCondition(site.condition, conditionSiteLabel({
|
|
@@ -3039,12 +3546,11 @@ function validateDefinition(definition) {
|
|
|
3039
3546
|
address: address
|
|
3040
3547
|
}));
|
|
3041
3548
|
}
|
|
3549
|
+
}
|
|
3550
|
+
|
|
3551
|
+
function validateStart(v2, definition) {
|
|
3042
3552
|
definition.start?.filter !== void 0 && v2.report("start.filter", startFilterSyntaxIssues(definition.start.filter));
|
|
3043
3553
|
for (const [index, requirement] of (definition.start?.requirements ?? []).entries()) requirement.type === "groq" && v2.report(`start.requirements[${index}].query`, startRequirementSyntaxIssues(requirement.query));
|
|
3044
|
-
for (const entry of definition.fields ?? []) v2.checkEntry(entry, `workflow.fields "${entry.name}"`);
|
|
3045
|
-
for (const stage of definition.stages) validateStage(v2, stage);
|
|
3046
|
-
for (const issue of checkWorkflowInvariants(definition)) v2.errors.push(` · ${formatIssuePath(issue.path)}: ${issue.message}`);
|
|
3047
|
-
if (v2.errors.length > 0) throw new Error(`validateDefinition("${definition.name}"): ${v2.errors.length} deploy-time validation error${v2.errors.length === 1 ? "" : "s"}:\n` + v2.errors.join(`\n`));
|
|
3048
3554
|
}
|
|
3049
3555
|
|
|
3050
3556
|
function createDefinitionValidator() {
|
|
@@ -3075,33 +3581,12 @@ function createDefinitionValidator() {
|
|
|
3075
3581
|
|
|
3076
3582
|
function conditionSiteLabel({stage: stage, address: address}) {
|
|
3077
3583
|
const at = `stage "${stage}"`;
|
|
3078
|
-
|
|
3079
|
-
case "predicate":
|
|
3080
|
-
return `predicate "${address.predicate}"`;
|
|
3081
|
-
|
|
3082
|
-
case "transition":
|
|
3083
|
-
return `${at} transition "${address.transition}" when`;
|
|
3084
|
-
|
|
3085
|
-
case "activity-filter":
|
|
3086
|
-
return `${at} activity "${address.activity}".filter`;
|
|
3087
|
-
|
|
3088
|
-
case "requirement":
|
|
3089
|
-
return `${at} activity "${address.activity}".requirements "${address.requirement}"`;
|
|
3090
|
-
|
|
3091
|
-
case "action":
|
|
3092
|
-
return `${at} activity "${address.activity}" action "${address.action}".filter`;
|
|
3093
|
-
|
|
3094
|
-
case "action-when":
|
|
3095
|
-
return `${at} activity "${address.activity}" action "${address.action}".when`;
|
|
3096
|
-
|
|
3097
|
-
case "editable-override":
|
|
3098
|
-
return `${at}.editable "${address.name}"`;
|
|
3099
|
-
}
|
|
3584
|
+
return address.kind === "predicate" ? `predicate "${address.predicate}"` : address.kind === "transition" ? `${at} transition "${address.transition}" when` : address.kind === "activity-filter" ? `${at} activity "${address.activity}".filter` : address.kind === "requirement" ? `${at} activity "${address.activity}".requirements "${address.requirement}"` : address.kind === "action" ? `${at} activity "${address.activity}" action "${address.action}".filter` : address.kind === "action-when" ? `${at} activity "${address.activity}" action "${address.action}".when` : `${at}.editable "${address.name}"`;
|
|
3100
3585
|
}
|
|
3101
3586
|
|
|
3102
3587
|
function validateStage(v2, stage) {
|
|
3103
3588
|
for (const entry of stage.fields ?? []) v2.checkEntry(entry, `stage "${stage.name}".fields "${entry.name}"`);
|
|
3104
|
-
for (const guard of stage.guards ?? [])
|
|
3589
|
+
for (const guard of stage.guards ?? []) validateGuard({
|
|
3105
3590
|
v: v2,
|
|
3106
3591
|
stageName: stage.name,
|
|
3107
3592
|
guard: guard
|
|
@@ -3113,12 +3598,28 @@ function validateStage(v2, stage) {
|
|
|
3113
3598
|
});
|
|
3114
3599
|
}
|
|
3115
3600
|
|
|
3116
|
-
function
|
|
3601
|
+
function validateGuard({v: v2, stageName: stageName, guard: guard}) {
|
|
3117
3602
|
const where = `stage "${stageName}" guard "${guard.name}"`;
|
|
3118
|
-
|
|
3603
|
+
if (validateGuardMatch({
|
|
3604
|
+
v: v2,
|
|
3605
|
+
where: where,
|
|
3606
|
+
guard: guard
|
|
3607
|
+
}), guard.predicate !== void 0 && guard.predicate !== "") {
|
|
3608
|
+
const issue = guardPredicateSyntaxIssue(guard.predicate);
|
|
3609
|
+
issue !== void 0 && v2.report(`${where} predicate`, [ issue ]);
|
|
3610
|
+
}
|
|
3119
3611
|
for (const [key, expr] of Object.entries(guard.metadata ?? {})) v2.checkGuardRead(expr, `${where} metadata "${key}"`);
|
|
3120
3612
|
}
|
|
3121
3613
|
|
|
3614
|
+
function validateGuardMatch({v: v2, where: where, guard: guard}) {
|
|
3615
|
+
guard.match.actions.length === 0 && v2.report(`${where} match.actions`, [ GUARD_ACTIONS_REQUIRED_MESSAGE ]);
|
|
3616
|
+
for (const pattern of guard.match.idPatterns ?? []) {
|
|
3617
|
+
const issue = guardIdPatternIssue(pattern, guard.match.actions);
|
|
3618
|
+
issue !== void 0 && v2.report(`${where} match.idPatterns`, [ issue ]);
|
|
3619
|
+
}
|
|
3620
|
+
for (const expr of guard.match.idRefs ?? []) v2.checkGuardRead(expr, `${where} match.idRefs`);
|
|
3621
|
+
}
|
|
3622
|
+
|
|
3122
3623
|
function validateActivity({v: v2, stageName: stageName, activity: activity}) {
|
|
3123
3624
|
const where = `stage "${stageName}" activity "${activity.name}"`;
|
|
3124
3625
|
for (const entry of activity.fields ?? []) v2.checkEntry(entry, `${where}.fields "${entry.name}"`);
|
|
@@ -3220,214 +3721,52 @@ function buildClientForGdr(args) {
|
|
|
3220
3721
|
return sameResource(target, workflowResource) ? client : siblingFor({
|
|
3221
3722
|
client: client,
|
|
3222
3723
|
workflowResource: workflowResource,
|
|
3223
|
-
target: target,
|
|
3224
|
-
parsed: parsed,
|
|
3225
|
-
siblings: siblings
|
|
3226
|
-
});
|
|
3227
|
-
};
|
|
3228
|
-
}
|
|
3229
|
-
|
|
3230
|
-
function siblingFor(args) {
|
|
3231
|
-
const {client: client, workflowResource: workflowResource, target: target, parsed: parsed, siblings: siblings} = args, key = `${target.type}:${target.id}`, cached = siblings.get(key);
|
|
3232
|
-
if (cached !== void 0) return cached;
|
|
3233
|
-
if (client.withConfig === void 0) throw new ContractViolationError(`Cannot route "${gdrFromResource(target, parsed.documentId)}": the GDR targets ${target.type} "${target.id}", not the workflow resource (${workflowResource.type} "${workflowResource.id}"), and the workflow client cannot derive a sibling client for it (no withConfig). Pass resourceClients to route foreign resources, or use a client with withConfig (e.g. @sanity/client).`);
|
|
3234
|
-
const sibling = client.withConfig(siblingConfig(target));
|
|
3235
|
-
return siblings.set(key, sibling), sibling;
|
|
3236
|
-
}
|
|
3237
|
-
|
|
3238
|
-
function siblingConfig(target) {
|
|
3239
|
-
if (target.type === "dataset") {
|
|
3240
|
-
const {projectId: projectId, dataset: dataset} = datasetResourceParts(target.id);
|
|
3241
|
-
return {
|
|
3242
|
-
resource: target,
|
|
3243
|
-
projectId: projectId,
|
|
3244
|
-
dataset: dataset
|
|
3245
|
-
};
|
|
3246
|
-
}
|
|
3247
|
-
return {
|
|
3248
|
-
resource: target
|
|
3249
|
-
};
|
|
3250
|
-
}
|
|
3251
|
-
|
|
3252
|
-
const wallClock = () => /* @__PURE__ */ (new Date).toISOString();
|
|
3253
|
-
|
|
3254
|
-
function addMs(instant, ms) {
|
|
3255
|
-
return new Date(Date.parse(instant) + ms).toISOString();
|
|
3256
|
-
}
|
|
3257
|
-
|
|
3258
|
-
function hasPassed(instant, now) {
|
|
3259
|
-
return Date.parse(instant) <= Date.parse(now);
|
|
3260
|
-
}
|
|
3261
|
-
|
|
3262
|
-
const GUARD_DOC_TYPE = "temp.system.guard";
|
|
3263
|
-
|
|
3264
|
-
class MutationGuardDeniedError extends WorkflowError {
|
|
3265
|
-
denied;
|
|
3266
|
-
documentId;
|
|
3267
|
-
action;
|
|
3268
|
-
constructor(args) {
|
|
3269
|
-
const ids = args.denied.map(d => d.guardId).join(", ");
|
|
3270
|
-
super("mutation-guard-denied", `Mutation on "${args.documentId}" (${args.action}) denied by guard(s) [${ids}]`),
|
|
3271
|
-
this.name = "MutationGuardDeniedError", this.denied = args.denied, this.documentId = args.documentId,
|
|
3272
|
-
this.action = args.action;
|
|
3273
|
-
}
|
|
3274
|
-
static fromGuards(args) {
|
|
3275
|
-
return new MutationGuardDeniedError({
|
|
3276
|
-
documentId: args.documentId,
|
|
3277
|
-
action: args.action,
|
|
3278
|
-
denied: deniedGuardRefs(args.guards)
|
|
3279
|
-
});
|
|
3280
|
-
}
|
|
3281
|
-
}
|
|
3282
|
-
|
|
3283
|
-
function deniedGuardRefs(guards) {
|
|
3284
|
-
return guards.map(g => ({
|
|
3285
|
-
guardId: g._id,
|
|
3286
|
-
...g.name !== void 0 ? {
|
|
3287
|
-
name: g.name
|
|
3288
|
-
} : {}
|
|
3289
|
-
}));
|
|
3290
|
-
}
|
|
3291
|
-
|
|
3292
|
-
function deniedGuardLabels(denied) {
|
|
3293
|
-
return denied.map(d => d.name ?? d.guardId);
|
|
3294
|
-
}
|
|
3295
|
-
|
|
3296
|
-
function lakeGuardId(args) {
|
|
3297
|
-
return `${GUARD_DOC_TYPE}.${args.instanceDocId}.${args.guardName}`;
|
|
3298
|
-
}
|
|
3299
|
-
|
|
3300
|
-
function compileGuard(args) {
|
|
3301
|
-
return {
|
|
3302
|
-
_id: args.id,
|
|
3303
|
-
_type: GUARD_DOC_TYPE,
|
|
3304
|
-
resourceType: args.resourceType,
|
|
3305
|
-
resourceId: args.resourceId,
|
|
3306
|
-
owner: args.owner,
|
|
3307
|
-
sourceInstanceId: args.sourceInstanceId,
|
|
3308
|
-
sourceDefinition: args.sourceDefinition,
|
|
3309
|
-
sourceStage: args.sourceStage,
|
|
3310
|
-
...args.name !== void 0 ? {
|
|
3311
|
-
name: args.name
|
|
3312
|
-
} : {},
|
|
3313
|
-
...args.description !== void 0 ? {
|
|
3314
|
-
description: args.description
|
|
3315
|
-
} : {},
|
|
3316
|
-
match: args.match,
|
|
3317
|
-
predicate: args.predicate,
|
|
3318
|
-
metadata: args.metadata
|
|
3319
|
-
};
|
|
3320
|
-
}
|
|
3321
|
-
|
|
3322
|
-
function toGroqJsPredicate(predicate) {
|
|
3323
|
-
return predicate.replace(new RegExp("(?<!\\$)\\bguard\\.", "g"), "$guard.").replace(new RegExp("(?<!\\$)\\bmutation\\.", "g"), "$mutation.");
|
|
3324
|
-
}
|
|
3325
|
-
|
|
3326
|
-
function globMatch(pattern, value) {
|
|
3327
|
-
if (!pattern.includes("*")) return pattern === value;
|
|
3328
|
-
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
3329
|
-
return new RegExp(`^${escaped}$`).test(value);
|
|
3330
|
-
}
|
|
3331
|
-
|
|
3332
|
-
function guardMatches({guard: guard, doc: doc, action: action}) {
|
|
3333
|
-
const m = guard.match;
|
|
3334
|
-
if (!m.actions.includes(action) || m.types && m.types.length > 0 && !(doc.type !== void 0 && m.types.includes(doc.type))) return !1;
|
|
3335
|
-
if (m.idRefs && m.idRefs.length > 0 || m.idPatterns && m.idPatterns.length > 0) {
|
|
3336
|
-
const byRef = m.idRefs?.includes(doc.id) ?? !1, byPattern = m.idPatterns?.some(p => globMatch(p, doc.id)) ?? !1;
|
|
3337
|
-
if (!byRef && !byPattern) return !1;
|
|
3338
|
-
}
|
|
3339
|
-
return !0;
|
|
3340
|
-
}
|
|
3341
|
-
|
|
3342
|
-
function guardPredicateParams(args) {
|
|
3343
|
-
return {
|
|
3344
|
-
guard: args.guard,
|
|
3345
|
-
mutation: {
|
|
3346
|
-
action: args.action
|
|
3347
|
-
}
|
|
3348
|
-
};
|
|
3349
|
-
}
|
|
3350
|
-
|
|
3351
|
-
async function evaluateMutationGuard(args) {
|
|
3352
|
-
const {guard: guard, context: context} = args;
|
|
3353
|
-
if (guard.predicate === "") return !1;
|
|
3354
|
-
const params = guardPredicateParams({
|
|
3355
|
-
guard: guard,
|
|
3356
|
-
action: context.action
|
|
3357
|
-
});
|
|
3358
|
-
try {
|
|
3359
|
-
const tree = parse(toGroqJsPredicate(guard.predicate), {
|
|
3360
|
-
mode: "delta",
|
|
3361
|
-
params: params
|
|
3362
|
-
});
|
|
3363
|
-
return await (await evaluate(tree, {
|
|
3364
|
-
before: context.before,
|
|
3365
|
-
after: context.after,
|
|
3366
|
-
params: params,
|
|
3367
|
-
...context.identity !== void 0 ? {
|
|
3368
|
-
identity: context.identity
|
|
3369
|
-
} : {}
|
|
3370
|
-
})).get() === !0;
|
|
3371
|
-
} catch {
|
|
3372
|
-
return !1;
|
|
3373
|
-
}
|
|
3724
|
+
target: target,
|
|
3725
|
+
parsed: parsed,
|
|
3726
|
+
siblings: siblings
|
|
3727
|
+
});
|
|
3728
|
+
};
|
|
3374
3729
|
}
|
|
3375
3730
|
|
|
3376
|
-
|
|
3377
|
-
const {
|
|
3378
|
-
|
|
3379
|
-
|
|
3380
|
-
|
|
3381
|
-
|
|
3382
|
-
}) && (await evaluateMutationGuard({
|
|
3383
|
-
guard: guard,
|
|
3384
|
-
context: context
|
|
3385
|
-
}) || denied.push(guard));
|
|
3386
|
-
return denied;
|
|
3731
|
+
function siblingFor(args) {
|
|
3732
|
+
const {client: client, workflowResource: workflowResource, target: target, parsed: parsed, siblings: siblings} = args, key = `${target.type}:${target.id}`, cached = siblings.get(key);
|
|
3733
|
+
if (cached !== void 0) return cached;
|
|
3734
|
+
if (client.withConfig === void 0) throw new ContractViolationError(`Cannot route "${gdrFromResource(target, parsed.documentId)}": the GDR targets ${target.type} "${target.id}", not the workflow resource (${workflowResource.type} "${workflowResource.id}"), and the workflow client cannot derive a sibling client for it (no withConfig). Pass resourceClients to route foreign resources, or use a client with withConfig (e.g. @sanity/client).`);
|
|
3735
|
+
const sibling = client.withConfig(siblingConfig(target));
|
|
3736
|
+
return siblings.set(key, sibling), sibling;
|
|
3387
3737
|
}
|
|
3388
3738
|
|
|
3389
|
-
|
|
3390
|
-
|
|
3391
|
-
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
}
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
|
|
3400
|
-
|
|
3401
|
-
...identity !== void 0 ? {
|
|
3402
|
-
identity: identity
|
|
3403
|
-
} : {}
|
|
3404
|
-
}
|
|
3405
|
-
});
|
|
3739
|
+
function siblingConfig(target) {
|
|
3740
|
+
if (target.type === "dataset") {
|
|
3741
|
+
const {projectId: projectId, dataset: dataset} = datasetResourceParts(target.id);
|
|
3742
|
+
return {
|
|
3743
|
+
resource: target,
|
|
3744
|
+
projectId: projectId,
|
|
3745
|
+
dataset: dataset
|
|
3746
|
+
};
|
|
3747
|
+
}
|
|
3748
|
+
return {
|
|
3749
|
+
resource: target
|
|
3750
|
+
};
|
|
3406
3751
|
}
|
|
3407
3752
|
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
});
|
|
3753
|
+
const wallClock = () => /* @__PURE__ */ (new Date).toISOString(), tick = ms => new Promise(resolve => {
|
|
3754
|
+
setTimeout(resolve, ms);
|
|
3755
|
+
}), wallSleeper = async ms => {
|
|
3756
|
+
let left = ms;
|
|
3757
|
+
for (;left > 2147483647; ) await tick(2147483647), left -= 2147483647;
|
|
3758
|
+
await tick(left);
|
|
3759
|
+
};
|
|
3760
|
+
|
|
3761
|
+
function addMs(instant, ms) {
|
|
3762
|
+
return new Date(Date.parse(instant) + ms).toISOString();
|
|
3419
3763
|
}
|
|
3420
3764
|
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
if (denied.length !== 0) throw MutationGuardDeniedError.fromGuards({
|
|
3424
|
-
documentId: args.instance._id,
|
|
3425
|
-
action: "update",
|
|
3426
|
-
guards: denied
|
|
3427
|
-
});
|
|
3765
|
+
function hasPassed(instant, now) {
|
|
3766
|
+
return Date.parse(instant) <= Date.parse(now);
|
|
3428
3767
|
}
|
|
3429
3768
|
|
|
3430
|
-
const DATA_MODEL_VERSION =
|
|
3769
|
+
const DATA_MODEL_VERSION = 10, DATA_MODEL_MIN_READER = 4, DATA_MODEL_MAX_READER = 10, READER_MODEL_ROLLOUT_URL = "https://www.sanity.io/docs/workflows/prerelease";
|
|
3431
3770
|
|
|
3432
3771
|
class ReaderModelAcknowledgementError extends WorkflowError {
|
|
3433
3772
|
code="WORKFLOW_READER_MODEL_ACKNOWLEDGEMENT_MISMATCH";
|
|
@@ -3567,6 +3906,38 @@ const DATA_MODEL_CHANGES = Object.freeze([ Object.freeze({
|
|
|
3567
3906
|
compatibility: "reader-floor",
|
|
3568
3907
|
applicability: "detectable",
|
|
3569
3908
|
summary: "Assignee fields may restrict newly assigned users and collective roles by role."
|
|
3909
|
+
}), Object.freeze({
|
|
3910
|
+
id: "split-guard-id-spaces",
|
|
3911
|
+
introducedInModel: 9,
|
|
3912
|
+
minReaderModel: 9,
|
|
3913
|
+
documentTypes: Object.freeze([ "definition", "instance" ]),
|
|
3914
|
+
compatibility: "reader-floor",
|
|
3915
|
+
applicability: "detectable",
|
|
3916
|
+
summary: "Guards spanning edit and lifecycle id spaces emit independently retractable documents."
|
|
3917
|
+
}), Object.freeze({
|
|
3918
|
+
id: "singular-assignee-lists",
|
|
3919
|
+
introducedInModel: 9,
|
|
3920
|
+
minReaderModel: 9,
|
|
3921
|
+
documentTypes: Object.freeze([ "definition", "instance" ]),
|
|
3922
|
+
compatibility: "reader-floor",
|
|
3923
|
+
applicability: "detectable",
|
|
3924
|
+
summary: "Singular assignee fields use member lists with at most one user and any number of roles."
|
|
3925
|
+
}), Object.freeze({
|
|
3926
|
+
id: "required-content-references",
|
|
3927
|
+
introducedInModel: 10,
|
|
3928
|
+
minReaderModel: 10,
|
|
3929
|
+
documentTypes: Object.freeze([ "definition", "instance" ]),
|
|
3930
|
+
compatibility: "reader-floor",
|
|
3931
|
+
applicability: "detectable",
|
|
3932
|
+
summary: "Required content references must remain readable after initialization before actions or transitions advance."
|
|
3933
|
+
}), Object.freeze({
|
|
3934
|
+
id: "effect-retry-policy",
|
|
3935
|
+
introducedInModel: 10,
|
|
3936
|
+
minReaderModel: 10,
|
|
3937
|
+
documentTypes: Object.freeze([ "definition", "instance" ]),
|
|
3938
|
+
compatibility: "reader-floor",
|
|
3939
|
+
applicability: "detectable",
|
|
3940
|
+
summary: "An effect node declares a bounded retry policy the engine enforces on every runtime."
|
|
3570
3941
|
}) ]);
|
|
3571
3942
|
|
|
3572
3943
|
function recordOf(value) {
|
|
@@ -3690,6 +4061,33 @@ function hasRoleConstrainedAssignments(document) {
|
|
|
3690
4061
|
return persistedFieldEntries(document).some(entry => entryKindMatches(entry, assignmentKindAcceptsRoles) && Array.isArray(entry.roles) && entry.roles.length > 0);
|
|
3691
4062
|
}
|
|
3692
4063
|
|
|
4064
|
+
function hasSingularAssigneeLists(document) {
|
|
4065
|
+
const root = recordOf(document);
|
|
4066
|
+
if (root === void 0) return !1;
|
|
4067
|
+
if (typeof root.definitionSnapshot != "string") return persistedFieldEntries(document).some(entry => entryHasKind(entry, "assignee"));
|
|
4068
|
+
const snapshot = parsedDefinitionSnapshot(root);
|
|
4069
|
+
return typeof snapshot?.modelVersion == "number" && isAssignmentListModel(snapshot.modelVersion) && persistedFieldEntries(document).some(entry => entryHasKind(entry, "assignee"));
|
|
4070
|
+
}
|
|
4071
|
+
|
|
4072
|
+
function hasEffectRetryPolicy(document) {
|
|
4073
|
+
return persistedDefinitionTree(document)?.actions.some(action => recordsAt(action, "effects").some(effect => recordOf(effect.retry) !== void 0)) ?? !1;
|
|
4074
|
+
}
|
|
4075
|
+
|
|
4076
|
+
function isMutationGuardAction(action) {
|
|
4077
|
+
return typeof action == "string" && MUTATION_GUARD_ACTIONS.some(candidate => candidate === action);
|
|
4078
|
+
}
|
|
4079
|
+
|
|
4080
|
+
function hasSplitGuardIdSpaces(document) {
|
|
4081
|
+
return persistedDefinitionTree(document)?.stages.some(stage => recordsAt(stage, "guards").some(guard => {
|
|
4082
|
+
const actions = recordOf(guard.match)?.actions;
|
|
4083
|
+
return Array.isArray(actions) && mutationGuardRequiresSplitEmission(actions.filter(isMutationGuardAction));
|
|
4084
|
+
})) ?? !1;
|
|
4085
|
+
}
|
|
4086
|
+
|
|
4087
|
+
function hasRequiredContentReferences(document) {
|
|
4088
|
+
return persistedDefinitionTree(document)?.roots.some(root => recordsAt(root, "fields").some(field => field.required === !0 && typeof field.type == "string" && refKindAcceptsTypes(field.type))) ?? !1;
|
|
4089
|
+
}
|
|
4090
|
+
|
|
3693
4091
|
const featureDetectors = {
|
|
3694
4092
|
"governed-model-stamps": () => !0,
|
|
3695
4093
|
"subject-field-kind": document => hasFieldKind(document, "subject"),
|
|
@@ -3702,6 +4100,10 @@ const featureDetectors = {
|
|
|
3702
4100
|
"readiness-requirements": hasReadinessRequirements,
|
|
3703
4101
|
"due-date-field-kinds": document => hasFieldKind(document, "dueDate") || hasFieldKind(document, "dueDatetime"),
|
|
3704
4102
|
"role-constrained-assignment-fields": hasRoleConstrainedAssignments,
|
|
4103
|
+
"singular-assignee-lists": hasSingularAssigneeLists,
|
|
4104
|
+
"split-guard-id-spaces": hasSplitGuardIdSpaces,
|
|
4105
|
+
"required-content-references": hasRequiredContentReferences,
|
|
4106
|
+
"effect-retry-policy": hasEffectRetryPolicy,
|
|
3705
4107
|
"node-semantics": hasNodeSemantics,
|
|
3706
4108
|
"field-patch-ops": document => hasPersistedOpType(document, [ "field.inc", "field.dec", "field.setIfMissing" ]),
|
|
3707
4109
|
"attributes-condition-var": () => !0
|
|
@@ -3841,6 +4243,91 @@ function findActivityNode(args) {
|
|
|
3841
4243
|
return findStageNode(args)?.activities?.find(entry => entry.name === args.activityName);
|
|
3842
4244
|
}
|
|
3843
4245
|
|
|
4246
|
+
function requirementDescriptor(requirement) {
|
|
4247
|
+
return {
|
|
4248
|
+
name: requirement.name,
|
|
4249
|
+
...requirement.title !== void 0 ? {
|
|
4250
|
+
title: requirement.title
|
|
4251
|
+
} : {},
|
|
4252
|
+
...requirement.description !== void 0 ? {
|
|
4253
|
+
description: requirement.description
|
|
4254
|
+
} : {}
|
|
4255
|
+
};
|
|
4256
|
+
}
|
|
4257
|
+
|
|
4258
|
+
function subjectDenialLabels(denied) {
|
|
4259
|
+
return denied.map(d => `${d.permission} on ${d.subject} (${d.resource})`);
|
|
4260
|
+
}
|
|
4261
|
+
|
|
4262
|
+
class ActionDisabledError extends WorkflowError {
|
|
4263
|
+
reason;
|
|
4264
|
+
activity;
|
|
4265
|
+
action;
|
|
4266
|
+
constructor(args) {
|
|
4267
|
+
super("action-disabled", formatDisabledReason({
|
|
4268
|
+
activity: args.activity,
|
|
4269
|
+
action: args.action,
|
|
4270
|
+
reason: args.reason
|
|
4271
|
+
})), this.name = "ActionDisabledError", this.reason = args.reason, this.activity = args.activity,
|
|
4272
|
+
this.action = args.action;
|
|
4273
|
+
}
|
|
4274
|
+
}
|
|
4275
|
+
|
|
4276
|
+
class StartNotAllowedError extends WorkflowError {
|
|
4277
|
+
definition;
|
|
4278
|
+
unmetRequirements;
|
|
4279
|
+
constructor(args) {
|
|
4280
|
+
super("start-not-allowed", `startInstance refused definition "${args.definition}": ${args.unmetRequirements.map(requirement => requirement.title ?? requirement.name).join(", ")}. Pre-flight the verdict with evaluateStart.`),
|
|
4281
|
+
this.name = "StartNotAllowedError", this.definition = args.definition, this.unmetRequirements = args.unmetRequirements;
|
|
4282
|
+
}
|
|
4283
|
+
}
|
|
4284
|
+
|
|
4285
|
+
function actionRendering(action) {
|
|
4286
|
+
const kind = action.disabledReason?.kind;
|
|
4287
|
+
return kind === "filter-failed" ? "absent" : action.triggered === !0 || kind === "cascade-fired" ? "automation" : "button";
|
|
4288
|
+
}
|
|
4289
|
+
|
|
4290
|
+
const disabledReasonDetail = {
|
|
4291
|
+
"filter-failed": r => `action filter returned false${r.detail ? ` (${r.detail})` : ""}`,
|
|
4292
|
+
"cascade-fired": r => `the action is cascade-fired (when: ${JSON.stringify(r.when)}) — the engine fires it on truth; it cannot be invoked via fireAction`,
|
|
4293
|
+
"activity-not-active": r => `activity status is "${r.status}"`,
|
|
4294
|
+
"stage-terminal": r => `stage "${r.stage}" is terminal`,
|
|
4295
|
+
"instance-completed": r => `instance completed at ${r.completedAt}`,
|
|
4296
|
+
"instance-aborted": r => `instance aborted at ${r.abortedAt}`,
|
|
4297
|
+
"requirements-unmet": r => `unmet requirement(s): ${r.unmetRequirements.map(requirement => requirement.name).join(", ")}`,
|
|
4298
|
+
"subject-permission-denied": r => `missing subject permission(s): ${subjectDenialLabels(r.denied).join(", ")}`
|
|
4299
|
+
};
|
|
4300
|
+
|
|
4301
|
+
function actionDisabledDetail(reason) {
|
|
4302
|
+
return disabledReasonDetail[reason.kind](reason);
|
|
4303
|
+
}
|
|
4304
|
+
|
|
4305
|
+
function formatDisabledReason({activity: activity, action: action, reason: reason}) {
|
|
4306
|
+
return `Action "${activity}:${action}" is not allowed: ${actionDisabledDetail(reason)}`;
|
|
4307
|
+
}
|
|
4308
|
+
|
|
4309
|
+
class EditFieldDeniedError extends WorkflowError {
|
|
4310
|
+
reason;
|
|
4311
|
+
target;
|
|
4312
|
+
constructor(args) {
|
|
4313
|
+
super("edit-field-denied", formatEditDisabledReason(args.target, args.reason)),
|
|
4314
|
+
this.name = "EditFieldDeniedError", this.reason = args.reason, this.target = args.target;
|
|
4315
|
+
}
|
|
4316
|
+
}
|
|
4317
|
+
|
|
4318
|
+
const editDisabledReasonDetail = {
|
|
4319
|
+
"not-editable": () => "field is not declared editable",
|
|
4320
|
+
"instance-completed": r => `instance completed at ${r.completedAt}`,
|
|
4321
|
+
"instance-aborted": r => `instance aborted at ${r.abortedAt}`,
|
|
4322
|
+
"edit-window-closed": r => `edit window closed (${r.detail})`,
|
|
4323
|
+
"editor-not-permitted": r => `editor not permitted (${r.predicate})`
|
|
4324
|
+
};
|
|
4325
|
+
|
|
4326
|
+
function formatEditDisabledReason(target, reason) {
|
|
4327
|
+
const detail = editDisabledReasonDetail[reason.kind](reason), where = target.activity !== void 0 ? `${target.activity}.${target.field}` : target.field;
|
|
4328
|
+
return `Field "${target.scope}:${where}" is not editable: ${detail}`;
|
|
4329
|
+
}
|
|
4330
|
+
|
|
3844
4331
|
function liveChildrenField(instance) {
|
|
3845
4332
|
const live = liveSubworkflows(instance).length;
|
|
3846
4333
|
return live > 0 ? {
|
|
@@ -3853,9 +4340,13 @@ function abortReason(instance) {
|
|
|
3853
4340
|
}
|
|
3854
4341
|
|
|
3855
4342
|
function diagnoseInputFromEvaluation(evaluation) {
|
|
4343
|
+
if (evaluation.missingDocuments?.length && evaluation.blockingMissingDocuments === void 0) throw new Error("Missing blocking document evidence in workflow evaluation");
|
|
3856
4344
|
const stage = openStage(evaluation.instance);
|
|
3857
4345
|
return {
|
|
3858
4346
|
instance: evaluation.instance,
|
|
4347
|
+
...evaluation.missingDocuments !== void 0 ? {
|
|
4348
|
+
missingDocuments: evaluation.blockingMissingDocuments ?? []
|
|
4349
|
+
} : {},
|
|
3859
4350
|
activities: evaluation.currentStage.activities,
|
|
3860
4351
|
transitions: evaluation.currentStage.transitions,
|
|
3861
4352
|
assignees: Object.fromEntries(evaluation.currentStage.activities.map(t => [ t.activity.name, assigneesOf(stage, t.activity.name) ]))
|
|
@@ -3867,7 +4358,8 @@ function openStage(instance) {
|
|
|
3867
4358
|
}
|
|
3868
4359
|
|
|
3869
4360
|
function assigneesOf(stage, activityName) {
|
|
3870
|
-
|
|
4361
|
+
const entry = stage?.activities.find(t => t.name === activityName);
|
|
4362
|
+
return [ ...assignmentMembers(entry?.fields ?? []) ];
|
|
3871
4363
|
}
|
|
3872
4364
|
|
|
3873
4365
|
function isResolved(status) {
|
|
@@ -3900,11 +4392,16 @@ function failedActivityCause(input) {
|
|
|
3900
4392
|
|
|
3901
4393
|
function waitingState(input) {
|
|
3902
4394
|
const active = input.activities.find(t => t.status === "active" && (t.activity.actions ?? []).length > 0 && (t.unmetRequirements ?? []).length === 0);
|
|
3903
|
-
if (active
|
|
4395
|
+
if (active === void 0) return;
|
|
4396
|
+
const actions = active.actions.filter(action => actionRendering(action) === "button" && action.allowed).map(action => action.action.name), hasManualAction = active.actions.some(action => !isCascadeFired(action.action));
|
|
4397
|
+
let waitingFor = "automation";
|
|
4398
|
+
return actions.length > 0 ? waitingFor = "caller" : hasManualAction && (waitingFor = "manual-action"),
|
|
4399
|
+
{
|
|
3904
4400
|
state: "waiting",
|
|
3905
4401
|
activity: active.activity.name,
|
|
3906
4402
|
assignees: input.assignees[active.activity.name] ?? [],
|
|
3907
|
-
actions:
|
|
4403
|
+
actions: actions,
|
|
4404
|
+
waitingFor: waitingFor
|
|
3908
4405
|
};
|
|
3909
4406
|
}
|
|
3910
4407
|
|
|
@@ -3968,7 +4465,7 @@ function diagnoseInstance(input) {
|
|
|
3968
4465
|
at: instance.completedAt,
|
|
3969
4466
|
...liveChildrenField(instance)
|
|
3970
4467
|
};
|
|
3971
|
-
const cause = stuckFromDocumentState(input) ?? noTransitionFiresCause(input);
|
|
4468
|
+
const cause = stuckFromDocumentState(input) ?? missingDocumentCause(input.missingDocuments) ?? noTransitionFiresCause(input);
|
|
3972
4469
|
return cause !== void 0 ? {
|
|
3973
4470
|
state: "stuck",
|
|
3974
4471
|
cause: cause
|
|
@@ -3977,10 +4474,27 @@ function diagnoseInstance(input) {
|
|
|
3977
4474
|
};
|
|
3978
4475
|
}
|
|
3979
4476
|
|
|
4477
|
+
function missingDocumentCause(documents = []) {
|
|
4478
|
+
return documents.length > 0 ? {
|
|
4479
|
+
kind: "document-missing",
|
|
4480
|
+
documents: documents
|
|
4481
|
+
} : void 0;
|
|
4482
|
+
}
|
|
4483
|
+
|
|
4484
|
+
function _additionalMissingDocuments(diagnosis, documents) {
|
|
4485
|
+
return diagnosis.state === "stuck" && diagnosis.cause.kind !== "document-missing" ? documents ?? [] : [];
|
|
4486
|
+
}
|
|
4487
|
+
|
|
3980
4488
|
const RUNNABLE_VERBS = /* @__PURE__ */ new Set([ "set-stage", "abort", "drain-effects", "reset-activity" ]);
|
|
3981
4489
|
|
|
3982
4490
|
function remediationsForCause(cause) {
|
|
3983
4491
|
switch (cause.kind) {
|
|
4492
|
+
case "document-missing":
|
|
4493
|
+
return [ {
|
|
4494
|
+
verb: "abort",
|
|
4495
|
+
rationale: "Required content cannot be read. Investigate the referenced documents, or abort the instance if it is no longer needed."
|
|
4496
|
+
} ];
|
|
4497
|
+
|
|
3984
4498
|
case "failed-effect":
|
|
3985
4499
|
return [ {
|
|
3986
4500
|
verb: "retry-effect",
|
|
@@ -4030,7 +4544,7 @@ function isClaimExpired(claim, now) {
|
|
|
4030
4544
|
return claim.leaseExpiresAt === void 0 || hasPassed(claim.leaseExpiresAt, now);
|
|
4031
4545
|
}
|
|
4032
4546
|
|
|
4033
|
-
const EFFECT_RUN_STATUSES = [ "done", "failed", "cancelled" ];
|
|
4547
|
+
const EFFECT_RUN_STATUSES = _exhaustiveOptions()([ "done", "failed", "cancelled" ]);
|
|
4034
4548
|
|
|
4035
4549
|
function userLoginProvider(user) {
|
|
4036
4550
|
return user?.provider ?? user?.loginProvider;
|
|
@@ -4075,12 +4589,12 @@ function joinCacheFor(client) {
|
|
|
4075
4589
|
}
|
|
4076
4590
|
|
|
4077
4591
|
function isClientProjectMember(value) {
|
|
4078
|
-
return isRecord(value) && typeof value.id == "string";
|
|
4592
|
+
return isRecord(value) && typeof value.id == "string" && (value.isRobot === void 0 || typeof value.isRobot == "boolean");
|
|
4079
4593
|
}
|
|
4080
4594
|
|
|
4081
|
-
async function
|
|
4595
|
+
async function _requestProjectMembers(request, projectId) {
|
|
4082
4596
|
const project = await request({
|
|
4083
|
-
|
|
4597
|
+
url: `/projects/${encodeURIComponent(projectId)}`
|
|
4084
4598
|
}), members = isRecord(project) ? project.members : void 0;
|
|
4085
4599
|
if (!Array.isArray(members) || !members.every(isClientProjectMember)) throw new Error(`Project member directory response is invalid for project "${projectId}"`);
|
|
4086
4600
|
return members;
|
|
@@ -4088,7 +4602,7 @@ async function requestProjectMembers(request, projectId) {
|
|
|
4088
4602
|
|
|
4089
4603
|
async function requestProjectUsers(args) {
|
|
4090
4604
|
const {request: request, projectId: projectId, memberIds: memberIds} = args, response = await request({
|
|
4091
|
-
|
|
4605
|
+
url: `/projects/${encodeURIComponent(projectId)}/users/${memberIds.map(encodeURIComponent).join(",")}`
|
|
4092
4606
|
}), rows = Array.isArray(response) ? response : [ response ];
|
|
4093
4607
|
if (!rows.every(isClientProjectUser)) throw new Error(`Project user directory response is invalid for project "${projectId}"`);
|
|
4094
4608
|
const requested = new Set(memberIds);
|
|
@@ -4096,7 +4610,7 @@ async function requestProjectUsers(args) {
|
|
|
4096
4610
|
}
|
|
4097
4611
|
|
|
4098
4612
|
async function requestProjectMemberDirectory(args) {
|
|
4099
|
-
const {request: request, projectId: projectId} = args, members = await
|
|
4613
|
+
const {request: request, projectId: projectId} = args, members = await _requestProjectMembers(request, projectId), memberIds = members.map(member => member.id).filter(id => classifyPrincipalId(id).namespace === "project");
|
|
4100
4614
|
if (memberIds.length === 0) return {
|
|
4101
4615
|
members: members,
|
|
4102
4616
|
globalUsers: []
|
|
@@ -4171,7 +4685,7 @@ function clientProjectUserDirectory(client, projectId) {
|
|
|
4171
4685
|
});
|
|
4172
4686
|
try {
|
|
4173
4687
|
const response = await client.request({
|
|
4174
|
-
|
|
4688
|
+
url: `/projects/${encodeURIComponent(projectId)}/users/${encodeURIComponent(id)}`
|
|
4175
4689
|
}), candidate = Array.isArray(response) ? response[0] : response;
|
|
4176
4690
|
return candidate == null ? {
|
|
4177
4691
|
status: "missing"
|
|
@@ -4283,8 +4797,30 @@ function collectWatchRefs(instance) {
|
|
|
4283
4797
|
}
|
|
4284
4798
|
|
|
4285
4799
|
function collectSubjectRefs(instance) {
|
|
4286
|
-
const
|
|
4287
|
-
return [ ...entryDocRefs(
|
|
4800
|
+
const entries = activeFieldEntries(instance).map(({entry: entry}) => entry);
|
|
4801
|
+
return [ ...entryDocRefs(entries), ...entryReleaseRefs(entries) ];
|
|
4802
|
+
}
|
|
4803
|
+
|
|
4804
|
+
function activeFieldEntries(instance) {
|
|
4805
|
+
const stage = findOpenStageEntry(instance);
|
|
4806
|
+
return [ ...scopedEntries(instance.fields, {
|
|
4807
|
+
scope: "workflow"
|
|
4808
|
+
}), ...scopedEntries(stage?.fields, {
|
|
4809
|
+
scope: "stage"
|
|
4810
|
+
}), ...(stage?.activities ?? []).flatMap(activity => scopedEntries(activity.fields, {
|
|
4811
|
+
scope: "activity",
|
|
4812
|
+
activity: activity.name
|
|
4813
|
+
})) ];
|
|
4814
|
+
}
|
|
4815
|
+
|
|
4816
|
+
function scopedEntries(entries, scope) {
|
|
4817
|
+
return (entries ?? []).map(entry => ({
|
|
4818
|
+
target: {
|
|
4819
|
+
...scope,
|
|
4820
|
+
field: entry.name
|
|
4821
|
+
},
|
|
4822
|
+
entry: entry
|
|
4823
|
+
}));
|
|
4288
4824
|
}
|
|
4289
4825
|
|
|
4290
4826
|
function foreignSubjectRefs(instance) {
|
|
@@ -4385,6 +4921,10 @@ function entrySubjectRefs(entries) {
|
|
|
4385
4921
|
return fieldEntries(entries).flatMap(entry => (typeof entry._type == "string" && isSingleDocRefKind(entry._type) || entry._type === "release.ref") && isGdr(entry.value) ? [ entry.value ] : []);
|
|
4386
4922
|
}
|
|
4387
4923
|
|
|
4924
|
+
function missingEntryDocRefs(entries, knownIds) {
|
|
4925
|
+
return entryDocRefs(entries).filter(reference => !knownIds.has(reference.id));
|
|
4926
|
+
}
|
|
4927
|
+
|
|
4388
4928
|
function fieldEntries(entries) {
|
|
4389
4929
|
return Array.isArray(entries) ? entries.filter(entry => !!entry && typeof entry == "object") : [];
|
|
4390
4930
|
}
|
|
@@ -4812,6 +5352,96 @@ function coerceToGdr(raw, workflowResource) {
|
|
|
4812
5352
|
} : null;
|
|
4813
5353
|
}
|
|
4814
5354
|
|
|
5355
|
+
const NOW = {
|
|
5356
|
+
variable: "now",
|
|
5357
|
+
path: []
|
|
5358
|
+
}, LAST_REPRESENTABLE_MS = 864e13;
|
|
5359
|
+
|
|
5360
|
+
function readsNowIn(reads) {
|
|
5361
|
+
return reads.some(read => read.variable === "now");
|
|
5362
|
+
}
|
|
5363
|
+
|
|
5364
|
+
function readsNow(condition) {
|
|
5365
|
+
return readsNowIn(analyzeCondition(condition).reads);
|
|
5366
|
+
}
|
|
5367
|
+
|
|
5368
|
+
async function stageTimeSites(args) {
|
|
5369
|
+
const {definition: definition, stage: stage, scopeFor: scopeFor} = args, sites = [];
|
|
5370
|
+
for (const site of conditionSitesOf(definition)) {
|
|
5371
|
+
if (site.stage !== stage || site.address.kind === "editable-override" || !readsNow(site.condition)) continue;
|
|
5372
|
+
const activity = "activity" in site.address ? site.address.activity : void 0;
|
|
5373
|
+
sites.push({
|
|
5374
|
+
condition: site.condition,
|
|
5375
|
+
params: await scopeFor(activity)
|
|
5376
|
+
});
|
|
5377
|
+
}
|
|
5378
|
+
return sites;
|
|
5379
|
+
}
|
|
5380
|
+
|
|
5381
|
+
async function nextEvaluationInstant(args) {
|
|
5382
|
+
const {sites: sites, dataset: dataset, now: now} = args, boundaries = [];
|
|
5383
|
+
for (const site of sites) {
|
|
5384
|
+
const boundary = await siteBoundary({
|
|
5385
|
+
...site,
|
|
5386
|
+
dataset: dataset,
|
|
5387
|
+
now: now
|
|
5388
|
+
});
|
|
5389
|
+
boundary !== void 0 && boundaries.push(boundary);
|
|
5390
|
+
}
|
|
5391
|
+
return [ ...boundaries ].sort(byInstant)[0];
|
|
5392
|
+
}
|
|
5393
|
+
|
|
5394
|
+
function byInstant(a, b) {
|
|
5395
|
+
return Date.parse(a) - Date.parse(b);
|
|
5396
|
+
}
|
|
5397
|
+
|
|
5398
|
+
async function siteBoundary(args) {
|
|
5399
|
+
const {condition: condition, params: params, dataset: dataset, now: now} = args, insight = await explainCondition({
|
|
5400
|
+
condition: condition,
|
|
5401
|
+
dataset: dataset,
|
|
5402
|
+
params: params
|
|
5403
|
+
});
|
|
5404
|
+
for (const candidate of await candidateInstants({
|
|
5405
|
+
insight: insight,
|
|
5406
|
+
params: params,
|
|
5407
|
+
dataset: dataset,
|
|
5408
|
+
now: now
|
|
5409
|
+
})) if ((await whatIfCondition({
|
|
5410
|
+
condition: condition,
|
|
5411
|
+
dataset: dataset,
|
|
5412
|
+
params: params,
|
|
5413
|
+
assign: {
|
|
5414
|
+
target: NOW,
|
|
5415
|
+
value: candidate
|
|
5416
|
+
}
|
|
5417
|
+
})).changed) return candidate;
|
|
5418
|
+
}
|
|
5419
|
+
|
|
5420
|
+
async function candidateInstants(args) {
|
|
5421
|
+
const {insight: insight, params: params, dataset: dataset, now: now} = args, comparands = [];
|
|
5422
|
+
for (const atom of insight.atoms) readsNowIn(atom.atom.reads) && comparands.push(...await comparandsOf({
|
|
5423
|
+
atom: atom,
|
|
5424
|
+
params: params,
|
|
5425
|
+
dataset: dataset
|
|
5426
|
+
}));
|
|
5427
|
+
const instants = comparands.filter(isParseableInstant).map(normalizeInstant).filter(instant => Date.parse(instant) < LAST_REPRESENTABLE_MS).flatMap(instant => [ instant, addMs(instant, 1) ]).filter(instant => !hasPassed(instant, now));
|
|
5428
|
+
return [ ...new Set(instants) ].sort(byInstant);
|
|
5429
|
+
}
|
|
5430
|
+
|
|
5431
|
+
function normalizeInstant(value) {
|
|
5432
|
+
return new Date(Date.parse(value)).toISOString();
|
|
5433
|
+
}
|
|
5434
|
+
|
|
5435
|
+
async function comparandsOf(args) {
|
|
5436
|
+
const {atom: atom, params: params, dataset: dataset} = args, requirement = atom.requirement, values = requirement !== void 0 && requirement.target.variable === "now" && "value" in requirement ? [ requirement.value ] : [];
|
|
5437
|
+
for (const read of atom.atom.reads) read.variable !== "now" && values.push(await runGroq({
|
|
5438
|
+
groq: formatRead(read),
|
|
5439
|
+
params: params,
|
|
5440
|
+
dataset: dataset
|
|
5441
|
+
}));
|
|
5442
|
+
return values;
|
|
5443
|
+
}
|
|
5444
|
+
|
|
4815
5445
|
const NonEmpty = NonEmptyString, PersistedAssignmentRolesSchema = v.pipe(v.array(NonEmptyString), v.minLength(1, "assignment roles must not be empty")), UnknownRecord = v.record(v.string(), v.unknown()), PersistedChoiceOptionsSchema = v.looseObject({
|
|
4816
5446
|
list: v.array(v.looseObject({
|
|
4817
5447
|
title: v.string(),
|
|
@@ -4864,7 +5494,7 @@ function fieldArm(kind, value) {
|
|
|
4864
5494
|
};
|
|
4865
5495
|
}
|
|
4866
5496
|
|
|
4867
|
-
const OptionalRefTypes = v.exactOptional(v.array(v.string())),
|
|
5497
|
+
const OptionalRefTypes = v.exactOptional(v.array(v.string())), RawResolvedFieldEntrySchema = v.variant("_type", [ v.looseObject(tolerantEntries()({
|
|
4868
5498
|
...fieldArm("doc.ref", fieldValueSchemas["doc.ref"]),
|
|
4869
5499
|
types: OptionalRefTypes
|
|
4870
5500
|
})), v.looseObject(tolerantEntries()({
|
|
@@ -4885,7 +5515,7 @@ const OptionalRefTypes = v.exactOptional(v.array(v.string())), ResolvedFieldEntr
|
|
|
4885
5515
|
})), v.looseObject(tolerantEntries()({
|
|
4886
5516
|
...fieldArm("array", v.array(UnknownRecord)),
|
|
4887
5517
|
of: v.array(PersistedFieldShapeSchema)
|
|
4888
|
-
})) ]), EffectOriginSchema = tolerantObject()({
|
|
5518
|
+
})) ]), ResolvedFieldEntrySchema = v.pipe(RawResolvedFieldEntrySchema, v.transform(normalizeNestedAssignmentValues)), EffectOriginSchema = tolerantObject()({
|
|
4889
5519
|
kind: v.literal("action"),
|
|
4890
5520
|
name: NonEmpty
|
|
4891
5521
|
}), PendingEffectClaimSchema = tolerantObject()({
|
|
@@ -5565,7 +6195,7 @@ async function resolveThroughDirectory(args) {
|
|
|
5565
6195
|
if (safeMisses.length === 0 || client.request === void 0) return out;
|
|
5566
6196
|
try {
|
|
5567
6197
|
const response = await client.request({
|
|
5568
|
-
|
|
6198
|
+
url: `/projects/${encodeURIComponent(projectId)}/users/${safeMisses.map(encodeURIComponent).join(",")}`,
|
|
5569
6199
|
tag: REQUEST_TAG.accessResolveActor
|
|
5570
6200
|
});
|
|
5571
6201
|
ingestDirectoryRows({
|
|
@@ -5746,6 +6376,31 @@ function buildInstanceBase(args) {
|
|
|
5746
6376
|
};
|
|
5747
6377
|
}
|
|
5748
6378
|
|
|
6379
|
+
function instanceDocumentForWrite(instance) {
|
|
6380
|
+
const listModel = writesSingularAssignmentLists(instance);
|
|
6381
|
+
return {
|
|
6382
|
+
...instance,
|
|
6383
|
+
fields: assignmentFieldsForWrite(instance.fields, listModel),
|
|
6384
|
+
stages: assignmentStagesForWrite(instance.stages, listModel)
|
|
6385
|
+
};
|
|
6386
|
+
}
|
|
6387
|
+
|
|
6388
|
+
function memoizedBy(compute) {
|
|
6389
|
+
const cache = /* @__PURE__ */ new Map;
|
|
6390
|
+
return key => {
|
|
6391
|
+
const hit = cache.get(key);
|
|
6392
|
+
if (hit !== void 0) return hit;
|
|
6393
|
+
const value = (async () => compute(key))();
|
|
6394
|
+
return cache.set(key, value), value;
|
|
6395
|
+
};
|
|
6396
|
+
}
|
|
6397
|
+
|
|
6398
|
+
function chunks(values, size) {
|
|
6399
|
+
return Array.from({
|
|
6400
|
+
length: Math.ceil(values.length / size)
|
|
6401
|
+
}, (_, index) => values.slice(index * size, (index + 1) * size));
|
|
6402
|
+
}
|
|
6403
|
+
|
|
5749
6404
|
async function hydrateSnapshot(args) {
|
|
5750
6405
|
const {client: client, clientForGdr: clientForGdr, instance: instance, overlay: overlay} = args, loaded = [], visited = /* @__PURE__ */ new Set;
|
|
5751
6406
|
loaded.push({
|
|
@@ -5856,12 +6511,6 @@ function groupReads(reads) {
|
|
|
5856
6511
|
return [ ...byClient.values() ].flatMap(groups => [ ...groups.values() ]);
|
|
5857
6512
|
}
|
|
5858
6513
|
|
|
5859
|
-
function chunks(values, size) {
|
|
5860
|
-
return Array.from({
|
|
5861
|
-
length: Math.ceil(values.length / size)
|
|
5862
|
-
}, (_, index) => values.slice(index * size, (index + 1) * size));
|
|
5863
|
-
}
|
|
5864
|
-
|
|
5865
6514
|
function collectEntryDocUris(resolvedFieldEntries) {
|
|
5866
6515
|
return entryDocRefs(resolvedFieldEntries).map(ref => ref.id);
|
|
5867
6516
|
}
|
|
@@ -5896,16 +6545,26 @@ function loadCallContext({client: client, instanceId: instanceId, options: optio
|
|
|
5896
6545
|
|
|
5897
6546
|
async function retryOnRevisionConflict(args) {
|
|
5898
6547
|
const {client: client, instanceId: instanceId, options: options, commit: commit, onExhausted: onExhausted} = args;
|
|
5899
|
-
|
|
5900
|
-
|
|
6548
|
+
return retryCommit({
|
|
6549
|
+
load: () => loadCallContext({
|
|
5901
6550
|
client: client,
|
|
5902
6551
|
instanceId: instanceId,
|
|
5903
6552
|
options: options
|
|
5904
|
-
})
|
|
6553
|
+
}),
|
|
6554
|
+
commit: commit,
|
|
6555
|
+
shouldRetry: isInstanceCommitRevisionConflict,
|
|
6556
|
+
onExhausted: onExhausted
|
|
6557
|
+
});
|
|
6558
|
+
}
|
|
6559
|
+
|
|
6560
|
+
async function retryCommit(args) {
|
|
6561
|
+
const {load: load, commit: commit, shouldRetry: shouldRetry, onExhausted: onExhausted} = args;
|
|
6562
|
+
for (let attempt = 1; attempt <= CONCURRENT_COMMIT_MAX_ATTEMPTS; attempt++) {
|
|
6563
|
+
const ctx = await load();
|
|
5905
6564
|
try {
|
|
5906
6565
|
return await commit(ctx);
|
|
5907
6566
|
} catch (error) {
|
|
5908
|
-
if (!
|
|
6567
|
+
if (!shouldRetry(error)) throw error;
|
|
5909
6568
|
}
|
|
5910
6569
|
}
|
|
5911
6570
|
throw onExhausted();
|
|
@@ -5953,6 +6612,22 @@ async function ctxConditionParams(ctx, opts) {
|
|
|
5953
6612
|
}, opts);
|
|
5954
6613
|
}
|
|
5955
6614
|
|
|
6615
|
+
async function ctxNextEvaluationAt(ctx) {
|
|
6616
|
+
if (ctx.instance.completedAt !== void 0) return;
|
|
6617
|
+
const scopeFor = memoizedBy(activityName => ctxConditionParams(ctx, activityName === void 0 ? void 0 : {
|
|
6618
|
+
activityName: activityName
|
|
6619
|
+
})), sites = await stageTimeSites({
|
|
6620
|
+
definition: ctx.definition,
|
|
6621
|
+
stage: ctx.instance.currentStage,
|
|
6622
|
+
scopeFor: scopeFor
|
|
6623
|
+
});
|
|
6624
|
+
if (sites.length !== 0) return nextEvaluationInstant({
|
|
6625
|
+
sites: sites,
|
|
6626
|
+
dataset: ctx.snapshot.docs,
|
|
6627
|
+
now: ctx.now
|
|
6628
|
+
});
|
|
6629
|
+
}
|
|
6630
|
+
|
|
5956
6631
|
async function ctxEvaluateConditionOutcome({ctx: ctx, condition: condition, opts: opts}) {
|
|
5957
6632
|
return condition === void 0 ? "satisfied" : evaluateConditionOutcome({
|
|
5958
6633
|
condition: condition,
|
|
@@ -6124,7 +6799,7 @@ async function resolveActivityFieldEntries(args) {
|
|
|
6124
6799
|
|
|
6125
6800
|
async function resolveBindings(args) {
|
|
6126
6801
|
const resolved = {};
|
|
6127
|
-
for (const [key, groq] of Object.entries(args.bindings ?? {})) resolved[key] = await runGroq({
|
|
6802
|
+
for (const [key, groq] of Object.entries(args.bindings ?? {})) resolved[key] = await runGroq$1({
|
|
6128
6803
|
groq: groq,
|
|
6129
6804
|
params: args.params,
|
|
6130
6805
|
snapshot: args.snapshot
|
|
@@ -6581,7 +7256,7 @@ function resolveGuard({guard: guard, instance: instance, stageName: stageName, n
|
|
|
6581
7256
|
if (route === null) return null;
|
|
6582
7257
|
const resource = assertSingleResource(route.targets), types = resolveMatchTypes(route.targets, guard.match.types);
|
|
6583
7258
|
return {
|
|
6584
|
-
|
|
7259
|
+
docs: compileGuards({
|
|
6585
7260
|
id: route.guardId,
|
|
6586
7261
|
resourceType: resource.type,
|
|
6587
7262
|
resourceId: resource.id,
|
|
@@ -6613,19 +7288,63 @@ function resolveGuard({guard: guard, instance: instance, stageName: stageName, n
|
|
|
6613
7288
|
async function upsertGuard(args) {
|
|
6614
7289
|
const {client: client, doc: doc, exists: exists} = args;
|
|
6615
7290
|
if (!exists) try {
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
|
|
6619
|
-
|
|
6620
|
-
|
|
7291
|
+
return {
|
|
7292
|
+
created: await client.create(doc, {
|
|
7293
|
+
...SYNC_COMMIT,
|
|
7294
|
+
tag: REQUEST_TAG.guardDeploy
|
|
7295
|
+
})
|
|
7296
|
+
};
|
|
6621
7297
|
} catch (error) {
|
|
6622
7298
|
if (!isCreateIdCollision(error)) throw error;
|
|
6623
7299
|
}
|
|
6624
7300
|
const {_id: _id, _type: _type, _rev: _rev, _createdAt: _createdAt, _updatedAt: _updatedAt, ...body} = doc;
|
|
6625
|
-
await client.patch(doc._id).set(body).commit({
|
|
7301
|
+
return await client.patch(doc._id).set(body).commit({
|
|
6626
7302
|
...SYNC_COMMIT,
|
|
6627
7303
|
tag: REQUEST_TAG.guardRefresh
|
|
7304
|
+
}), {
|
|
7305
|
+
created: void 0
|
|
7306
|
+
};
|
|
7307
|
+
}
|
|
7308
|
+
|
|
7309
|
+
async function retractCreatedGuards(guards) {
|
|
7310
|
+
for (const {client: client, doc: doc} of guards.toReversed()) {
|
|
7311
|
+
if (doc._rev === void 0) return new Error(`Cannot roll back guard ${doc._id}: created document has no revision`);
|
|
7312
|
+
try {
|
|
7313
|
+
const revisionCheck = client.patch(doc._id).set({
|
|
7314
|
+
predicate: doc.predicate
|
|
7315
|
+
}).ifRevisionId(doc._rev);
|
|
7316
|
+
await client.transaction().patch(revisionCheck).delete(doc._id).commit({
|
|
7317
|
+
tag: REQUEST_TAG.guardDeploy
|
|
7318
|
+
});
|
|
7319
|
+
} catch (error) {
|
|
7320
|
+
return error;
|
|
7321
|
+
}
|
|
7322
|
+
}
|
|
7323
|
+
}
|
|
7324
|
+
|
|
7325
|
+
async function deployResolvedGuard(args) {
|
|
7326
|
+
const {client: client, resourceKey: resourceKey, doc: doc, guard: guard, stageArgs: stageArgs, existingByResource: existingByResource} = args, localized = await localizeGuardMetadata({
|
|
7327
|
+
doc: doc,
|
|
7328
|
+
guard: guard,
|
|
7329
|
+
client: client,
|
|
7330
|
+
instance: stageArgs.instance,
|
|
7331
|
+
definition: stageArgs.definition
|
|
6628
7332
|
});
|
|
7333
|
+
return (await upsertGuard({
|
|
7334
|
+
client: client,
|
|
7335
|
+
doc: localized,
|
|
7336
|
+
exists: existingByResource.get(resourceKey)?.has(doc._id) ?? !1
|
|
7337
|
+
})).created;
|
|
7338
|
+
}
|
|
7339
|
+
|
|
7340
|
+
async function failGuardDeploy(args) {
|
|
7341
|
+
const {cause: cause, stageName: stageName, deployed: deployed, createdGuards: createdGuards, updatedExistingGuard: updatedExistingGuard} = args, rollbackError = updatedExistingGuard ? void 0 : await retractCreatedGuards(createdGuards);
|
|
7342
|
+
throw deployed > 0 && (updatedExistingGuard || rollbackError !== void 0) ? new PartialGuardDeployError({
|
|
7343
|
+
stageName: stageName,
|
|
7344
|
+
deployed: deployed,
|
|
7345
|
+
cause: cause,
|
|
7346
|
+
rollbackError: rollbackError
|
|
7347
|
+
}) : cause;
|
|
6629
7348
|
}
|
|
6630
7349
|
|
|
6631
7350
|
function resolvedStageGuards(args) {
|
|
@@ -6638,10 +7357,12 @@ function resolvedStageGuards(args) {
|
|
|
6638
7357
|
now: args.now,
|
|
6639
7358
|
snapshot: args.snapshot
|
|
6640
7359
|
});
|
|
6641
|
-
resolved
|
|
6642
|
-
|
|
6643
|
-
|
|
6644
|
-
|
|
7360
|
+
if (resolved === null) continue;
|
|
7361
|
+
const client = args.clientForGdr(resolved.routeGdr), resourceKey = resourceGdr(resourceFromParsed(resolved.routeGdr));
|
|
7362
|
+
for (const doc of resolved.docs) out.push({
|
|
7363
|
+
client: client,
|
|
7364
|
+
resourceKey: resourceKey,
|
|
7365
|
+
value: doc,
|
|
6645
7366
|
guard: guard
|
|
6646
7367
|
});
|
|
6647
7368
|
}
|
|
@@ -6657,10 +7378,15 @@ function resolvedStageGuardRoutes(args) {
|
|
|
6657
7378
|
}, out = [];
|
|
6658
7379
|
for (const guard of stage?.guards ?? []) {
|
|
6659
7380
|
const route = resolveGuardRoute(guard, ctx);
|
|
6660
|
-
route
|
|
6661
|
-
|
|
6662
|
-
|
|
6663
|
-
|
|
7381
|
+
if (route === null) continue;
|
|
7382
|
+
const client = args.clientForGdr(route.routeGdr), resourceKey = resourceGdr(resourceFromParsed(route.routeGdr));
|
|
7383
|
+
for (const guardId of compiledGuardIds({
|
|
7384
|
+
id: route.guardId,
|
|
7385
|
+
actions: guard.match.actions
|
|
7386
|
+
})) out.push({
|
|
7387
|
+
client: client,
|
|
7388
|
+
resourceKey: resourceKey,
|
|
7389
|
+
value: guardId
|
|
6664
7390
|
});
|
|
6665
7391
|
}
|
|
6666
7392
|
return out;
|
|
@@ -6716,26 +7442,30 @@ async function deployStageGuards(args) {
|
|
|
6716
7442
|
if (live === void 0 || live.currentStage !== args.stageName || live.abortedAt !== void 0) return;
|
|
6717
7443
|
const routed = resolvedStageGuards(args), existingByResource = await existingGuardIds(groupByResource(routed));
|
|
6718
7444
|
let deployed = 0;
|
|
7445
|
+
const createdGuards = [];
|
|
7446
|
+
let updatedExistingGuard = !1;
|
|
6719
7447
|
for (const {client: client, resourceKey: resourceKey, value: doc, guard: guard} of routed) {
|
|
6720
7448
|
try {
|
|
6721
|
-
const
|
|
7449
|
+
const created = await deployResolvedGuard({
|
|
7450
|
+
client: client,
|
|
7451
|
+
resourceKey: resourceKey,
|
|
6722
7452
|
doc: doc,
|
|
6723
7453
|
guard: guard,
|
|
6724
|
-
|
|
6725
|
-
|
|
6726
|
-
definition: args.definition
|
|
7454
|
+
stageArgs: args,
|
|
7455
|
+
existingByResource: existingByResource
|
|
6727
7456
|
});
|
|
6728
|
-
|
|
7457
|
+
created === void 0 ? updatedExistingGuard = !0 : createdGuards.push({
|
|
6729
7458
|
client: client,
|
|
6730
|
-
doc:
|
|
6731
|
-
exists: existingByResource.get(resourceKey)?.has(doc._id) ?? !1
|
|
7459
|
+
doc: created
|
|
6732
7460
|
});
|
|
6733
7461
|
} catch (cause) {
|
|
6734
|
-
|
|
7462
|
+
await failGuardDeploy({
|
|
7463
|
+
cause: cause,
|
|
6735
7464
|
stageName: args.stageName,
|
|
6736
7465
|
deployed: deployed,
|
|
6737
|
-
|
|
6738
|
-
|
|
7466
|
+
createdGuards: createdGuards,
|
|
7467
|
+
updatedExistingGuard: updatedExistingGuard
|
|
7468
|
+
});
|
|
6739
7469
|
}
|
|
6740
7470
|
deployed += 1;
|
|
6741
7471
|
}
|
|
@@ -6967,7 +7697,8 @@ async function persistThenDeploy({ctx: ctx, mutation: mutation, deploy: deploy})
|
|
|
6967
7697
|
committedRev: committed._rev,
|
|
6968
7698
|
restore: instanceStateFields({
|
|
6969
7699
|
...ctx.instance,
|
|
6970
|
-
minReaderModel: minReaderModelOf(ctx.instance)
|
|
7700
|
+
minReaderModel: minReaderModelOf(ctx.instance),
|
|
7701
|
+
definitionModelVersion: definitionAssignmentModel(ctx.instance)
|
|
6971
7702
|
}),
|
|
6972
7703
|
unset: ctx.instance.completedAt === void 0 ? [ "completedAt" ] : [],
|
|
6973
7704
|
reversible: !spawned,
|
|
@@ -6999,6 +7730,29 @@ async function refreshStageGuards({ctx: ctx, mutation: mutation, stageName: stag
|
|
|
6999
7730
|
});
|
|
7000
7731
|
}
|
|
7001
7732
|
|
|
7733
|
+
function missingRequiredDocuments({instance: instance, definition: definition, snapshot: snapshot}) {
|
|
7734
|
+
return (definition.fields ?? []).filter(field => field.required === !0 && refKindAcceptsTypes(field.type)).flatMap(field => {
|
|
7735
|
+
const entry = instance.fields.find(candidate => candidate.name === field.name && candidate._type === field.type);
|
|
7736
|
+
return missingEntryDocRefs(entry === void 0 ? [] : [ entry ], snapshot.knownIds).map(reference => ({
|
|
7737
|
+
target: {
|
|
7738
|
+
scope: "workflow",
|
|
7739
|
+
field: field.name
|
|
7740
|
+
},
|
|
7741
|
+
reference: reference
|
|
7742
|
+
}));
|
|
7743
|
+
});
|
|
7744
|
+
}
|
|
7745
|
+
|
|
7746
|
+
function requiredDocumentRequirements(args) {
|
|
7747
|
+
return [ ...new Set(missingRequiredDocuments(args).map(({target: target}) => target.field)) ].map(field => {
|
|
7748
|
+
const title = args.definition.fields?.find(entry => entry.name === field)?.title ?? field;
|
|
7749
|
+
return {
|
|
7750
|
+
name: `field:${field}`,
|
|
7751
|
+
title: `Documents selected for “${title}” must be readable`
|
|
7752
|
+
};
|
|
7753
|
+
});
|
|
7754
|
+
}
|
|
7755
|
+
|
|
7002
7756
|
async function discoverItems(args) {
|
|
7003
7757
|
const {client: client, groq: groq, params: params, perspective: perspective} = args;
|
|
7004
7758
|
return client.fetch(groq, paramsForLake(params), {
|
|
@@ -7232,7 +7986,7 @@ async function projectRowFields({ctx: ctx, sub: sub, childDefinition: childDefin
|
|
|
7232
7986
|
for (const [name, groq] of Object.entries(sub.with ?? {})) {
|
|
7233
7987
|
const declared = (childDefinition.fields ?? []).find(e => e.name === name);
|
|
7234
7988
|
if (declared === void 0) throw new Error(`spawn.with["${name}"]: subworkflow "${childDefinition.name}" declares no field entry "${name}"`);
|
|
7235
|
-
const raw = await runGroq({
|
|
7989
|
+
const raw = await runGroq$1({
|
|
7236
7990
|
groq: groq,
|
|
7237
7991
|
params: {
|
|
7238
7992
|
...parentScope,
|
|
@@ -7275,7 +8029,7 @@ async function projectRowFields({ctx: ctx, sub: sub, childDefinition: childDefin
|
|
|
7275
8029
|
async function resolveContextHandoff({ctx: ctx, sub: sub, parentScope: parentScope}) {
|
|
7276
8030
|
const out = {};
|
|
7277
8031
|
for (const [name, groq] of Object.entries(sub.context ?? {})) {
|
|
7278
|
-
const v2 = await runGroq({
|
|
8032
|
+
const v2 = await runGroq$1({
|
|
7279
8033
|
groq: groq,
|
|
7280
8034
|
params: parentScope,
|
|
7281
8035
|
snapshot: ctx.snapshot
|
|
@@ -7574,7 +8328,7 @@ async function persistStageMove({ctx: ctx, mutation: mutation, exitedStage: exit
|
|
|
7574
8328
|
}
|
|
7575
8329
|
|
|
7576
8330
|
async function pickTransition(ctx, stage) {
|
|
7577
|
-
for (const candidate of stage.transitions ?? []) {
|
|
8331
|
+
if (!(missingRequiredDocuments(ctx).length > 0)) for (const candidate of stage.transitions ?? []) {
|
|
7578
8332
|
const outcome = await ctxEvaluateConditionOutcome({
|
|
7579
8333
|
ctx: ctx,
|
|
7580
8334
|
condition: candidate.when
|
|
@@ -7676,7 +8430,7 @@ const actorCache = /* @__PURE__ */ new WeakMap, grantsCache = /* @__PURE__ */ ne
|
|
|
7676
8430
|
|
|
7677
8431
|
async function resolveAccess(taggedClient, args = {}) {
|
|
7678
8432
|
const client = unwrapRequestTag(taggedClient), requestFn = lazyRequest(client);
|
|
7679
|
-
if (requestFn === void 0) throw new ContractViolationError("workflow: no actor available. The engine resolves the actor from the client's token via `client.request({
|
|
8433
|
+
if (requestFn === void 0) throw new ContractViolationError("workflow: no actor available. The engine resolves the actor from the client's token via `client.request({url: '/users/me'})`. Supply a real `@sanity/client` configured with a token (the test bench serves these endpoints per registered token).");
|
|
7680
8434
|
const grantsPromise = args.grantsFromPath !== void 0 ? cachedGrants({
|
|
7681
8435
|
client: client,
|
|
7682
8436
|
requestFn: requestFn,
|
|
@@ -7745,20 +8499,32 @@ function cachedByKey(args) {
|
|
|
7745
8499
|
}
|
|
7746
8500
|
|
|
7747
8501
|
async function fetchActor(client, requestFn) {
|
|
7748
|
-
const
|
|
8502
|
+
const resourceUser = await fetchCurrentUser(requestFn, "the workflow resource host"), resourceId = usableId(resourceUser), resourceGlobalId = firstCarriedGlobalId([ resourceId ]) ?? directoryBridgeId(resourceUser?.sanityUserId);
|
|
8503
|
+
if (resourceGlobalId !== void 0) return identityFromUsers({
|
|
8504
|
+
id: resourceGlobalId,
|
|
8505
|
+
resourceUser: resourceUser
|
|
8506
|
+
});
|
|
8507
|
+
const globalUser = await fetchGlobalUser(client), globalHostId = usableId(globalUser.user), carriedId = firstCarriedGlobalId([ globalHostId ]), sessionId = carriedId ?? globalHostId ?? resourceId;
|
|
7749
8508
|
if (sessionId === void 0) return;
|
|
7750
8509
|
const bridge = carriedId === void 0 ? await bridgeProjectPrincipal({
|
|
7751
8510
|
client: client,
|
|
7752
8511
|
sessionId: sessionId,
|
|
7753
8512
|
resourceId: resourceId
|
|
7754
8513
|
}) : NO_BRIDGE, id = bridge.status === "resolved" ? bridge.globalId : sessionId;
|
|
7755
|
-
refuseProjectScopedActor({
|
|
8514
|
+
return refuseProjectScopedActor({
|
|
7756
8515
|
id: id,
|
|
7757
8516
|
globalUser: globalUser,
|
|
7758
8517
|
globalHostId: globalHostId,
|
|
7759
8518
|
bridgeFailure: bridge.status === "unavailable" ? bridge : void 0
|
|
8519
|
+
}), identityFromUsers({
|
|
8520
|
+
id: id,
|
|
8521
|
+
resourceUser: resourceUser,
|
|
8522
|
+
globalUser: globalUser.user
|
|
7760
8523
|
});
|
|
7761
|
-
|
|
8524
|
+
}
|
|
8525
|
+
|
|
8526
|
+
function identityFromUsers(args) {
|
|
8527
|
+
const {id: id, resourceUser: resourceUser, globalUser: globalUser} = args, resourceId = usableId(resourceUser), roleNames = roleNamesFor(resourceUser, globalUser);
|
|
7762
8528
|
return {
|
|
7763
8529
|
actor: {
|
|
7764
8530
|
kind: "person",
|
|
@@ -7774,7 +8540,7 @@ async function fetchActor(client, requestFn) {
|
|
|
7774
8540
|
}
|
|
7775
8541
|
|
|
7776
8542
|
function roleNamesFor(resourceUser, globalUser) {
|
|
7777
|
-
return (resourceUser?.roles?.length ? resourceUser : globalUser
|
|
8543
|
+
return (resourceUser?.roles?.length ? resourceUser : globalUser)?.roles?.map(r => r.name).filter(n => !!n) ?? [];
|
|
7778
8544
|
}
|
|
7779
8545
|
|
|
7780
8546
|
const NO_BRIDGE = {
|
|
@@ -7838,11 +8604,11 @@ function usableId(user) {
|
|
|
7838
8604
|
async function fetchCurrentUser(requestFn, hostDescription) {
|
|
7839
8605
|
try {
|
|
7840
8606
|
return await requestFn({
|
|
7841
|
-
|
|
8607
|
+
url: "/users/me",
|
|
7842
8608
|
tag: REQUEST_TAG.accessResolveActor
|
|
7843
8609
|
});
|
|
7844
8610
|
} catch (err) {
|
|
7845
|
-
throw new Error(`workflow: /users/me request against ${hostDescription} failed. The engine resolves the actor from the client's token via \`client.request({
|
|
8611
|
+
throw new Error(`workflow: /users/me request against ${hostDescription} failed. The engine resolves the actor from the client's token via \`client.request({url: '/users/me'})\`. Check the token/connectivity.`, {
|
|
7846
8612
|
cause: err
|
|
7847
8613
|
});
|
|
7848
8614
|
}
|
|
@@ -7859,7 +8625,7 @@ async function fetchGlobalUser(client) {
|
|
|
7859
8625
|
};
|
|
7860
8626
|
try {
|
|
7861
8627
|
const user = await sibling.request({
|
|
7862
|
-
|
|
8628
|
+
url: "/users/me",
|
|
7863
8629
|
tag: REQUEST_TAG.accessResolveActor
|
|
7864
8630
|
}), id = usableId(user);
|
|
7865
8631
|
return sibling.requireGlobalPrincipal && id !== void 0 && classifyPrincipalId(id).namespace === "project" ? {
|
|
@@ -7982,7 +8748,7 @@ async function resolveOrganizationId(args) {
|
|
|
7982
8748
|
load: async () => {
|
|
7983
8749
|
try {
|
|
7984
8750
|
const project = await requestFn({
|
|
7985
|
-
|
|
8751
|
+
url: `/projects/${encodeURIComponent(projectId)}`,
|
|
7986
8752
|
tag: REQUEST_TAG.accessResolveOrg
|
|
7987
8753
|
});
|
|
7988
8754
|
if (!isRecord(project)) return;
|
|
@@ -8007,15 +8773,17 @@ function httpStatusOf(err) {
|
|
|
8007
8773
|
return typeof status == "number" ? status : void 0;
|
|
8008
8774
|
}
|
|
8009
8775
|
|
|
8776
|
+
function attributesRequestOf(client) {
|
|
8777
|
+
const version = client.config?.().apiVersion, request = typeof client.withConfig == "function" || version === void 0 || version === ENGINE_API_VERSION ? lazyRequest(pinApiVersion(client)) : void 0;
|
|
8778
|
+
return request === void 0 && console.warn("workflow: skipped org attributes fetch; the client cannot issue API-version-pinned requests. Advisory $attributes stays undefined (conditions referencing it fail closed)."),
|
|
8779
|
+
request;
|
|
8780
|
+
}
|
|
8781
|
+
|
|
8010
8782
|
async function fetchAttributesCached(args) {
|
|
8011
|
-
const {client: client, orgId: orgId} = args,
|
|
8012
|
-
if (
|
|
8013
|
-
|
|
8014
|
-
|
|
8015
|
-
}
|
|
8016
|
-
try {
|
|
8017
|
-
const response = await sibling.request({
|
|
8018
|
-
uri: `/organizations/${encodeURIComponent(orgId)}/users/me/attributes?limit=${ATTRIBUTES_FETCH_LIMIT}`,
|
|
8783
|
+
const {client: client, orgId: orgId} = args, request = attributesRequestOf(client);
|
|
8784
|
+
if (request !== void 0) try {
|
|
8785
|
+
const response = await request({
|
|
8786
|
+
url: `/organizations/${encodeURIComponent(orgId)}/users/me/attributes?limit=${ATTRIBUTES_FETCH_LIMIT}`,
|
|
8019
8787
|
tag: REQUEST_TAG.accessAttributes
|
|
8020
8788
|
}), envelope = isRecord(response) ? response : void 0;
|
|
8021
8789
|
if (envelope?.hasMore === !0) {
|
|
@@ -8061,91 +8829,6 @@ async function callerBoundVarsForCall(args) {
|
|
|
8061
8829
|
});
|
|
8062
8830
|
}
|
|
8063
8831
|
|
|
8064
|
-
function requirementDescriptor(requirement) {
|
|
8065
|
-
return {
|
|
8066
|
-
name: requirement.name,
|
|
8067
|
-
...requirement.title !== void 0 ? {
|
|
8068
|
-
title: requirement.title
|
|
8069
|
-
} : {},
|
|
8070
|
-
...requirement.description !== void 0 ? {
|
|
8071
|
-
description: requirement.description
|
|
8072
|
-
} : {}
|
|
8073
|
-
};
|
|
8074
|
-
}
|
|
8075
|
-
|
|
8076
|
-
function subjectDenialLabels(denied) {
|
|
8077
|
-
return denied.map(d => `${d.permission} on ${d.subject} (${d.resource})`);
|
|
8078
|
-
}
|
|
8079
|
-
|
|
8080
|
-
class ActionDisabledError extends WorkflowError {
|
|
8081
|
-
reason;
|
|
8082
|
-
activity;
|
|
8083
|
-
action;
|
|
8084
|
-
constructor(args) {
|
|
8085
|
-
super("action-disabled", formatDisabledReason({
|
|
8086
|
-
activity: args.activity,
|
|
8087
|
-
action: args.action,
|
|
8088
|
-
reason: args.reason
|
|
8089
|
-
})), this.name = "ActionDisabledError", this.reason = args.reason, this.activity = args.activity,
|
|
8090
|
-
this.action = args.action;
|
|
8091
|
-
}
|
|
8092
|
-
}
|
|
8093
|
-
|
|
8094
|
-
class StartNotAllowedError extends WorkflowError {
|
|
8095
|
-
definition;
|
|
8096
|
-
unmetRequirements;
|
|
8097
|
-
constructor(args) {
|
|
8098
|
-
super("start-not-allowed", `startInstance refused definition "${args.definition}": ${args.unmetRequirements.map(requirement => requirement.title ?? requirement.name).join(", ")}. Pre-flight the verdict with evaluateStart.`),
|
|
8099
|
-
this.name = "StartNotAllowedError", this.definition = args.definition, this.unmetRequirements = args.unmetRequirements;
|
|
8100
|
-
}
|
|
8101
|
-
}
|
|
8102
|
-
|
|
8103
|
-
function actionRendering(action) {
|
|
8104
|
-
const kind = action.disabledReason?.kind;
|
|
8105
|
-
return kind === "filter-failed" ? "absent" : action.triggered === !0 || kind === "cascade-fired" ? "automation" : "button";
|
|
8106
|
-
}
|
|
8107
|
-
|
|
8108
|
-
const disabledReasonDetail = {
|
|
8109
|
-
"filter-failed": r => `action filter returned false${r.detail ? ` (${r.detail})` : ""}`,
|
|
8110
|
-
"cascade-fired": r => `the action is cascade-fired (when: ${JSON.stringify(r.when)}) — the engine fires it on truth; it cannot be invoked via fireAction`,
|
|
8111
|
-
"activity-not-active": r => `activity status is "${r.status}"`,
|
|
8112
|
-
"stage-terminal": r => `stage "${r.stage}" is terminal`,
|
|
8113
|
-
"instance-completed": r => `instance completed at ${r.completedAt}`,
|
|
8114
|
-
"instance-aborted": r => `instance aborted at ${r.abortedAt}`,
|
|
8115
|
-
"requirements-unmet": r => `unmet requirement(s): ${r.unmetRequirements.map(requirement => requirement.name).join(", ")}`,
|
|
8116
|
-
"subject-permission-denied": r => `missing subject permission(s): ${subjectDenialLabels(r.denied).join(", ")}`
|
|
8117
|
-
};
|
|
8118
|
-
|
|
8119
|
-
function actionDisabledDetail(reason) {
|
|
8120
|
-
return disabledReasonDetail[reason.kind](reason);
|
|
8121
|
-
}
|
|
8122
|
-
|
|
8123
|
-
function formatDisabledReason({activity: activity, action: action, reason: reason}) {
|
|
8124
|
-
return `Action "${activity}:${action}" is not allowed: ${actionDisabledDetail(reason)}`;
|
|
8125
|
-
}
|
|
8126
|
-
|
|
8127
|
-
class EditFieldDeniedError extends WorkflowError {
|
|
8128
|
-
reason;
|
|
8129
|
-
target;
|
|
8130
|
-
constructor(args) {
|
|
8131
|
-
super("edit-field-denied", formatEditDisabledReason(args.target, args.reason)),
|
|
8132
|
-
this.name = "EditFieldDeniedError", this.reason = args.reason, this.target = args.target;
|
|
8133
|
-
}
|
|
8134
|
-
}
|
|
8135
|
-
|
|
8136
|
-
const editDisabledReasonDetail = {
|
|
8137
|
-
"not-editable": () => "field is not declared editable",
|
|
8138
|
-
"instance-completed": r => `instance completed at ${r.completedAt}`,
|
|
8139
|
-
"instance-aborted": r => `instance aborted at ${r.abortedAt}`,
|
|
8140
|
-
"edit-window-closed": r => `edit window closed (${r.detail})`,
|
|
8141
|
-
"editor-not-permitted": r => `editor not permitted (${r.predicate})`
|
|
8142
|
-
};
|
|
8143
|
-
|
|
8144
|
-
function formatEditDisabledReason(target, reason) {
|
|
8145
|
-
const detail = editDisabledReasonDetail[reason.kind](reason), where = target.activity !== void 0 ? `${target.activity}.${target.field}` : target.field;
|
|
8146
|
-
return `Field "${target.scope}:${where}" is not editable: ${detail}`;
|
|
8147
|
-
}
|
|
8148
|
-
|
|
8149
8832
|
async function fireAction(args) {
|
|
8150
8833
|
const {client: client, instanceId: instanceId, activity: activity, action: action, params: params, requestRecord: requestRecord, options: options} = args;
|
|
8151
8834
|
return retryOnRevisionConflict({
|
|
@@ -8214,6 +8897,15 @@ async function resolveActionCommit({ctx: ctx, activityName: activityName, action
|
|
|
8214
8897
|
actionName: actionName,
|
|
8215
8898
|
status: entry?.status
|
|
8216
8899
|
});
|
|
8900
|
+
const unmetRequirements = requiredDocumentRequirements(ctx);
|
|
8901
|
+
if (unmetRequirements.length > 0) throw new ActionDisabledError({
|
|
8902
|
+
activity: activityName,
|
|
8903
|
+
action: actionName,
|
|
8904
|
+
reason: {
|
|
8905
|
+
kind: "requirements-unmet",
|
|
8906
|
+
unmetRequirements: unmetRequirements
|
|
8907
|
+
}
|
|
8908
|
+
});
|
|
8217
8909
|
const params = validateActionParams({
|
|
8218
8910
|
action: action,
|
|
8219
8911
|
activityName: activityName,
|
|
@@ -8313,7 +9005,7 @@ async function commitAction({ctx: ctx, activityName: activityName, actionName: a
|
|
|
8313
9005
|
actionName: actionName,
|
|
8314
9006
|
callerParams: callerParams,
|
|
8315
9007
|
options: options
|
|
8316
|
-
}), mutation =
|
|
9008
|
+
}), mutation = startRevisionRetryMutation(ctx.instance);
|
|
8317
9009
|
recordProcessedRequest({
|
|
8318
9010
|
mutation: mutation,
|
|
8319
9011
|
record: requestRecord,
|
|
@@ -8370,13 +9062,13 @@ async function runTriggeredActions({ctx: ctx, mutation: mutation, stage: stage})
|
|
|
8370
9062
|
async function runActivityTriggers({ctx: ctx, mutation: mutation, activity: activity}) {
|
|
8371
9063
|
let fires = 0, ranFieldOps = !1;
|
|
8372
9064
|
for (const action of activity.actions ?? []) {
|
|
8373
|
-
if (!isCascadeFired(action)) continue;
|
|
8374
9065
|
const entry = currentActivities(mutation).find(e => e.name === activity.name);
|
|
8375
9066
|
if (entry === void 0 || entry.status !== "active") break;
|
|
8376
|
-
if (
|
|
9067
|
+
if (!await triggerIsLive({
|
|
8377
9068
|
ctx: ctx,
|
|
8378
9069
|
activity: activity,
|
|
8379
|
-
action: action
|
|
9070
|
+
action: action,
|
|
9071
|
+
entry: entry
|
|
8380
9072
|
})) continue;
|
|
8381
9073
|
const result = await fireTriggeredAction({
|
|
8382
9074
|
ctx: ctx,
|
|
@@ -8393,8 +9085,12 @@ async function runActivityTriggers({ctx: ctx, mutation: mutation, activity: acti
|
|
|
8393
9085
|
};
|
|
8394
9086
|
}
|
|
8395
9087
|
|
|
8396
|
-
|
|
8397
|
-
|
|
9088
|
+
function isUnfiredTrigger(action, entry) {
|
|
9089
|
+
return isCascadeFired(action) && !(entry.firedActions ?? []).includes(action.name);
|
|
9090
|
+
}
|
|
9091
|
+
|
|
9092
|
+
async function triggerIsLive({ctx: ctx, activity: activity, action: action, entry: entry}) {
|
|
9093
|
+
if (!isUnfiredTrigger(action, entry) || missingRequiredDocuments(ctx).length > 0 || !tokenMayExecute({
|
|
8398
9094
|
actor: ctx.actor,
|
|
8399
9095
|
action: action,
|
|
8400
9096
|
ctx: ctx
|
|
@@ -8479,7 +9175,7 @@ async function primeInitialStage(args) {
|
|
|
8479
9175
|
})
|
|
8480
9176
|
});
|
|
8481
9177
|
const terminal = isTerminalStage(stage), committed = await client.patch(instance._id).set({
|
|
8482
|
-
stages: [ initialStageEntry ],
|
|
9178
|
+
stages: assignmentStagesForWrite([ initialStageEntry ], writesSingularAssignmentLists(instance)),
|
|
8483
9179
|
lastChangedAt: now,
|
|
8484
9180
|
...terminal ? {
|
|
8485
9181
|
completedAt: now
|
|
@@ -8537,32 +9233,59 @@ async function runCascadeHop({client: client, instanceId: instanceId, actor: act
|
|
|
8537
9233
|
settlingCohorts: settlingCohorts
|
|
8538
9234
|
} : {},
|
|
8539
9235
|
memberRolesLoader: memberRolesLoader
|
|
8540
|
-
}
|
|
8541
|
-
|
|
8542
|
-
|
|
8543
|
-
|
|
8544
|
-
|
|
8545
|
-
|
|
8546
|
-
|
|
8547
|
-
|
|
8548
|
-
|
|
9236
|
+
};
|
|
9237
|
+
let preloadedInstance = instance;
|
|
9238
|
+
return retryCommit({
|
|
9239
|
+
load: async () => {
|
|
9240
|
+
if (preloadedInstance === void 0) return loadContext({
|
|
9241
|
+
client: client,
|
|
9242
|
+
instanceId: instanceId,
|
|
9243
|
+
options: contextOptions
|
|
9244
|
+
});
|
|
9245
|
+
const loaded = preloadedInstance;
|
|
9246
|
+
return preloadedInstance = void 0, buildEngineContext({
|
|
9247
|
+
client: client,
|
|
9248
|
+
instance: loaded,
|
|
9249
|
+
definition: parseDefinitionSnapshot(loaded),
|
|
9250
|
+
...contextOptions
|
|
9251
|
+
});
|
|
9252
|
+
},
|
|
9253
|
+
commit: ctx => commitCascadeHop({
|
|
9254
|
+
ctx: ctx,
|
|
9255
|
+
actor: actor
|
|
9256
|
+
}),
|
|
9257
|
+
shouldRetry: isInstanceCommitRevisionConflict,
|
|
9258
|
+
onExhausted: () => new ConcurrentCascadeError({
|
|
9259
|
+
instanceId: instanceId,
|
|
9260
|
+
attempts: CONCURRENT_COMMIT_MAX_ATTEMPTS
|
|
9261
|
+
})
|
|
8549
9262
|
});
|
|
9263
|
+
}
|
|
9264
|
+
|
|
9265
|
+
async function commitCascadeHop({ctx: ctx, actor: actor}) {
|
|
8550
9266
|
if (isTerminal(ctx)) return {
|
|
8551
9267
|
moved: !1
|
|
8552
9268
|
};
|
|
8553
|
-
const stage = findStage(ctx.definition, ctx.instance.currentStage), mutation =
|
|
9269
|
+
const stage = findStage(ctx.definition, ctx.instance.currentStage), mutation = startRevisionRetryMutation(ctx.instance), fired = await runTriggeredActions({
|
|
8554
9270
|
ctx: ctx,
|
|
8555
9271
|
mutation: mutation,
|
|
8556
9272
|
stage: stage
|
|
8557
9273
|
}), hopCtx = liveViewContext(ctx, mutation), transition = await pickTransition(hopCtx, stage);
|
|
8558
|
-
|
|
8559
|
-
|
|
8560
|
-
|
|
8561
|
-
|
|
8562
|
-
|
|
8563
|
-
|
|
8564
|
-
|
|
8565
|
-
|
|
9274
|
+
if (transition === void 0) {
|
|
9275
|
+
const nextEvaluationAt = await ctxNextEvaluationAt(hopCtx);
|
|
9276
|
+
return fired.fires > 0 && await persistThenMaybeRefresh({
|
|
9277
|
+
ctx: ctx,
|
|
9278
|
+
mutation: mutation,
|
|
9279
|
+
stageName: stage.name,
|
|
9280
|
+
didChangeState: fired.ranFieldOps
|
|
9281
|
+
}), {
|
|
9282
|
+
moved: !1,
|
|
9283
|
+
...nextEvaluationAt !== void 0 ? {
|
|
9284
|
+
nextEvaluationAt: nextEvaluationAt
|
|
9285
|
+
} : {}
|
|
9286
|
+
};
|
|
9287
|
+
}
|
|
9288
|
+
return await commitStageMove({
|
|
8566
9289
|
ctx: ctx,
|
|
8567
9290
|
mutation: mutation,
|
|
8568
9291
|
fromStage: stage,
|
|
@@ -8572,7 +9295,7 @@ async function runCascadeHop({client: client, instanceId: instanceId, actor: act
|
|
|
8572
9295
|
actor: actor
|
|
8573
9296
|
}), {
|
|
8574
9297
|
moved: !0
|
|
8575
|
-
}
|
|
9298
|
+
};
|
|
8576
9299
|
}
|
|
8577
9300
|
|
|
8578
9301
|
async function cascadeAutoTransitions({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, executionContext: executionContext, telemetry: telemetry, overlay: overlay, settlingCohorts: settlingCohorts}) {
|
|
@@ -8588,8 +9311,7 @@ async function cascadeAutoTransitions({client: client, instanceId: instanceId, a
|
|
|
8588
9311
|
clock: clock,
|
|
8589
9312
|
executionContext: executionContext,
|
|
8590
9313
|
telemetry: telemetry
|
|
8591
|
-
})
|
|
8592
|
-
if (!(await runCascadeHop({
|
|
9314
|
+
}), hop = await runCascadeHop({
|
|
8593
9315
|
client: client,
|
|
8594
9316
|
instanceId: instanceId,
|
|
8595
9317
|
actor: actor,
|
|
@@ -8604,7 +9326,13 @@ async function cascadeAutoTransitions({client: client, instanceId: instanceId, a
|
|
|
8604
9326
|
...drained.drained ? {} : {
|
|
8605
9327
|
instance: drained.instance
|
|
8606
9328
|
}
|
|
8607
|
-
})
|
|
9329
|
+
});
|
|
9330
|
+
if (!hop.moved) return {
|
|
9331
|
+
cascaded: count,
|
|
9332
|
+
...hop.nextEvaluationAt !== void 0 ? {
|
|
9333
|
+
nextEvaluationAt: hop.nextEvaluationAt
|
|
9334
|
+
} : {}
|
|
9335
|
+
};
|
|
8608
9336
|
if (count++, count >= CASCADE_LIMIT) throw new CascadeLimitError({
|
|
8609
9337
|
instanceId: instanceId,
|
|
8610
9338
|
limit: CASCADE_LIMIT
|
|
@@ -8904,6 +9632,8 @@ async function recordOrphanedPropagation({ctx: ctx, mutation: mutation, child: c
|
|
|
8904
9632
|
function startMutation(instance) {
|
|
8905
9633
|
return {
|
|
8906
9634
|
minReaderModel: minReaderModelOf(instance),
|
|
9635
|
+
definitionSnapshot: instance.definitionSnapshot,
|
|
9636
|
+
definitionModelVersion: definitionAssignmentModel(instance),
|
|
8907
9637
|
currentStage: instance.currentStage,
|
|
8908
9638
|
fields: (instance.fields ?? []).map(s => ({
|
|
8909
9639
|
...s
|
|
@@ -8951,11 +9681,17 @@ function startMutation(instance) {
|
|
|
8951
9681
|
};
|
|
8952
9682
|
}
|
|
8953
9683
|
|
|
9684
|
+
function startRevisionRetryMutation(instance) {
|
|
9685
|
+
const mutation = startMutation(instance);
|
|
9686
|
+
return mutation.commitErrorMapper = error => isRetryableInstanceCommitConflict(error) ? new InstanceCommitRevisionConflict(error) : error,
|
|
9687
|
+
mutation;
|
|
9688
|
+
}
|
|
9689
|
+
|
|
8954
9690
|
function instanceStateFields(src) {
|
|
8955
|
-
const state = {
|
|
9691
|
+
const listModel = isAssignmentListModel(src.definitionModelVersion), state = {
|
|
8956
9692
|
currentStage: src.currentStage,
|
|
8957
|
-
fields: src.fields,
|
|
8958
|
-
stages: src.stages,
|
|
9693
|
+
fields: assignmentFieldsForWrite(src.fields, listModel),
|
|
9694
|
+
stages: assignmentStagesForWrite(src.stages, listModel),
|
|
8959
9695
|
subworkflows: src.subworkflows ?? [],
|
|
8960
9696
|
pendingEffects: src.pendingEffects,
|
|
8961
9697
|
effectHistory: src.effectHistory,
|
|
@@ -8972,7 +9708,10 @@ function instanceStateFields(src) {
|
|
|
8972
9708
|
return {
|
|
8973
9709
|
...modelStampFor({
|
|
8974
9710
|
documentType: "instance",
|
|
8975
|
-
document:
|
|
9711
|
+
document: {
|
|
9712
|
+
...state,
|
|
9713
|
+
definitionSnapshot: src.definitionSnapshot
|
|
9714
|
+
},
|
|
8976
9715
|
storedMinReaderModel: src.minReaderModel
|
|
8977
9716
|
}),
|
|
8978
9717
|
...state
|
|
@@ -9007,11 +9746,11 @@ async function persist(ctx, mutation) {
|
|
|
9007
9746
|
...instanceStateFields(mutation),
|
|
9008
9747
|
lastChangedAt: ctx.now
|
|
9009
9748
|
}, pendingCreates = mutation.pendingCreates;
|
|
9010
|
-
if (pendingCreates.length === 0) return ctx.client.patch(ctx.instance._id).set(set).ifRevisionId(ctx.instance._rev).commit(SYNC_COMMIT);
|
|
9749
|
+
if (pendingCreates.length === 0) return commitInstanceWrite(() => ctx.client.patch(ctx.instance._id).set(set).ifRevisionId(ctx.instance._rev).commit(SYNC_COMMIT), mutation.commitErrorMapper);
|
|
9011
9750
|
const tx = ctx.client.transaction();
|
|
9012
|
-
for (const {body: body} of pendingCreates) tx.create(body);
|
|
9751
|
+
for (const {body: body} of pendingCreates) tx.create(instanceDocumentForWrite(body));
|
|
9013
9752
|
tx.patch(ctx.client.patch(ctx.instance._id).set(set).ifRevisionId(ctx.instance._rev)),
|
|
9014
|
-
await tx.commit(), mutation.pendingCreates = [];
|
|
9753
|
+
await commitInstanceWrite(() => tx.commit(), mutation.commitErrorMapper), mutation.pendingCreates = [];
|
|
9015
9754
|
const actorForPriming = ctx.actor, settlingCohorts = new Set(ctx.settlingCohorts).add(ctx.instance._id);
|
|
9016
9755
|
for (const {body: body} of pendingCreates) try {
|
|
9017
9756
|
const stepArgs = spawnStepArgs(ctx, {
|
|
@@ -9038,6 +9777,14 @@ async function persist(ctx, mutation) {
|
|
|
9038
9777
|
return reloaded;
|
|
9039
9778
|
}
|
|
9040
9779
|
|
|
9780
|
+
async function commitInstanceWrite(write, mapError) {
|
|
9781
|
+
try {
|
|
9782
|
+
return await write();
|
|
9783
|
+
} catch (error) {
|
|
9784
|
+
throw mapError?.(error) ?? error;
|
|
9785
|
+
}
|
|
9786
|
+
}
|
|
9787
|
+
|
|
9041
9788
|
async function settleSpawnBatch(ctx, args) {
|
|
9042
9789
|
try {
|
|
9043
9790
|
await propagateSpawnBatch({
|
|
@@ -9300,7 +10047,7 @@ async function commitCompleteEffect({ctx: ctx, effectKey: effectKey, status: sta
|
|
|
9300
10047
|
outputs: outputs,
|
|
9301
10048
|
memberRoles: ctx.memberRoles,
|
|
9302
10049
|
roleAliases: ctx.definition.roleAliases
|
|
9303
|
-
}), mutation =
|
|
10050
|
+
}), mutation = startRevisionRetryMutation(ctx.instance);
|
|
9304
10051
|
recordProcessedRequest({
|
|
9305
10052
|
mutation: mutation,
|
|
9306
10053
|
record: requestRecord,
|
|
@@ -9447,7 +10194,7 @@ function staleClaimReason(args) {
|
|
|
9447
10194
|
if (isClaimExpired(claim, args.now)) return "lease-expired";
|
|
9448
10195
|
}
|
|
9449
10196
|
|
|
9450
|
-
function renewClaimLease(args) {
|
|
10197
|
+
function renewClaimLease$1(args) {
|
|
9451
10198
|
const {mutation: mutation, effectKey: effectKey, now: now, leaseMs: leaseMs} = args;
|
|
9452
10199
|
mutation.pendingEffects = mutation.pendingEffects.map(entry => entry._key === effectKey && entry.claim !== void 0 ? {
|
|
9453
10200
|
...entry,
|
|
@@ -9479,12 +10226,12 @@ async function commitReport({ctx: ctx, effectKey: effectKey, claimToken: claimTo
|
|
|
9479
10226
|
effect: pending.name,
|
|
9480
10227
|
issues: [ "a mid-dispatch report must carry at least one field op — there is nothing to commit" ]
|
|
9481
10228
|
});
|
|
9482
|
-
const mutation =
|
|
10229
|
+
const mutation = startRevisionRetryMutation(ctx.instance);
|
|
9483
10230
|
return recordProcessedRequest({
|
|
9484
10231
|
mutation: mutation,
|
|
9485
10232
|
record: requestRecord,
|
|
9486
10233
|
now: ctx.now
|
|
9487
|
-
}), renewClaimLease({
|
|
10234
|
+
}), renewClaimLease$1({
|
|
9488
10235
|
mutation: mutation,
|
|
9489
10236
|
effectKey: effectKey,
|
|
9490
10237
|
now: ctx.now,
|
|
@@ -9515,7 +10262,7 @@ async function commitReport({ctx: ctx, effectKey: effectKey, claimToken: claimTo
|
|
|
9515
10262
|
};
|
|
9516
10263
|
}
|
|
9517
10264
|
|
|
9518
|
-
const RESET_ACTIVITY_TARGETS = [ "active", "skipped" ];
|
|
10265
|
+
const RESET_ACTIVITY_TARGETS = _exhaustiveOptions()([ "active", "skipped" ]);
|
|
9519
10266
|
|
|
9520
10267
|
function isResetActivityTarget(value) {
|
|
9521
10268
|
return RESET_ACTIVITY_TARGETS.includes(value);
|
|
@@ -9573,6 +10320,100 @@ async function commitResetActivity({ctx: ctx, activity: activity, to: to, reques
|
|
|
9573
10320
|
};
|
|
9574
10321
|
}
|
|
9575
10322
|
|
|
10323
|
+
const AvailabilityResponse = v.object({
|
|
10324
|
+
documents: v.array(v.object({
|
|
10325
|
+
_id: v.string()
|
|
10326
|
+
})),
|
|
10327
|
+
omitted: v.array(v.object({
|
|
10328
|
+
id: v.string(),
|
|
10329
|
+
reason: v.picklist([ "existence", "permission" ])
|
|
10330
|
+
}))
|
|
10331
|
+
}), AVAILABILITY_BATCH_SIZE = 100, AVAILABILITY_CONCURRENCY = 4, pendingReads = /* @__PURE__ */ new WeakMap;
|
|
10332
|
+
|
|
10333
|
+
function groupsFor(documents, clientForGdr) {
|
|
10334
|
+
const groups = /* @__PURE__ */ new Map;
|
|
10335
|
+
for (const {reference: reference} of documents) {
|
|
10336
|
+
const parsed = parseGdr(reference.id), resource = resourceFromParsed(parsed), key = resourceGdr(resource), group = groups.get(key) ?? {
|
|
10337
|
+
client: clientForGdr(parsed),
|
|
10338
|
+
resource: resource,
|
|
10339
|
+
documents: /* @__PURE__ */ new Map
|
|
10340
|
+
};
|
|
10341
|
+
group.documents.set(reference.id, parsed.documentId), groups.set(key, group);
|
|
10342
|
+
}
|
|
10343
|
+
return [ ...groups.values() ];
|
|
10344
|
+
}
|
|
10345
|
+
|
|
10346
|
+
async function readGroup(group) {
|
|
10347
|
+
let response;
|
|
10348
|
+
try {
|
|
10349
|
+
const url = documentUrl(group);
|
|
10350
|
+
url !== void 0 && group.client.request !== void 0 ? response = await readResponse(group.client, url) : logUnavailable({
|
|
10351
|
+
resource: group.resource,
|
|
10352
|
+
reason: "unsupported-client"
|
|
10353
|
+
});
|
|
10354
|
+
} catch (error) {
|
|
10355
|
+
logUnavailable({
|
|
10356
|
+
resource: group.resource,
|
|
10357
|
+
reason: v.isValiError(error) ? "invalid-response" : "request-failed",
|
|
10358
|
+
error: error
|
|
10359
|
+
});
|
|
10360
|
+
}
|
|
10361
|
+
return [ ...group.documents ].map(([uri, id]) => [ uri, availabilityOf(response, id) ]);
|
|
10362
|
+
}
|
|
10363
|
+
|
|
10364
|
+
function logUnavailable({resource: resource, reason: reason, error: error}) {
|
|
10365
|
+
const statusCode = typeof error == "object" && error !== null && "statusCode" in error ? error.statusCode : void 0;
|
|
10366
|
+
console.warn("[workflows] Document availability could not be confirmed.", {
|
|
10367
|
+
resource: resourceGdr(resource),
|
|
10368
|
+
reason: reason,
|
|
10369
|
+
...typeof statusCode == "number" ? {
|
|
10370
|
+
statusCode: statusCode
|
|
10371
|
+
} : {}
|
|
10372
|
+
});
|
|
10373
|
+
}
|
|
10374
|
+
|
|
10375
|
+
function availabilityOf(response, id) {
|
|
10376
|
+
if (response === void 0) return "unreadable";
|
|
10377
|
+
const omissions = response.omitted.filter(entry => getPublishedId(DocumentId(entry.id)) === id);
|
|
10378
|
+
return omissions.some(entry => entry.reason === "permission") ? "unreadable" : response.documents.some(doc => getPublishedId(DocumentId(doc._id)) === id) ? "outside-perspective" : omissions.some(entry => entry.id === id && entry.reason === "existence") ? "deleted" : "unreadable";
|
|
10379
|
+
}
|
|
10380
|
+
|
|
10381
|
+
function documentUrl(group) {
|
|
10382
|
+
const ids = [ ...new Set(group.documents.values()) ].sort().map(encodeURIComponent).join(",");
|
|
10383
|
+
let url;
|
|
10384
|
+
if ("getDataUrl" in group.client && group.client.getDataUrl !== void 0) url = group.client.getDataUrl("doc", ids); else {
|
|
10385
|
+
if (group.resource.type !== "dataset") return;
|
|
10386
|
+
const {dataset: dataset} = datasetResourceParts(group.resource.id);
|
|
10387
|
+
url = `/data/doc/${dataset}/${ids}`;
|
|
10388
|
+
}
|
|
10389
|
+
return `${url}?includeAllVersions=true`;
|
|
10390
|
+
}
|
|
10391
|
+
|
|
10392
|
+
function readResponse(client, url) {
|
|
10393
|
+
const identity = unwrapRequestTag(client), pending = pendingReads.get(identity) ?? /* @__PURE__ */ new Map;
|
|
10394
|
+
pendingReads.set(identity, pending);
|
|
10395
|
+
const existing = pending.get(url);
|
|
10396
|
+
if (existing !== void 0) return existing;
|
|
10397
|
+
const result = client.request({
|
|
10398
|
+
url: url
|
|
10399
|
+
}).then(response => v.parse(AvailabilityResponse, response)).finally(() => pending.delete(url));
|
|
10400
|
+
return pending.set(url, result), result;
|
|
10401
|
+
}
|
|
10402
|
+
|
|
10403
|
+
function documentAvailabilityReader(args) {
|
|
10404
|
+
return async documents => {
|
|
10405
|
+
const result = /* @__PURE__ */ new Map, groups = groupsFor(documents, args.clientForGdr).flatMap(group => chunks([ ...group.documents ], AVAILABILITY_BATCH_SIZE).map(entries => ({
|
|
10406
|
+
...group,
|
|
10407
|
+
documents: new Map(entries)
|
|
10408
|
+
})));
|
|
10409
|
+
for (const wave of chunks(groups, AVAILABILITY_CONCURRENCY)) {
|
|
10410
|
+
const rows = await Promise.all(wave.map(readGroup));
|
|
10411
|
+
for (const [uri, availability] of rows.flat()) result.set(uri, availability);
|
|
10412
|
+
}
|
|
10413
|
+
return result;
|
|
10414
|
+
};
|
|
10415
|
+
}
|
|
10416
|
+
|
|
9576
10417
|
const REPLAY_SURFACE = {
|
|
9577
10418
|
allows: () => !0,
|
|
9578
10419
|
description: "any resource (in-memory replay; the committing surface enforces)"
|
|
@@ -9627,6 +10468,10 @@ function replayable(args) {
|
|
|
9627
10468
|
return (stage.activities ?? []).every(declared => primed.has(declared.name)) ? !(stage.activities ?? []).flatMap(declared => declared.actions ?? []).some(sibling => isCascadeFired(sibling) && sibling.spawn !== void 0) : !1;
|
|
9628
10469
|
}
|
|
9629
10470
|
|
|
10471
|
+
function clientGuardDereference(client) {
|
|
10472
|
+
return async ({_ref: _ref}) => await client.getDocument(toBareId(_ref)) ?? null;
|
|
10473
|
+
}
|
|
10474
|
+
|
|
9630
10475
|
async function buildFieldInsights({sites: sites, snapshot: snapshot}) {
|
|
9631
10476
|
const insights = [];
|
|
9632
10477
|
for (const field of fieldsReadAcross(sites)) {
|
|
@@ -9720,6 +10565,80 @@ function fabricatable(assignment) {
|
|
|
9720
10565
|
return assignment.target.path.every(segment => typeof segment != "number" || segment <= MAX_COUNTERFACTUAL_INDEX);
|
|
9721
10566
|
}
|
|
9722
10567
|
|
|
10568
|
+
function missingDocumentsOf({instance: instance, snapshot: snapshot, definition: definition}) {
|
|
10569
|
+
const stage = findStageNode({
|
|
10570
|
+
definition: definition,
|
|
10571
|
+
stageName: instance.currentStage
|
|
10572
|
+
});
|
|
10573
|
+
return activeFieldEntries(instance).flatMap(({target: target, entry: entry}) => {
|
|
10574
|
+
const declarations = {
|
|
10575
|
+
workflow: definition?.fields,
|
|
10576
|
+
stage: stage?.fields,
|
|
10577
|
+
activity: stage?.activities?.find(activity => activity.name === target.activity)?.fields
|
|
10578
|
+
}, title = entry.title ?? declarations[target.scope ?? "workflow"]?.find(field => field.name === target.field)?.title;
|
|
10579
|
+
return missingEntryDocRefs([ entry ], snapshot.knownIds).map(reference => ({
|
|
10580
|
+
target: target,
|
|
10581
|
+
reference: reference,
|
|
10582
|
+
...title !== void 0 ? {
|
|
10583
|
+
title: title
|
|
10584
|
+
} : {}
|
|
10585
|
+
}));
|
|
10586
|
+
});
|
|
10587
|
+
}
|
|
10588
|
+
|
|
10589
|
+
function blockingConditions(evaluation) {
|
|
10590
|
+
const transitions = evaluation.currentStage.transitions.some(entry => entry.whenSatisfied) ? [] : evaluation.currentStage.transitions;
|
|
10591
|
+
return [ ...evaluation.currentStage.activities.filter(entry => entry.status === "active" && (entry.activity.actions ?? []).length > 0).flatMap(entry => (entry.unmetRequirements ?? []).map(requirement => ({
|
|
10592
|
+
insight: entry.requirementInsights?.[requirement.name],
|
|
10593
|
+
activity: entry.activity.name
|
|
10594
|
+
}))), ...transitions.map(entry => ({
|
|
10595
|
+
insight: entry.insight
|
|
10596
|
+
})) ];
|
|
10597
|
+
}
|
|
10598
|
+
|
|
10599
|
+
function readsDocumentContent({entry: entry, read: read, atom: atom}) {
|
|
10600
|
+
return atomReadsDataset(atom) ? !0 : isSingleDocRefKind(entry._type) && read.path.length > 1 && read.path[1] !== "_id" && read.path[1] !== "_type";
|
|
10601
|
+
}
|
|
10602
|
+
|
|
10603
|
+
function contentTargets({atoms: atoms, fields: fields, activity: activity}) {
|
|
10604
|
+
return atoms.flatMap(atom => atom.reads.flatMap(read => {
|
|
10605
|
+
if (read.variable !== "fields") return [];
|
|
10606
|
+
const field = fields.findLast(({entry: entry, target: target}) => entry.name === read.path[0] && (target.scope !== "activity" || target.activity === activity));
|
|
10607
|
+
return field !== void 0 && readsDocumentContent({
|
|
10608
|
+
entry: field.entry,
|
|
10609
|
+
read: read,
|
|
10610
|
+
atom: atom
|
|
10611
|
+
}) ? [ _fieldTargetLabel(field.target) ] : [];
|
|
10612
|
+
}));
|
|
10613
|
+
}
|
|
10614
|
+
|
|
10615
|
+
async function blockingAtoms(condition, args) {
|
|
10616
|
+
const atoms = condition.insight?.blockedBy.map(({atom: atom}) => atom) ?? [], names = [ ...new Set(atoms.flatMap(atom => atom.reads).filter(read => read.path.length === 0 && args.definition.predicates?.[read.variable] !== void 0).map(read => read.variable)) ];
|
|
10617
|
+
if (names.length === 0) return atoms;
|
|
10618
|
+
const site = args.sites.find(entry => entry.insight === condition.insight);
|
|
10619
|
+
if (site === void 0) throw new Error("Missing evaluated scope for a blocking condition");
|
|
10620
|
+
const dependencies = await Promise.all(names.map(async name => {
|
|
10621
|
+
const predicate = args.definition.predicates?.[name];
|
|
10622
|
+
if (predicate === void 0) throw new Error(`Missing predicate "${name}"`);
|
|
10623
|
+
return (await explainCondition({
|
|
10624
|
+
condition: site.params[name] === !0 ? `!(${predicate})` : predicate,
|
|
10625
|
+
dataset: args.snapshot.docs,
|
|
10626
|
+
params: site.params
|
|
10627
|
+
})).blockedBy.map(({atom: atom}) => atom);
|
|
10628
|
+
}));
|
|
10629
|
+
return [ ...atoms, ...dependencies.flat() ];
|
|
10630
|
+
}
|
|
10631
|
+
|
|
10632
|
+
async function blockingMissingDocuments(args) {
|
|
10633
|
+
if (!args.missingDocuments?.length) return [];
|
|
10634
|
+
const fields = activeFieldEntries(args.instance), targets = (await Promise.all(blockingConditions(args).map(async condition => contentTargets({
|
|
10635
|
+
atoms: await blockingAtoms(condition, args),
|
|
10636
|
+
fields: fields,
|
|
10637
|
+
activity: condition.activity
|
|
10638
|
+
})))).flat(), blocked = /* @__PURE__ */ new Set([ ...targets, ...missingRequiredDocuments(args).map(({target: target}) => _fieldTargetLabel(target)) ]);
|
|
10639
|
+
return args.missingDocuments.filter(({target: target}) => blocked.has(_fieldTargetLabel(target)));
|
|
10640
|
+
}
|
|
10641
|
+
|
|
9723
10642
|
async function subjectResourceGrants(args) {
|
|
9724
10643
|
const {clientForGdr: clientForGdr, instance: instance} = args, clients = /* @__PURE__ */ new Map;
|
|
9725
10644
|
for (const {parsed: parsed, resource: resource} of foreignSubjectRefs(instance)) {
|
|
@@ -9782,6 +10701,10 @@ async function evaluateInstance(args) {
|
|
|
9782
10701
|
guards: guards,
|
|
9783
10702
|
now: now,
|
|
9784
10703
|
resourceGrants: resourceGrants,
|
|
10704
|
+
documentAvailability: documentAvailabilityReader({
|
|
10705
|
+
clientForGdr: clientForGdr
|
|
10706
|
+
}),
|
|
10707
|
+
guardDereference: clientGuardDereference(client),
|
|
9785
10708
|
...localPrincipalId !== void 0 ? {
|
|
9786
10709
|
localPrincipalId: localPrincipalId
|
|
9787
10710
|
} : {},
|
|
@@ -9794,16 +10717,6 @@ async function evaluateInstance(args) {
|
|
|
9794
10717
|
});
|
|
9795
10718
|
}
|
|
9796
10719
|
|
|
9797
|
-
function memoizedByName(render) {
|
|
9798
|
-
const rendered = /* @__PURE__ */ new Map;
|
|
9799
|
-
return activityName => {
|
|
9800
|
-
const hit = rendered.get(activityName);
|
|
9801
|
-
if (hit !== void 0) return hit;
|
|
9802
|
-
const scope = render(activityName);
|
|
9803
|
-
return rendered.set(activityName, scope), scope;
|
|
9804
|
-
};
|
|
9805
|
-
}
|
|
9806
|
-
|
|
9807
10720
|
async function callerBoundProjectionScopes(args) {
|
|
9808
10721
|
const can = await advisoryCan({
|
|
9809
10722
|
instance: args.instance,
|
|
@@ -9824,13 +10737,17 @@ async function callerBoundProjectionScopes(args) {
|
|
|
9824
10737
|
};
|
|
9825
10738
|
return {
|
|
9826
10739
|
scope: await renderConditionScope(args.scopeSource, opts),
|
|
9827
|
-
scopeForActivity:
|
|
10740
|
+
scopeForActivity: memoizedBy(activityName => renderConditionScope(args.scopeSource, {
|
|
9828
10741
|
activityName: activityName,
|
|
9829
10742
|
...opts
|
|
9830
10743
|
}))
|
|
9831
10744
|
};
|
|
9832
10745
|
}
|
|
9833
10746
|
|
|
10747
|
+
function clockSitesOf(instance, sites) {
|
|
10748
|
+
return terminalState(instance) !== "in-flight" ? [] : sites.filter(entry => readsNowIn(entry.insight.analysis.reads));
|
|
10749
|
+
}
|
|
10750
|
+
|
|
9834
10751
|
function currentStageOf(instance, definition) {
|
|
9835
10752
|
try {
|
|
9836
10753
|
return findStage(definition, instance.currentStage);
|
|
@@ -9880,12 +10797,13 @@ async function evaluateFromSnapshot(args) {
|
|
|
9880
10797
|
grants: grants,
|
|
9881
10798
|
attributes: attributes,
|
|
9882
10799
|
identity: anchorIdentity
|
|
9883
|
-
}), cascadeScopeForActivity =
|
|
10800
|
+
}), cascadeScopeForActivity = memoizedBy(activityName => renderConditionScope(scopeSource, {
|
|
9884
10801
|
activityName: activityName
|
|
9885
10802
|
})), currentActivityEntries = findOpenStageEntry(instance)?.activities ?? [], guardDenial = await instanceGuardReason({
|
|
9886
10803
|
instance: instance,
|
|
9887
10804
|
identity: anchorIdentity,
|
|
9888
|
-
guards: args.guards
|
|
10805
|
+
guards: args.guards,
|
|
10806
|
+
dereference: args.guardDereference
|
|
9889
10807
|
}), subjectDenials = await forecastSubjectDenials({
|
|
9890
10808
|
instance: instance,
|
|
9891
10809
|
snapshot: snapshot,
|
|
@@ -9893,7 +10811,11 @@ async function evaluateFromSnapshot(args) {
|
|
|
9893
10811
|
}), subjectPermissionReason = subjectDenials.length > 0 ? {
|
|
9894
10812
|
kind: "subject-permission-denied",
|
|
9895
10813
|
denied: subjectDenials
|
|
9896
|
-
} : void 0, sites = [], activityEvaluations = []
|
|
10814
|
+
} : void 0, sites = [], activityEvaluations = [], requiredDocuments = requiredDocumentRequirements({
|
|
10815
|
+
instance: instance,
|
|
10816
|
+
definition: definition,
|
|
10817
|
+
snapshot: snapshot
|
|
10818
|
+
});
|
|
9897
10819
|
for (const activity of stage.activities ?? []) activityEvaluations.push(await evaluateActivity({
|
|
9898
10820
|
activity: activity,
|
|
9899
10821
|
statusEntry: currentActivityEntries.find(t => t.name === activity.name),
|
|
@@ -9904,36 +10826,23 @@ async function evaluateFromSnapshot(args) {
|
|
|
9904
10826
|
stageHasExits: !isTerminalStage(stage),
|
|
9905
10827
|
guardDenial: guardDenial,
|
|
9906
10828
|
subjectPermissionReason: subjectPermissionReason,
|
|
10829
|
+
requiredDocuments: requiredDocuments,
|
|
9907
10830
|
sites: sites,
|
|
9908
10831
|
autonomy: activityAutonomyOf(stageAutonomy2, activity.name),
|
|
9909
10832
|
fireConsequence: fireConsequence
|
|
9910
10833
|
}));
|
|
9911
|
-
const cascadeParams = await renderConditionScope(scopeSource), transitionEvaluations =
|
|
9912
|
-
|
|
9913
|
-
|
|
9914
|
-
|
|
9915
|
-
|
|
9916
|
-
|
|
9917
|
-
},
|
|
9918
|
-
condition: transition.when,
|
|
9919
|
-
params: cascadeParams,
|
|
9920
|
-
snapshot: snapshot,
|
|
9921
|
-
sites: sites
|
|
9922
|
-
});
|
|
9923
|
-
transitionEvaluations.push({
|
|
9924
|
-
transition: transition,
|
|
9925
|
-
whenSatisfied: insight.outcome === "satisfied",
|
|
9926
|
-
unevaluable: insight.outcome === "unevaluable",
|
|
9927
|
-
insight: insight
|
|
9928
|
-
});
|
|
9929
|
-
}
|
|
9930
|
-
const currentStage = {
|
|
10834
|
+
const cascadeParams = await renderConditionScope(scopeSource), transitionEvaluations = await evaluateTransitions({
|
|
10835
|
+
stage: stage,
|
|
10836
|
+
cascadeParams: cascadeParams,
|
|
10837
|
+
snapshot: snapshot,
|
|
10838
|
+
sites: sites
|
|
10839
|
+
}), currentStage = {
|
|
9931
10840
|
stage: stage,
|
|
9932
10841
|
...semanticsOf(stage),
|
|
9933
10842
|
activities: activityEvaluations,
|
|
9934
10843
|
transitions: transitionEvaluations,
|
|
9935
10844
|
autonomy: stageAutonomy2
|
|
9936
|
-
}, pendingOnYou = activityEvaluations.filter(t => t.pendingOnActor), canInteract = activityEvaluations.some(t => t.actions.some(a => a.allowed)), editGuardDenial = guardDenial?.kind === "mutation-guard-denied" ? guardDenial : void 0, editableFields = await evaluateEditableFields({
|
|
10845
|
+
}, missingDocuments = await readMissingDocuments(args), pendingOnYou = activityEvaluations.filter(t => t.pendingOnActor), canInteract = activityEvaluations.some(t => t.actions.some(a => a.allowed)), editGuardDenial = guardDenial?.kind === "mutation-guard-denied" ? guardDenial : void 0, editableFields = await evaluateEditableFields({
|
|
9937
10846
|
instance: instance,
|
|
9938
10847
|
definition: definition,
|
|
9939
10848
|
stage: stage,
|
|
@@ -9942,6 +10851,10 @@ async function evaluateFromSnapshot(args) {
|
|
|
9942
10851
|
scopeForActivity: scopeForActivity,
|
|
9943
10852
|
guardDenial: editGuardDenial,
|
|
9944
10853
|
sites: sites
|
|
10854
|
+
}), nextEvaluationAt = await nextEvaluationInstant({
|
|
10855
|
+
sites: clockSitesOf(instance, sites),
|
|
10856
|
+
dataset: snapshot.docs,
|
|
10857
|
+
now: now
|
|
9945
10858
|
});
|
|
9946
10859
|
return {
|
|
9947
10860
|
instance: instance,
|
|
@@ -9956,10 +10869,60 @@ async function evaluateFromSnapshot(args) {
|
|
|
9956
10869
|
sites: sites,
|
|
9957
10870
|
snapshot: snapshot
|
|
9958
10871
|
}),
|
|
9959
|
-
|
|
10872
|
+
...missingDocuments.length > 0 ? {
|
|
10873
|
+
missingDocuments: missingDocuments,
|
|
10874
|
+
blockingMissingDocuments: await blockingMissingDocuments({
|
|
10875
|
+
instance: instance,
|
|
10876
|
+
definition: definition,
|
|
10877
|
+
currentStage: currentStage,
|
|
10878
|
+
missingDocuments: missingDocuments,
|
|
10879
|
+
snapshot: snapshot,
|
|
10880
|
+
sites: sites
|
|
10881
|
+
})
|
|
10882
|
+
} : {},
|
|
10883
|
+
autonomy: autonomy,
|
|
10884
|
+
...nextEvaluationAt !== void 0 ? {
|
|
10885
|
+
nextEvaluationAt: nextEvaluationAt
|
|
10886
|
+
} : {}
|
|
9960
10887
|
};
|
|
9961
10888
|
}
|
|
9962
10889
|
|
|
10890
|
+
async function readMissingDocuments(args) {
|
|
10891
|
+
const missingDocuments = missingDocumentsOf(args);
|
|
10892
|
+
if (args.documentAvailability === void 0 || missingDocuments.length === 0) return missingDocuments;
|
|
10893
|
+
const availability = await args.documentAvailability(missingDocuments);
|
|
10894
|
+
return missingDocuments.map(document => {
|
|
10895
|
+
const verdict = availability.get(document.reference.id);
|
|
10896
|
+
return verdict === void 0 ? document : {
|
|
10897
|
+
...document,
|
|
10898
|
+
availability: verdict
|
|
10899
|
+
};
|
|
10900
|
+
});
|
|
10901
|
+
}
|
|
10902
|
+
|
|
10903
|
+
async function evaluateTransitions(args) {
|
|
10904
|
+
const {stage: stage, cascadeParams: cascadeParams, snapshot: snapshot, sites: sites} = args, evaluations = [];
|
|
10905
|
+
for (const transition of stage.transitions ?? []) {
|
|
10906
|
+
const insight = await explainSite({
|
|
10907
|
+
site: {
|
|
10908
|
+
kind: "transition",
|
|
10909
|
+
transition: transition.name
|
|
10910
|
+
},
|
|
10911
|
+
condition: transition.when,
|
|
10912
|
+
params: cascadeParams,
|
|
10913
|
+
snapshot: snapshot,
|
|
10914
|
+
sites: sites
|
|
10915
|
+
});
|
|
10916
|
+
evaluations.push({
|
|
10917
|
+
transition: transition,
|
|
10918
|
+
whenSatisfied: insight.outcome === "satisfied",
|
|
10919
|
+
unevaluable: insight.outcome === "unevaluable",
|
|
10920
|
+
insight: insight
|
|
10921
|
+
});
|
|
10922
|
+
}
|
|
10923
|
+
return evaluations;
|
|
10924
|
+
}
|
|
10925
|
+
|
|
9963
10926
|
const AUTONOMY_CACHE = /* @__PURE__ */ new WeakMap;
|
|
9964
10927
|
|
|
9965
10928
|
function autonomyOf(definition) {
|
|
@@ -9970,54 +10933,56 @@ function autonomyOf(definition) {
|
|
|
9970
10933
|
}
|
|
9971
10934
|
|
|
9972
10935
|
async function evaluateEditableFields(args) {
|
|
9973
|
-
const
|
|
9974
|
-
for (const site of editableFieldsInStage(definition, stage))
|
|
9975
|
-
const window = fieldWindowOpen(instance, site), insight = await editPredicateInsight({
|
|
9976
|
-
site: site,
|
|
9977
|
-
snapshot: snapshot,
|
|
9978
|
-
scope: scope,
|
|
9979
|
-
scopeForActivity: scopeForActivity,
|
|
9980
|
-
sites: sites
|
|
9981
|
-
}), predicateSatisfied = insight === void 0 || insight.outcome === "satisfied", reason = editDisabledReason({
|
|
9982
|
-
effective: site.effective,
|
|
9983
|
-
instance: instance,
|
|
9984
|
-
window: window,
|
|
9985
|
-
guardDenial: guardDenial,
|
|
9986
|
-
predicateSatisfied: predicateSatisfied
|
|
9987
|
-
}), value = readFieldValue(instance, site);
|
|
9988
|
-
fields.push({
|
|
9989
|
-
scope: site.scope,
|
|
9990
|
-
...site.activity !== void 0 ? {
|
|
9991
|
-
activity: site.activity
|
|
9992
|
-
} : {},
|
|
9993
|
-
name: site.name,
|
|
9994
|
-
type: site.type,
|
|
9995
|
-
...site.title !== void 0 ? {
|
|
9996
|
-
title: site.title
|
|
9997
|
-
} : {},
|
|
9998
|
-
...site.validation !== void 0 ? {
|
|
9999
|
-
validation: site.validation
|
|
10000
|
-
} : {},
|
|
10001
|
-
...site.roles !== void 0 ? {
|
|
10002
|
-
roles: site.roles,
|
|
10003
|
-
...definition.roleAliases !== void 0 ? {
|
|
10004
|
-
roleAliases: definition.roleAliases
|
|
10005
|
-
} : {}
|
|
10006
|
-
} : {},
|
|
10007
|
-
value: value,
|
|
10008
|
-
editable: reason === void 0,
|
|
10009
|
-
...reason !== void 0 ? {
|
|
10010
|
-
disabledReason: reason
|
|
10011
|
-
} : {},
|
|
10012
|
-
...fieldProvenance(instance, site.ref),
|
|
10013
|
-
...insight !== void 0 ? {
|
|
10014
|
-
insight: insight
|
|
10015
|
-
} : {}
|
|
10016
|
-
});
|
|
10017
|
-
}
|
|
10936
|
+
const fields = [];
|
|
10937
|
+
for (const site of editableFieldsInStage(args.definition, args.stage)) fields.push(await evaluateEditableField(args, site));
|
|
10018
10938
|
return fields;
|
|
10019
10939
|
}
|
|
10020
10940
|
|
|
10941
|
+
async function evaluateEditableField(args, site) {
|
|
10942
|
+
const {instance: instance, definition: definition, snapshot: snapshot, scope: scope, scopeForActivity: scopeForActivity, guardDenial: guardDenial, sites: sites} = args, window = fieldWindowOpen(instance, site), insight = await editPredicateInsight({
|
|
10943
|
+
site: site,
|
|
10944
|
+
snapshot: snapshot,
|
|
10945
|
+
scope: scope,
|
|
10946
|
+
scopeForActivity: scopeForActivity,
|
|
10947
|
+
sites: sites
|
|
10948
|
+
}), predicateSatisfied = insight === void 0 || insight.outcome === "satisfied", reason = editDisabledReason({
|
|
10949
|
+
effective: site.effective,
|
|
10950
|
+
instance: instance,
|
|
10951
|
+
window: window,
|
|
10952
|
+
guardDenial: guardDenial,
|
|
10953
|
+
predicateSatisfied: predicateSatisfied
|
|
10954
|
+
});
|
|
10955
|
+
return {
|
|
10956
|
+
scope: site.scope,
|
|
10957
|
+
...site.activity !== void 0 ? {
|
|
10958
|
+
activity: site.activity
|
|
10959
|
+
} : {},
|
|
10960
|
+
name: site.name,
|
|
10961
|
+
type: site.type,
|
|
10962
|
+
...site.title !== void 0 ? {
|
|
10963
|
+
title: site.title
|
|
10964
|
+
} : {},
|
|
10965
|
+
...site.validation !== void 0 ? {
|
|
10966
|
+
validation: site.validation
|
|
10967
|
+
} : {},
|
|
10968
|
+
...site.roles !== void 0 ? {
|
|
10969
|
+
roles: site.roles,
|
|
10970
|
+
...definition.roleAliases !== void 0 ? {
|
|
10971
|
+
roleAliases: definition.roleAliases
|
|
10972
|
+
} : {}
|
|
10973
|
+
} : {},
|
|
10974
|
+
value: readFieldValue(instance, site),
|
|
10975
|
+
editable: reason === void 0,
|
|
10976
|
+
...reason !== void 0 ? {
|
|
10977
|
+
disabledReason: reason
|
|
10978
|
+
} : {},
|
|
10979
|
+
...fieldProvenance(instance, site.ref),
|
|
10980
|
+
...insight !== void 0 ? {
|
|
10981
|
+
insight: insight
|
|
10982
|
+
} : {}
|
|
10983
|
+
};
|
|
10984
|
+
}
|
|
10985
|
+
|
|
10021
10986
|
async function editPredicateInsight(args) {
|
|
10022
10987
|
const {site: site, snapshot: snapshot, scope: scope, scopeForActivity: scopeForActivity, sites: sites} = args;
|
|
10023
10988
|
if (typeof site.effective != "string") return;
|
|
@@ -10041,14 +11006,10 @@ async function editPredicateInsight(args) {
|
|
|
10041
11006
|
async function forecastSubjectDenials(args) {
|
|
10042
11007
|
const {instance: instance, snapshot: snapshot, resourceGrants: resourceGrants} = args;
|
|
10043
11008
|
if (resourceGrants === void 0 || resourceGrants.size === 0) return [];
|
|
10044
|
-
const denials = [],
|
|
10045
|
-
for (const {ref: ref, parsed: parsed, resource: resource} of
|
|
10046
|
-
|
|
10047
|
-
|
|
10048
|
-
const access = resourceGrants.get(resourceGdr(resource));
|
|
10049
|
-
if (access === void 0) continue;
|
|
10050
|
-
const doc = snapshot.docs.find(d => d._id === ref.id);
|
|
10051
|
-
doc !== void 0 && await subjectUpdateAllowed({
|
|
11009
|
+
const denials = [], subjects = new Map(foreignSubjectRefs(instance).map(subject => [ subject.ref.id, subject ]));
|
|
11010
|
+
for (const {ref: ref, parsed: parsed, resource: resource} of subjects.values()) {
|
|
11011
|
+
const access = resourceGrants.get(resourceGdr(resource)), doc = snapshot.docs.find(d => d._id === ref.id);
|
|
11012
|
+
access === void 0 || doc === void 0 || await subjectUpdateAllowed({
|
|
10052
11013
|
doc: doc,
|
|
10053
11014
|
parsed: parsed,
|
|
10054
11015
|
grants: access.grants,
|
|
@@ -10081,13 +11042,13 @@ async function subjectUpdateAllowed(args) {
|
|
|
10081
11042
|
}
|
|
10082
11043
|
|
|
10083
11044
|
async function evaluateActivity(args) {
|
|
10084
|
-
const {activity: activity, statusEntry: statusEntry, instance: instance, snapshot: snapshot, activityScope: activityScope, cascadeActivityScope: cascadeActivityScope, stageHasExits: stageHasExits, guardDenial: guardDenial, subjectPermissionReason: subjectPermissionReason, sites: sites, autonomy: autonomy, fireConsequence: fireConsequence} = args, status = statusEntry?.status ?? "skipped", assigned = activityScope.assigned === !0, {unmetRequirements:
|
|
11045
|
+
const {activity: activity, statusEntry: statusEntry, instance: instance, snapshot: snapshot, activityScope: activityScope, cascadeActivityScope: cascadeActivityScope, stageHasExits: stageHasExits, guardDenial: guardDenial, subjectPermissionReason: subjectPermissionReason, requiredDocuments: requiredDocuments, sites: sites, autonomy: autonomy, fireConsequence: fireConsequence} = args, status = statusEntry?.status ?? "skipped", assigned = activityScope.assigned === !0, {unmetRequirements: activityRequirements, ...conditionInsights} = await explainActivityConditions({
|
|
10085
11046
|
activity: activity,
|
|
10086
11047
|
activityScope: activityScope,
|
|
10087
11048
|
cascadeActivityScope: cascadeActivityScope,
|
|
10088
11049
|
snapshot: snapshot,
|
|
10089
11050
|
sites: sites
|
|
10090
|
-
}), requirementsReason = unmetRequirements.length > 0 ? {
|
|
11051
|
+
}), unmetRequirements = [ ...requiredDocuments, ...activityRequirements ], requirementsReason = unmetRequirements.length > 0 ? {
|
|
10091
11052
|
kind: "requirements-unmet",
|
|
10092
11053
|
unmetRequirements: unmetRequirements
|
|
10093
11054
|
} : void 0, actions = [];
|
|
@@ -10104,7 +11065,8 @@ async function evaluateActivity(args) {
|
|
|
10104
11065
|
subjectPermissionReason: subjectPermissionReason,
|
|
10105
11066
|
requirementsReason: requirementsReason,
|
|
10106
11067
|
sites: sites,
|
|
10107
|
-
fireConsequence: a => fireConsequence(activity, a)
|
|
11068
|
+
fireConsequence: a => fireConsequence(activity, a),
|
|
11069
|
+
assignment: assignmentMembers(statusEntry?.fields ?? [])
|
|
10108
11070
|
}));
|
|
10109
11071
|
return {
|
|
10110
11072
|
activity: activity,
|
|
@@ -10261,19 +11223,31 @@ function fireableActionVerdict({args: args, insights: insights}) {
|
|
|
10261
11223
|
reason: args.requirementsReason,
|
|
10262
11224
|
...insights
|
|
10263
11225
|
});
|
|
10264
|
-
const
|
|
10265
|
-
return
|
|
11226
|
+
const filterFailure = filterFailureVerdict(args, insights);
|
|
11227
|
+
return filterFailure !== void 0 ? filterFailure : {
|
|
11228
|
+
...actionEvaluationIdentity(action),
|
|
11229
|
+
allowed: !0,
|
|
11230
|
+
...insights
|
|
11231
|
+
};
|
|
11232
|
+
}
|
|
11233
|
+
|
|
11234
|
+
function filterFailureVerdict(args, insights) {
|
|
11235
|
+
const {action: action} = args, {insight: insight} = insights;
|
|
11236
|
+
if (action.filter === void 0 || insight === void 0 || insight.outcome === "satisfied") return;
|
|
11237
|
+
const holderGate = insight.blockedBy.some(entry => isAssignedGate(entry.atom)) ? {
|
|
11238
|
+
holders: [ ...activeAssignmentMembers(args.assignment) ]
|
|
11239
|
+
} : void 0;
|
|
11240
|
+
return disabled({
|
|
10266
11241
|
action: action,
|
|
10267
11242
|
reason: {
|
|
10268
11243
|
kind: "filter-failed",
|
|
10269
11244
|
filter: action.filter
|
|
10270
11245
|
},
|
|
10271
|
-
...insights
|
|
10272
|
-
|
|
10273
|
-
|
|
10274
|
-
|
|
10275
|
-
|
|
10276
|
-
};
|
|
11246
|
+
...insights,
|
|
11247
|
+
...holderGate === void 0 ? {} : {
|
|
11248
|
+
holderGate: holderGate
|
|
11249
|
+
}
|
|
11250
|
+
});
|
|
10277
11251
|
}
|
|
10278
11252
|
|
|
10279
11253
|
function actionEvaluationIdentity(action) {
|
|
@@ -10289,12 +11263,13 @@ function semanticsOf(node) {
|
|
|
10289
11263
|
};
|
|
10290
11264
|
}
|
|
10291
11265
|
|
|
10292
|
-
async function instanceGuardReason({instance: instance, identity: identity, guards: guards}) {
|
|
11266
|
+
async function instanceGuardReason({instance: instance, identity: identity, guards: guards, dereference: dereference}) {
|
|
10293
11267
|
if (guards === void 0 || guards.length === 0) return;
|
|
10294
11268
|
const denied = await instanceWriteDenials({
|
|
10295
11269
|
instance: instance,
|
|
10296
11270
|
guards: guards,
|
|
10297
|
-
identity: identity
|
|
11271
|
+
identity: identity,
|
|
11272
|
+
dereference: dereference ?? (() => Promise.resolve(null))
|
|
10298
11273
|
});
|
|
10299
11274
|
if (denied.length !== 0) return {
|
|
10300
11275
|
kind: "mutation-guard-denied",
|
|
@@ -10325,10 +11300,29 @@ function disabled(args) {
|
|
|
10325
11300
|
} : {},
|
|
10326
11301
|
...args.whenInsight !== void 0 ? {
|
|
10327
11302
|
whenInsight: args.whenInsight
|
|
11303
|
+
} : {},
|
|
11304
|
+
...args.holderGate !== void 0 ? {
|
|
11305
|
+
holderGate: args.holderGate
|
|
10328
11306
|
} : {}
|
|
10329
11307
|
};
|
|
10330
11308
|
}
|
|
10331
11309
|
|
|
11310
|
+
const ASSIGNMENT_KINDS = '["assignee", "assignees"]', HAS_USER = '(count(value[@.type == "user"]) > 0 || value.type == "user")', HAS_MEMBER = "(count(value) > 0 || defined(value.type))";
|
|
11311
|
+
|
|
11312
|
+
function assignmentActivityArm(state) {
|
|
11313
|
+
const assignmentFields = `fields[_type in ${ASSIGNMENT_KINDS}]`;
|
|
11314
|
+
return state === "unrouted" ? `count(${assignmentFields}[${HAS_MEMBER}]) == 0` : state === "held" ? `count(${assignmentFields}[${HAS_USER} && (count(value[@.type == "user" && id == $assignmentUserId]) > 0 || (value.type == "user" && value.id == $assignmentUserId))]) > 0` : `count(${assignmentFields}[${HAS_USER}]) == 0 && count(${assignmentFields}[count(value[@.type == "role" && role in $assignmentRoles]) > 0 || (value.type == "role" && value.role in $assignmentRoles)]) > 0`;
|
|
11315
|
+
}
|
|
11316
|
+
|
|
11317
|
+
function assignmentPrefilter(assignment, params) {
|
|
11318
|
+
if (assignment.userId.length === 0) throw new ContractViolationError("instancesQuery: assignment.userId must be non-empty");
|
|
11319
|
+
const states = [ ...new Set(assignment.states ?? [ "unrouted", "routed", "held" ]) ];
|
|
11320
|
+
if (states.length === 0) throw new ContractViolationError("instancesQuery: assignment.states must not be empty");
|
|
11321
|
+
if (states.some(state => state !== "unrouted" && state !== "routed" && state !== "held")) throw new ContractViolationError("instancesQuery: assignment.states contains an unknown state");
|
|
11322
|
+
return params.assignmentUserId = assignment.userId, params.assignmentRoles = [ ...new Set(assignment.roles ?? []) ].sort(),
|
|
11323
|
+
`count(stages[!defined(exitedAt)].activities[status == "active" && (${states.map(assignmentActivityArm).join(" || ")})]) > 0`;
|
|
11324
|
+
}
|
|
11325
|
+
|
|
10332
11326
|
function inFlightFilter() {
|
|
10333
11327
|
return "!defined(completedAt)";
|
|
10334
11328
|
}
|
|
@@ -10368,7 +11362,7 @@ function compiledConditions(filter, params) {
|
|
|
10368
11362
|
const conditions = [ `_type == "${WORKFLOW_INSTANCE_TYPE}"`, tagScopeFilter() ];
|
|
10369
11363
|
filter.includeCompleted !== !0 && conditions.push(inFlightFilter()), filter.definition !== void 0 && (conditions.push("definition == $definition"),
|
|
10370
11364
|
params.definition = filter.definition), filter.stage !== void 0 && (conditions.push("currentStage == $stage"),
|
|
10371
|
-
params.stage = filter.stage);
|
|
11365
|
+
params.stage = filter.stage), filter.assignment !== void 0 && conditions.push(assignmentPrefilter(filter.assignment, params));
|
|
10372
11366
|
const cursor = beforeArm(filter, params);
|
|
10373
11367
|
cursor !== void 0 && conditions.push(cursor);
|
|
10374
11368
|
const arms = [ documentArm(filter, params), idsArm(filter, params) ].filter(arm => arm !== void 0);
|
|
@@ -10430,6 +11424,46 @@ function claimReleasedEntry(args) {
|
|
|
10430
11424
|
};
|
|
10431
11425
|
}
|
|
10432
11426
|
|
|
11427
|
+
const RENEW_MAX_ATTEMPTS = 3;
|
|
11428
|
+
|
|
11429
|
+
async function claimOf(args) {
|
|
11430
|
+
const {client: client, tag: tag, instanceId: instanceId, effectKey: effectKey} = args, instance = await reload({
|
|
11431
|
+
client: client,
|
|
11432
|
+
instanceId: instanceId,
|
|
11433
|
+
tag: tag
|
|
11434
|
+
});
|
|
11435
|
+
return {
|
|
11436
|
+
instance: instance,
|
|
11437
|
+
claim: instance.pendingEffects.find(e => e._key === effectKey)?.claim
|
|
11438
|
+
};
|
|
11439
|
+
}
|
|
11440
|
+
|
|
11441
|
+
async function claimStillHeld(args) {
|
|
11442
|
+
const {claimToken: claimToken, ...where} = args, {claim: claim} = await claimOf(where);
|
|
11443
|
+
return claim?.claimToken === claimToken;
|
|
11444
|
+
}
|
|
11445
|
+
|
|
11446
|
+
async function renewClaimLease(args) {
|
|
11447
|
+
const {client: client, tag: tag, instanceId: instanceId, effectKey: effectKey, claimToken: claimToken, holdMs: holdMs, clock: clock} = args;
|
|
11448
|
+
for (let attempt = 1; attempt <= RENEW_MAX_ATTEMPTS; attempt++) {
|
|
11449
|
+
const {instance: instance, claim: claim} = await claimOf({
|
|
11450
|
+
client: client,
|
|
11451
|
+
tag: tag,
|
|
11452
|
+
instanceId: instanceId,
|
|
11453
|
+
effectKey: effectKey
|
|
11454
|
+
});
|
|
11455
|
+
if (claim?.claimToken !== claimToken) return;
|
|
11456
|
+
const until = addMs(clock(), holdMs);
|
|
11457
|
+
try {
|
|
11458
|
+
return await client.patch(instance._id).ifRevisionId(instance._rev).set({
|
|
11459
|
+
[`pendingEffects[_key=="${effectKey}"].claim.leaseExpiresAt`]: until
|
|
11460
|
+
}).commit(SYNC_COMMIT), until;
|
|
11461
|
+
} catch (error) {
|
|
11462
|
+
if (!isRevisionConflict(error)) throw error;
|
|
11463
|
+
}
|
|
11464
|
+
}
|
|
11465
|
+
}
|
|
11466
|
+
|
|
10433
11467
|
const SWEEP_MAX_ATTEMPTS = 5;
|
|
10434
11468
|
|
|
10435
11469
|
async function sweepStaleClaims(args) {
|
|
@@ -10471,6 +11505,222 @@ function stripClaim(entry) {
|
|
|
10471
11505
|
return rest;
|
|
10472
11506
|
}
|
|
10473
11507
|
|
|
11508
|
+
function isProjectRole(value) {
|
|
11509
|
+
return isRecord(value) && typeof value.name == "string";
|
|
11510
|
+
}
|
|
11511
|
+
|
|
11512
|
+
async function projectRoleNames(request, projectId) {
|
|
11513
|
+
const result = await request({
|
|
11514
|
+
url: `/projects/${encodeURIComponent(projectId)}/roles`
|
|
11515
|
+
});
|
|
11516
|
+
if (!Array.isArray(result) || !result.every(isProjectRole)) throw new Error(`Project role catalog response is invalid for project "${projectId}"`);
|
|
11517
|
+
return new Set(result.map(role => role.name));
|
|
11518
|
+
}
|
|
11519
|
+
|
|
11520
|
+
function memberRoleNames(member, projectId) {
|
|
11521
|
+
if (!Array.isArray(member.roles) || !member.roles.every(isProjectRole)) throw new Error(`Project member role directory response is invalid for project "${projectId}" and member "${member.id}"`);
|
|
11522
|
+
return member.roles.map(role => role.name);
|
|
11523
|
+
}
|
|
11524
|
+
|
|
11525
|
+
function heldRoleNames(members, projectId) {
|
|
11526
|
+
return new Set(members.flatMap(member => member.isRobot === !0 ? [] : memberRoleNames(member, projectId)));
|
|
11527
|
+
}
|
|
11528
|
+
|
|
11529
|
+
function addRoles(collector, args) {
|
|
11530
|
+
for (const role of args.roles ?? []) collector.references.push({
|
|
11531
|
+
definition: collector.definition,
|
|
11532
|
+
path: args.path,
|
|
11533
|
+
role: role
|
|
11534
|
+
});
|
|
11535
|
+
}
|
|
11536
|
+
|
|
11537
|
+
function visitAssigneeValue(collector, args) {
|
|
11538
|
+
const members = Array.isArray(args.value) ? args.value : [ args.value ];
|
|
11539
|
+
for (const [index, member] of members.entries()) !isRecord(member) || member.type !== "role" || typeof member.role != "string" || collector.references.push({
|
|
11540
|
+
definition: collector.definition,
|
|
11541
|
+
path: `${args.path}[${index}].role`,
|
|
11542
|
+
role: member.role
|
|
11543
|
+
});
|
|
11544
|
+
}
|
|
11545
|
+
|
|
11546
|
+
function visitObjectValue(collector, args) {
|
|
11547
|
+
for (const shape of args.fields ?? []) visitShapeValue(collector, {
|
|
11548
|
+
path: `${args.path}.${shape.name}`,
|
|
11549
|
+
shape: shape,
|
|
11550
|
+
value: args.value[shape.name]
|
|
11551
|
+
});
|
|
11552
|
+
}
|
|
11553
|
+
|
|
11554
|
+
function visitArrayValue(collector, args) {
|
|
11555
|
+
for (const [index, row] of args.rows.entries()) if (isRecord(row)) for (const shape of args.shapes ?? []) visitShapeValue(collector, {
|
|
11556
|
+
path: `${args.path}[${index}].${shape.name}`,
|
|
11557
|
+
shape: shape,
|
|
11558
|
+
value: row[shape.name]
|
|
11559
|
+
});
|
|
11560
|
+
}
|
|
11561
|
+
|
|
11562
|
+
function visitAssignmentValue(collector, args) {
|
|
11563
|
+
args.kind === "assignee" || args.kind === "assignees" ? visitAssigneeValue(collector, args) : args.kind === "object" && isRecord(args.value) ? visitObjectValue(collector, {
|
|
11564
|
+
...args,
|
|
11565
|
+
value: args.value
|
|
11566
|
+
}) : args.kind === "array" && Array.isArray(args.value) && visitArrayValue(collector, {
|
|
11567
|
+
path: args.path,
|
|
11568
|
+
rows: args.value,
|
|
11569
|
+
shapes: args.of
|
|
11570
|
+
});
|
|
11571
|
+
}
|
|
11572
|
+
|
|
11573
|
+
function visitShapeValue(collector, args) {
|
|
11574
|
+
visitAssignmentValue(collector, {
|
|
11575
|
+
path: args.path,
|
|
11576
|
+
kind: args.shape.type,
|
|
11577
|
+
value: args.value,
|
|
11578
|
+
fields: args.shape.fields,
|
|
11579
|
+
of: args.shape.of
|
|
11580
|
+
});
|
|
11581
|
+
}
|
|
11582
|
+
|
|
11583
|
+
function visitField(collector, args) {
|
|
11584
|
+
addRoles(collector, {
|
|
11585
|
+
path: `${args.path}.roles`,
|
|
11586
|
+
roles: args.field.roles
|
|
11587
|
+
});
|
|
11588
|
+
for (const shape of args.field.fields ?? args.field.of ?? []) visitFieldShape(collector, {
|
|
11589
|
+
parentPath: args.path,
|
|
11590
|
+
shape: shape
|
|
11591
|
+
});
|
|
11592
|
+
args.field.initialValue?.type === "literal" && visitAssignmentValue(collector, {
|
|
11593
|
+
path: `${args.path}.initialValue.value`,
|
|
11594
|
+
kind: args.field.type,
|
|
11595
|
+
value: args.field.initialValue.value,
|
|
11596
|
+
fields: args.field.fields,
|
|
11597
|
+
of: args.field.of
|
|
11598
|
+
});
|
|
11599
|
+
}
|
|
11600
|
+
|
|
11601
|
+
function visitFieldShape(collector, args) {
|
|
11602
|
+
const path = `${args.parentPath}.${args.shape.name}`;
|
|
11603
|
+
addRoles(collector, {
|
|
11604
|
+
path: `${path}.roles`,
|
|
11605
|
+
roles: args.shape.roles
|
|
11606
|
+
});
|
|
11607
|
+
for (const child of args.shape.fields ?? args.shape.of ?? []) visitFieldShape(collector, {
|
|
11608
|
+
parentPath: path,
|
|
11609
|
+
shape: child
|
|
11610
|
+
});
|
|
11611
|
+
}
|
|
11612
|
+
|
|
11613
|
+
function rolesFromConditions(definition) {
|
|
11614
|
+
return conditionSitesOf(definition).flatMap(site => {
|
|
11615
|
+
try {
|
|
11616
|
+
return analyzeCondition(site.condition).atoms.flatMap(atom => rolesGateOf(atom) ?? []);
|
|
11617
|
+
} catch {
|
|
11618
|
+
return [];
|
|
11619
|
+
}
|
|
11620
|
+
});
|
|
11621
|
+
}
|
|
11622
|
+
|
|
11623
|
+
function visitRoleAliases(collector, aliases) {
|
|
11624
|
+
for (const [role, fulfillers] of Object.entries(aliases ?? {})) role !== UNIVERSAL_ROLE_ALIAS_KEY && collector.references.push({
|
|
11625
|
+
definition: collector.definition,
|
|
11626
|
+
path: `roleAliases.${role}`,
|
|
11627
|
+
role: role
|
|
11628
|
+
}), addRoles(collector, {
|
|
11629
|
+
path: `roleAliases.${role}`,
|
|
11630
|
+
roles: fulfillers
|
|
11631
|
+
});
|
|
11632
|
+
}
|
|
11633
|
+
|
|
11634
|
+
function visitFields(collector, args) {
|
|
11635
|
+
for (const [index, field] of (args.fields ?? []).entries()) visitField(collector, {
|
|
11636
|
+
path: `${args.parentPath}[${index}]`,
|
|
11637
|
+
field: field
|
|
11638
|
+
});
|
|
11639
|
+
}
|
|
11640
|
+
|
|
11641
|
+
function visitAction(collector, args) {
|
|
11642
|
+
addRoles(collector, {
|
|
11643
|
+
path: `${args.path}.roles`,
|
|
11644
|
+
roles: args.action.roles
|
|
11645
|
+
});
|
|
11646
|
+
for (const [effectIndex, effect] of (args.action.effects ?? []).entries()) for (const shape of effect.outputs ?? []) visitFieldShape(collector, {
|
|
11647
|
+
parentPath: `${args.path}.effects[${effectIndex}].outputs`,
|
|
11648
|
+
shape: shape
|
|
11649
|
+
});
|
|
11650
|
+
}
|
|
11651
|
+
|
|
11652
|
+
function visitStage(collector, args) {
|
|
11653
|
+
const stagePath = `stages[${args.stageIndex}]`;
|
|
11654
|
+
visitFields(collector, {
|
|
11655
|
+
fields: args.stage.fields,
|
|
11656
|
+
parentPath: `${stagePath}.fields`
|
|
11657
|
+
});
|
|
11658
|
+
for (const [activityIndex, activity] of (args.stage.activities ?? []).entries()) {
|
|
11659
|
+
const activityPath = `${stagePath}.activities[${activityIndex}]`;
|
|
11660
|
+
visitFields(collector, {
|
|
11661
|
+
fields: activity.fields,
|
|
11662
|
+
parentPath: `${activityPath}.fields`
|
|
11663
|
+
});
|
|
11664
|
+
for (const [actionIndex, action] of (activity.actions ?? []).entries()) visitAction(collector, {
|
|
11665
|
+
action: action,
|
|
11666
|
+
path: `${activityPath}.actions[${actionIndex}]`
|
|
11667
|
+
});
|
|
11668
|
+
}
|
|
11669
|
+
}
|
|
11670
|
+
|
|
11671
|
+
function addConditionReferences(collector, definition) {
|
|
11672
|
+
for (const role of rolesFromConditions(definition)) collector.references.push({
|
|
11673
|
+
definition: collector.definition,
|
|
11674
|
+
path: "generated role condition",
|
|
11675
|
+
role: role
|
|
11676
|
+
});
|
|
11677
|
+
}
|
|
11678
|
+
|
|
11679
|
+
function definitionRoleReferences(definition) {
|
|
11680
|
+
const collector = {
|
|
11681
|
+
references: [],
|
|
11682
|
+
definition: definition.name
|
|
11683
|
+
};
|
|
11684
|
+
visitRoleAliases(collector, definition.roleAliases), visitFields(collector, {
|
|
11685
|
+
fields: definition.fields,
|
|
11686
|
+
parentPath: "fields"
|
|
11687
|
+
});
|
|
11688
|
+
for (const [stageIndex, stage] of definition.stages.entries()) visitStage(collector, {
|
|
11689
|
+
stage: stage,
|
|
11690
|
+
stageIndex: stageIndex
|
|
11691
|
+
});
|
|
11692
|
+
return addConditionReferences(collector, definition), collector.references;
|
|
11693
|
+
}
|
|
11694
|
+
|
|
11695
|
+
function definitionRoleNames(definition) {
|
|
11696
|
+
return [ ...new Set(definitionRoleReferences(definition).map(reference => reference.role)) ];
|
|
11697
|
+
}
|
|
11698
|
+
|
|
11699
|
+
function roleCatalogProjectId(client, resource) {
|
|
11700
|
+
if (resource.type === "dataset") return datasetResourceParts(resource.id).projectId;
|
|
11701
|
+
const projectId = client.config?.().projectId;
|
|
11702
|
+
if (typeof projectId == "string" && projectId.length > 0) return projectId;
|
|
11703
|
+
throw new Error(`workflow.deployDefinitions: definitions reference project roles on ${resource.type} workflow resources, but the client does not expose a projectId for the project role catalog`);
|
|
11704
|
+
}
|
|
11705
|
+
|
|
11706
|
+
async function validateDefinitionRoles(args) {
|
|
11707
|
+
const references = args.definitions.flatMap(definitionRoleReferences);
|
|
11708
|
+
if (references.length === 0) /* @__PURE__ */ return new Map;
|
|
11709
|
+
if (args.client.request === void 0) throw new Error("workflow.deployDefinitions: definitions reference project roles, but the client cannot read the project role catalog");
|
|
11710
|
+
const projectId = roleCatalogProjectId(args.client, args.workflowResource), [known, members] = await Promise.all([ projectRoleNames(args.client.request, projectId), _requestProjectMembers(args.client.request, projectId) ]), unknown = references.filter(reference => !known.has(reference.role));
|
|
11711
|
+
if (unknown.length > 0) {
|
|
11712
|
+
const lines = unknown.map(({definition: definition, path: path, role: role}) => ` - ${definition}: ${path} references unknown role "${role}"`);
|
|
11713
|
+
throw new Error(`workflow.deployDefinitions: unknown project roles for project "${projectId}":\n${lines.join(`\n`)}`);
|
|
11714
|
+
}
|
|
11715
|
+
const held = heldRoleNames(members, projectId), warnings = /* @__PURE__ */ new Map;
|
|
11716
|
+
for (const {definition: definition, role: role} of references) {
|
|
11717
|
+
if (held.has(role)) continue;
|
|
11718
|
+
const message = `Project role "${role}" has no current user holder; work routed to it has no recipient.`, definitionWarnings = warnings.get(definition) ?? [];
|
|
11719
|
+
definitionWarnings.includes(message) || definitionWarnings.push(message), warnings.set(definition, definitionWarnings);
|
|
11720
|
+
}
|
|
11721
|
+
return warnings;
|
|
11722
|
+
}
|
|
11723
|
+
|
|
10474
11724
|
async function sortByDependencies({client: client, definitions: definitions, tag: tag}) {
|
|
10475
11725
|
const byName = /* @__PURE__ */ new Map;
|
|
10476
11726
|
for (const def of definitions) {
|
|
@@ -10587,7 +11837,7 @@ function refsOf(def) {
|
|
|
10587
11837
|
}
|
|
10588
11838
|
|
|
10589
11839
|
function hashDefinitionContent(def) {
|
|
10590
|
-
const canonical = stableStringify(def);
|
|
11840
|
+
const canonical = stableStringify(withoutAuthoringRuntime(def));
|
|
10591
11841
|
let hash = 0xcbf29ce484222325n;
|
|
10592
11842
|
for (let i = 0; i < canonical.length; i++) hash ^= BigInt(canonical.charCodeAt(i)),
|
|
10593
11843
|
hash = hash * 0x100000001b3n & 0xffffffffffffffffn;
|
|
@@ -10600,7 +11850,7 @@ function planDefinitionDeploy({def: def, latest: latest, target: target}) {
|
|
|
10600
11850
|
definition: def,
|
|
10601
11851
|
workflowResource: target.workflowResource
|
|
10602
11852
|
});
|
|
10603
|
-
const expanded = expandResourceAliases(def, target.resourceAliases), contentHash = hashDefinitionContent(expanded), unchanged = latest !== void 0 && latest.contentHash === contentHash, version = unchanged ? latest.version : (latest?.version ?? 0) + 1, docId = definitionDocId({
|
|
11853
|
+
const expanded = withoutAuthoringRuntime(expandResourceAliases(def, target.resourceAliases)), contentHash = hashDefinitionContent(expanded), unchanged = latest !== void 0 && latest.contentHash === contentHash, version = unchanged ? latest.version : (latest?.version ?? 0) + 1, docId = definitionDocId({
|
|
10604
11854
|
tag: target.tag,
|
|
10605
11855
|
definition: expanded.name,
|
|
10606
11856
|
version: version
|
|
@@ -10657,10 +11907,17 @@ function isDocumentEnvelopeKey(key) {
|
|
|
10657
11907
|
return key.startsWith("_") || DEPLOY_STAMPED_KEYS.has(key);
|
|
10658
11908
|
}
|
|
10659
11909
|
|
|
11910
|
+
const AUTHORING_RUNTIME_KEY = "runtime";
|
|
11911
|
+
|
|
11912
|
+
function withoutAuthoringRuntime(def) {
|
|
11913
|
+
const content = Object.entries(def).filter(([key]) => key !== AUTHORING_RUNTIME_KEY);
|
|
11914
|
+
return Object.fromEntries(content);
|
|
11915
|
+
}
|
|
11916
|
+
|
|
10660
11917
|
function parseDefinitionInput(def, caller) {
|
|
10661
11918
|
const label = labelFor(caller, def);
|
|
10662
11919
|
if (def._type !== void 0 && def._type !== WORKFLOW_DEFINITION_TYPE) throw new Error(`${label}: expected a workflow definition or a "${WORKFLOW_DEFINITION_TYPE}" document, got _type "${String(def._type)}"`);
|
|
10663
|
-
const content = Object.fromEntries(Object.entries(def).filter(([key]) => !isDocumentEnvelopeKey(key)));
|
|
11920
|
+
const content = Object.fromEntries(Object.entries(withoutAuthoringRuntime(def)).filter(([key]) => !isDocumentEnvelopeKey(key)));
|
|
10664
11921
|
return parseStoredDefinition(content, label);
|
|
10665
11922
|
}
|
|
10666
11923
|
|
|
@@ -10771,7 +12028,7 @@ async function commitEdit({ctx: ctx, target: target, mode: mode, value: value, r
|
|
|
10771
12028
|
site: site,
|
|
10772
12029
|
mode: mode,
|
|
10773
12030
|
value: value
|
|
10774
|
-
}), mutation =
|
|
12031
|
+
}), mutation = startRevisionRetryMutation(ctx.instance);
|
|
10775
12032
|
recordProcessedRequest({
|
|
10776
12033
|
mutation: mutation,
|
|
10777
12034
|
record: requestRecord,
|
|
@@ -10915,14 +12172,14 @@ async function runCommitVerb(params) {
|
|
|
10915
12172
|
client: client,
|
|
10916
12173
|
instanceId: instanceId,
|
|
10917
12174
|
tag: tag
|
|
10918
|
-
});
|
|
12175
|
+
}), now = clock();
|
|
10919
12176
|
return runDeduped({
|
|
10920
12177
|
client: client,
|
|
10921
12178
|
tag: tag,
|
|
10922
12179
|
instanceId: instanceId,
|
|
10923
12180
|
record: record,
|
|
10924
12181
|
before: before,
|
|
10925
|
-
now:
|
|
12182
|
+
now: now,
|
|
10926
12183
|
actor: context.actor,
|
|
10927
12184
|
clientForGdr: context.clientForGdr,
|
|
10928
12185
|
refSurface: context.refSurface,
|
|
@@ -10933,6 +12190,7 @@ async function runCommitVerb(params) {
|
|
|
10933
12190
|
run: () => run({
|
|
10934
12191
|
...context,
|
|
10935
12192
|
clock: clock,
|
|
12193
|
+
now: now,
|
|
10936
12194
|
record: record,
|
|
10937
12195
|
before: before
|
|
10938
12196
|
})
|
|
@@ -10956,7 +12214,7 @@ async function runDeduped(args) {
|
|
|
10956
12214
|
client: client,
|
|
10957
12215
|
instanceId: instanceId,
|
|
10958
12216
|
tag: tag
|
|
10959
|
-
}),
|
|
12217
|
+
}), settled = await cascadeAndReload({
|
|
10960
12218
|
client: client,
|
|
10961
12219
|
tag: tag,
|
|
10962
12220
|
instanceId: instanceId,
|
|
@@ -10968,11 +12226,10 @@ async function runDeduped(args) {
|
|
|
10968
12226
|
executionContext: executionContext,
|
|
10969
12227
|
telemetry: telemetry
|
|
10970
12228
|
});
|
|
10971
|
-
return {
|
|
10972
|
-
|
|
10973
|
-
|
|
10974
|
-
|
|
10975
|
-
};
|
|
12229
|
+
return settledResult({
|
|
12230
|
+
...settled,
|
|
12231
|
+
changed: settled.instance._rev !== pre._rev
|
|
12232
|
+
});
|
|
10976
12233
|
};
|
|
10977
12234
|
if (record !== void 0 && before !== void 0 && findProcessedRequest({
|
|
10978
12235
|
instance: before,
|
|
@@ -10988,7 +12245,7 @@ async function runDeduped(args) {
|
|
|
10988
12245
|
}
|
|
10989
12246
|
|
|
10990
12247
|
async function cascade({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, executionContext: executionContext, telemetry: telemetry, overlay: overlay}) {
|
|
10991
|
-
const
|
|
12248
|
+
const settled = await cascadeAutoTransitions({
|
|
10992
12249
|
client: client,
|
|
10993
12250
|
instanceId: instanceId,
|
|
10994
12251
|
actor: actor,
|
|
@@ -11024,13 +12281,13 @@ async function cascade({client: client, instanceId: instanceId, actor: actor, cl
|
|
|
11024
12281
|
...telemetry !== void 0 ? {
|
|
11025
12282
|
telemetry: telemetry
|
|
11026
12283
|
} : {}
|
|
11027
|
-
}),
|
|
12284
|
+
}), settled;
|
|
11028
12285
|
}
|
|
11029
12286
|
|
|
11030
12287
|
async function cascadeAndReload(args) {
|
|
11031
12288
|
const {client: client, tag: tag, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock} = args;
|
|
11032
12289
|
return {
|
|
11033
|
-
|
|
12290
|
+
...await cascade({
|
|
11034
12291
|
client: client,
|
|
11035
12292
|
instanceId: instanceId,
|
|
11036
12293
|
actor: actor,
|
|
@@ -11053,6 +12310,84 @@ async function cascadeAndReload(args) {
|
|
|
11053
12310
|
};
|
|
11054
12311
|
}
|
|
11055
12312
|
|
|
12313
|
+
async function unchangedNextEvaluationAt(args) {
|
|
12314
|
+
const {client: client, instance: instance, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, now: now} = args;
|
|
12315
|
+
if (terminalState(instance) !== "in-flight") return;
|
|
12316
|
+
const ctx = await buildEngineContext({
|
|
12317
|
+
client: client,
|
|
12318
|
+
instance: instance,
|
|
12319
|
+
definition: parseDefinitionSnapshot(instance),
|
|
12320
|
+
clientForGdr: clientForGdr,
|
|
12321
|
+
refSurface: refSurface,
|
|
12322
|
+
memberRolesLoader: memberRolesLoader,
|
|
12323
|
+
clock: () => now,
|
|
12324
|
+
...actor !== void 0 ? {
|
|
12325
|
+
actor: actor
|
|
12326
|
+
} : {},
|
|
12327
|
+
...args.executionContext !== void 0 ? {
|
|
12328
|
+
executionContext: args.executionContext
|
|
12329
|
+
} : {},
|
|
12330
|
+
...args.telemetry !== void 0 ? {
|
|
12331
|
+
telemetry: args.telemetry
|
|
12332
|
+
} : {}
|
|
12333
|
+
});
|
|
12334
|
+
return ctxNextEvaluationAt(ctx);
|
|
12335
|
+
}
|
|
12336
|
+
|
|
12337
|
+
async function adminOverrideResult(args) {
|
|
12338
|
+
const {client: client, tag: tag, instanceId: instanceId, fired: fired, now: now, executionContext: executionContext, telemetry: telemetry, ...scope} = args, optional = {
|
|
12339
|
+
...executionContext !== void 0 ? {
|
|
12340
|
+
executionContext: executionContext
|
|
12341
|
+
} : {},
|
|
12342
|
+
...telemetry !== void 0 ? {
|
|
12343
|
+
telemetry: telemetry
|
|
12344
|
+
} : {}
|
|
12345
|
+
}, settled = fired ? await cascade({
|
|
12346
|
+
client: client,
|
|
12347
|
+
instanceId: instanceId,
|
|
12348
|
+
...scope,
|
|
12349
|
+
...optional
|
|
12350
|
+
}) : {
|
|
12351
|
+
cascaded: 0
|
|
12352
|
+
}, instance = await reload({
|
|
12353
|
+
client: client,
|
|
12354
|
+
instanceId: instanceId,
|
|
12355
|
+
tag: tag
|
|
12356
|
+
});
|
|
12357
|
+
return settledResult({
|
|
12358
|
+
...settled,
|
|
12359
|
+
instance: instance,
|
|
12360
|
+
changed: fired,
|
|
12361
|
+
...fired ? {} : {
|
|
12362
|
+
nextEvaluationAt: await unchangedNextEvaluationAt({
|
|
12363
|
+
client: client,
|
|
12364
|
+
instance: instance,
|
|
12365
|
+
now: now,
|
|
12366
|
+
actor: scope.actor,
|
|
12367
|
+
clientForGdr: scope.clientForGdr,
|
|
12368
|
+
refSurface: scope.refSurface,
|
|
12369
|
+
memberRolesLoader: scope.memberRolesLoader,
|
|
12370
|
+
...optional
|
|
12371
|
+
})
|
|
12372
|
+
}
|
|
12373
|
+
});
|
|
12374
|
+
}
|
|
12375
|
+
|
|
12376
|
+
function settledResult(args) {
|
|
12377
|
+
const {instance: instance, cascaded: cascaded, changed: changed, ranOps: ranOps, nextEvaluationAt: nextEvaluationAt} = args;
|
|
12378
|
+
return {
|
|
12379
|
+
instance: instance,
|
|
12380
|
+
cascaded: cascaded,
|
|
12381
|
+
changed: changed,
|
|
12382
|
+
...ranOps !== void 0 ? {
|
|
12383
|
+
ranOps: ranOps
|
|
12384
|
+
} : {},
|
|
12385
|
+
...nextEvaluationAt !== void 0 ? {
|
|
12386
|
+
nextEvaluationAt: nextEvaluationAt
|
|
12387
|
+
} : {}
|
|
12388
|
+
};
|
|
12389
|
+
}
|
|
12390
|
+
|
|
11056
12391
|
async function settleStart(args) {
|
|
11057
12392
|
const runArgs = {
|
|
11058
12393
|
client: args.client,
|
|
@@ -11114,7 +12449,7 @@ async function resumeStart(args) {
|
|
|
11114
12449
|
instance: existing,
|
|
11115
12450
|
initialFieldCount: initialFieldCount,
|
|
11116
12451
|
viaSpawn: !1
|
|
11117
|
-
})),
|
|
12452
|
+
})), settled = await settleStart({
|
|
11118
12453
|
...settlementArgs,
|
|
11119
12454
|
instanceId: existing._id,
|
|
11120
12455
|
...completesStart ? {
|
|
@@ -11125,11 +12460,11 @@ async function resumeStart(args) {
|
|
|
11125
12460
|
instanceId: existing._id,
|
|
11126
12461
|
tag: args.tag
|
|
11127
12462
|
});
|
|
11128
|
-
return {
|
|
12463
|
+
return settledResult({
|
|
12464
|
+
...settled,
|
|
11129
12465
|
instance: instance,
|
|
11130
|
-
cascaded: cascaded,
|
|
11131
12466
|
changed: instance._rev !== existing._rev
|
|
11132
|
-
};
|
|
12467
|
+
});
|
|
11133
12468
|
}
|
|
11134
12469
|
|
|
11135
12470
|
function engineOptionsForActor({actor: actor, clock: clock, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, executionContext: executionContext, telemetry: telemetry}) {
|
|
@@ -11228,7 +12563,7 @@ async function dispatchGatedWrite(args) {
|
|
|
11228
12563
|
...resourceClients !== void 0 ? {
|
|
11229
12564
|
resourceClients: resourceClients
|
|
11230
12565
|
} : {}
|
|
11231
|
-
}), ranOps = await apply(args.before, evaluation),
|
|
12566
|
+
}), ranOps = await apply(args.before, evaluation), settled = await cascadeAndReload({
|
|
11232
12567
|
client: client,
|
|
11233
12568
|
tag: tag,
|
|
11234
12569
|
instanceId: instanceId,
|
|
@@ -11240,14 +12575,13 @@ async function dispatchGatedWrite(args) {
|
|
|
11240
12575
|
executionContext: args.executionContext,
|
|
11241
12576
|
telemetry: args.telemetry
|
|
11242
12577
|
});
|
|
11243
|
-
return {
|
|
11244
|
-
|
|
11245
|
-
cascaded: cascaded,
|
|
12578
|
+
return settledResult({
|
|
12579
|
+
...settled,
|
|
11246
12580
|
changed: !0,
|
|
11247
12581
|
...ranOps !== void 0 ? {
|
|
11248
12582
|
ranOps: ranOps
|
|
11249
12583
|
} : {}
|
|
11250
|
-
};
|
|
12584
|
+
});
|
|
11251
12585
|
}
|
|
11252
12586
|
|
|
11253
12587
|
function guardDeniedError(instanceId, reason) {
|
|
@@ -11461,6 +12795,10 @@ async function abortInstances(args) {
|
|
|
11461
12795
|
return aborted;
|
|
11462
12796
|
}
|
|
11463
12797
|
|
|
12798
|
+
function lintDeadActivityInputSeeds(definition) {
|
|
12799
|
+
return definition.stages.flatMap(stage => (stage.activities ?? []).flatMap(activity => (activity.fields ?? []).filter(field => field.initialValue?.type === "input").map(field => `Activity field "${stage.name}.${activity.name}.${field.name}" uses an input seed, but start/spawn inputs address workflow-scope fields only; this field will always materialize empty. Use a literal, query, field read, or working-memory field instead.`)));
|
|
12800
|
+
}
|
|
12801
|
+
|
|
11464
12802
|
function projectStartSliceRow(instance) {
|
|
11465
12803
|
const subject = (instance.fields ?? []).find(field => field._type === "subject")?.value;
|
|
11466
12804
|
return {
|
|
@@ -11488,14 +12826,18 @@ const workflow = {
|
|
|
11488
12826
|
definitions.forEach(validateDefinition), assertReaderModelAcknowledgement(args.expectedMinReaderModel, {
|
|
11489
12827
|
requiredMinReaderModel: requiredDefinitionReaderModel(definitions)
|
|
11490
12828
|
});
|
|
11491
|
-
const
|
|
12829
|
+
const roleWarnings = await validateDefinitionRoles({
|
|
12830
|
+
client: client,
|
|
12831
|
+
definitions: definitions,
|
|
12832
|
+
workflowResource: workflowResource
|
|
12833
|
+
}), ordered = await sortByDependencies({
|
|
11492
12834
|
client: client,
|
|
11493
12835
|
definitions: definitions,
|
|
11494
12836
|
tag: tag
|
|
11495
12837
|
}), results = [], deployed = [], tx = client.transaction();
|
|
11496
12838
|
let hasWrites = !1;
|
|
11497
12839
|
for (const def of ordered) {
|
|
11498
|
-
const lint = lintEffectOutputs(def), warnings = lint.length > 0 ? {
|
|
12840
|
+
const lint = [ ...lintEffectOutputs(def), ...lintDeadActivityInputSeeds(def), ...roleWarnings.get(def.name) ?? [] ], warnings = lint.length > 0 ? {
|
|
11499
12841
|
warnings: lint
|
|
11500
12842
|
} : {}, latest = await loadLatestDeployed({
|
|
11501
12843
|
client: client,
|
|
@@ -11588,12 +12930,27 @@ const workflow = {
|
|
|
11588
12930
|
return runCommitVerb({
|
|
11589
12931
|
args: args,
|
|
11590
12932
|
op: "fireAction",
|
|
11591
|
-
run: async ({access: access, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, record: record, before: before}) => {
|
|
11592
|
-
if (findCurrentActivityEntry(before, activity) === void 0 && idempotent === !0) return {
|
|
12933
|
+
run: async ({access: access, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, now: now, record: record, before: before}) => {
|
|
12934
|
+
if (findCurrentActivityEntry(before, activity) === void 0 && idempotent === !0) return settledResult({
|
|
11593
12935
|
instance: before,
|
|
11594
12936
|
cascaded: 0,
|
|
11595
|
-
changed: !1
|
|
11596
|
-
|
|
12937
|
+
changed: !1,
|
|
12938
|
+
nextEvaluationAt: await unchangedNextEvaluationAt({
|
|
12939
|
+
client: client,
|
|
12940
|
+
instance: before,
|
|
12941
|
+
actor: actor,
|
|
12942
|
+
clientForGdr: clientForGdr,
|
|
12943
|
+
refSurface: refSurface,
|
|
12944
|
+
memberRolesLoader: memberRolesLoader,
|
|
12945
|
+
now: now,
|
|
12946
|
+
...executionContext !== void 0 ? {
|
|
12947
|
+
executionContext: executionContext
|
|
12948
|
+
} : {},
|
|
12949
|
+
...args.telemetry !== void 0 ? {
|
|
12950
|
+
telemetry: args.telemetry
|
|
12951
|
+
} : {}
|
|
12952
|
+
})
|
|
12953
|
+
});
|
|
11597
12954
|
const result = await dispatchGatedWrite({
|
|
11598
12955
|
client: client,
|
|
11599
12956
|
tag: tag,
|
|
@@ -11730,7 +13087,7 @@ const workflow = {
|
|
|
11730
13087
|
});
|
|
11731
13088
|
},
|
|
11732
13089
|
completeEffect: async rawArgs => {
|
|
11733
|
-
const args = taggedScope(rawArgs, REQUEST_TAG.completeEffect), {client: client, tag: tag, instanceId: instanceId, effectKey: effectKey, status: status, outputs: outputs, ops: ops, detail: detail, error: error, durationMs: durationMs, executionContext: executionContext} = args, operationContext = await resolveOperationContext(args), clock = args.clock ?? wallClock, record = requestRecordFor({
|
|
13090
|
+
const args = taggedScope(rawArgs, REQUEST_TAG.completeEffect), {client: client, tag: tag, instanceId: instanceId, effectKey: effectKey, status: status, outputs: outputs, ops: ops, detail: detail, error: error, durationMs: durationMs, executionContext: executionContext} = args, operationContext = await resolveOperationContext(args), clock = args.clock ?? wallClock, now = clock(), record = requestRecordFor({
|
|
11734
13091
|
idempotencyKey: args.idempotencyKey,
|
|
11735
13092
|
op: "completeEffect",
|
|
11736
13093
|
idempotencyTtlMs: args.idempotencyTtlMs
|
|
@@ -11740,7 +13097,7 @@ const workflow = {
|
|
|
11740
13097
|
tag: tag,
|
|
11741
13098
|
instanceId: instanceId,
|
|
11742
13099
|
record: record,
|
|
11743
|
-
now:
|
|
13100
|
+
now: now,
|
|
11744
13101
|
...operationContext,
|
|
11745
13102
|
clock: clock,
|
|
11746
13103
|
executionContext: executionContext,
|
|
@@ -11775,7 +13132,7 @@ const workflow = {
|
|
|
11775
13132
|
executionContext: executionContext,
|
|
11776
13133
|
telemetry: args.telemetry
|
|
11777
13134
|
})
|
|
11778
|
-
}),
|
|
13135
|
+
}), settled = await cascadeAndReload({
|
|
11779
13136
|
client: client,
|
|
11780
13137
|
tag: tag,
|
|
11781
13138
|
instanceId: instanceId,
|
|
@@ -11785,22 +13142,21 @@ const workflow = {
|
|
|
11785
13142
|
telemetry: args.telemetry
|
|
11786
13143
|
});
|
|
11787
13144
|
return resolveTelemetry(args.telemetry).log(WorkflowEffectCompleted, {
|
|
11788
|
-
...definitionHashFragment(instance.pinnedContentHash),
|
|
13145
|
+
...definitionHashFragment(settled.instance.pinnedContentHash),
|
|
11789
13146
|
instanceId: instanceId,
|
|
11790
13147
|
effect: completion.effect,
|
|
11791
13148
|
status: status,
|
|
11792
13149
|
origin: completion.origin,
|
|
11793
|
-
cascaded: cascaded
|
|
11794
|
-
}), {
|
|
11795
|
-
|
|
11796
|
-
cascaded: cascaded,
|
|
13150
|
+
cascaded: settled.cascaded
|
|
13151
|
+
}), settledResult({
|
|
13152
|
+
...settled,
|
|
11797
13153
|
changed: !0
|
|
11798
|
-
};
|
|
13154
|
+
});
|
|
11799
13155
|
}
|
|
11800
13156
|
});
|
|
11801
13157
|
},
|
|
11802
13158
|
commitEffectOps: async rawArgs => {
|
|
11803
|
-
const args = taggedScope(rawArgs, REQUEST_TAG.commitEffectOps), {client: client, tag: tag, instanceId: instanceId, effectKey: effectKey, claimToken: claimToken, ops: ops, executionContext: executionContext} = args, operationContext = await resolveOperationContext(args), clock = args.clock ?? wallClock, record = requestRecordFor({
|
|
13159
|
+
const args = taggedScope(rawArgs, REQUEST_TAG.commitEffectOps), {client: client, tag: tag, instanceId: instanceId, effectKey: effectKey, claimToken: claimToken, ops: ops, executionContext: executionContext} = args, operationContext = await resolveOperationContext(args), clock = args.clock ?? wallClock, now = clock(), record = requestRecordFor({
|
|
11804
13160
|
idempotencyKey: args.idempotencyKey,
|
|
11805
13161
|
op: "commitEffectOps",
|
|
11806
13162
|
idempotencyTtlMs: args.idempotencyTtlMs
|
|
@@ -11811,7 +13167,7 @@ const workflow = {
|
|
|
11811
13167
|
tag: tag,
|
|
11812
13168
|
instanceId: instanceId,
|
|
11813
13169
|
record: record,
|
|
11814
|
-
now:
|
|
13170
|
+
now: now,
|
|
11815
13171
|
...operationContext,
|
|
11816
13172
|
clock: clock,
|
|
11817
13173
|
executionContext: executionContext,
|
|
@@ -11831,7 +13187,7 @@ const workflow = {
|
|
|
11831
13187
|
executionContext: executionContext,
|
|
11832
13188
|
telemetry: args.telemetry
|
|
11833
13189
|
})
|
|
11834
|
-
}),
|
|
13190
|
+
}), settled = await cascadeAndReload({
|
|
11835
13191
|
client: client,
|
|
11836
13192
|
tag: tag,
|
|
11837
13193
|
instanceId: instanceId,
|
|
@@ -11841,15 +13197,14 @@ const workflow = {
|
|
|
11841
13197
|
telemetry: args.telemetry
|
|
11842
13198
|
});
|
|
11843
13199
|
return resolveTelemetry(args.telemetry).log(WorkflowEffectStateReported, {
|
|
11844
|
-
...definitionHashFragment(instance.pinnedContentHash),
|
|
13200
|
+
...definitionHashFragment(settled.instance.pinnedContentHash),
|
|
11845
13201
|
instanceId: instanceId,
|
|
11846
13202
|
effect: report.effect,
|
|
11847
|
-
cascaded: cascaded
|
|
11848
|
-
}), {
|
|
11849
|
-
|
|
11850
|
-
cascaded: cascaded,
|
|
13203
|
+
cascaded: settled.cascaded
|
|
13204
|
+
}), settledResult({
|
|
13205
|
+
...settled,
|
|
11851
13206
|
changed: !0
|
|
11852
|
-
};
|
|
13207
|
+
});
|
|
11853
13208
|
}
|
|
11854
13209
|
});
|
|
11855
13210
|
},
|
|
@@ -11862,9 +13217,10 @@ const workflow = {
|
|
|
11862
13217
|
await assertInstanceWriteAllowed({
|
|
11863
13218
|
instance: current,
|
|
11864
13219
|
guards: guards,
|
|
11865
|
-
identity: lakePrincipalId(access)
|
|
13220
|
+
identity: lakePrincipalId(access),
|
|
13221
|
+
dereference: clientGuardDereference(client)
|
|
11866
13222
|
});
|
|
11867
|
-
const
|
|
13223
|
+
const settled = await cascadeAndReload({
|
|
11868
13224
|
client: client,
|
|
11869
13225
|
tag: tag,
|
|
11870
13226
|
instanceId: instanceId,
|
|
@@ -11874,21 +13230,20 @@ const workflow = {
|
|
|
11874
13230
|
telemetry: args.telemetry
|
|
11875
13231
|
});
|
|
11876
13232
|
return resolveTelemetry(args.telemetry).log(WorkflowInstanceTicked, {
|
|
11877
|
-
...definitionHashFragment(instance.pinnedContentHash),
|
|
13233
|
+
...definitionHashFragment(settled.instance.pinnedContentHash),
|
|
11878
13234
|
instanceId: instanceId,
|
|
11879
|
-
cascaded: cascaded
|
|
11880
|
-
}), {
|
|
11881
|
-
|
|
11882
|
-
|
|
11883
|
-
|
|
11884
|
-
};
|
|
13235
|
+
cascaded: settled.cascaded
|
|
13236
|
+
}), settledResult({
|
|
13237
|
+
...settled,
|
|
13238
|
+
changed: settled.instance._rev !== current._rev
|
|
13239
|
+
});
|
|
11885
13240
|
},
|
|
11886
13241
|
setStage: async rawArgs => {
|
|
11887
13242
|
const args = taggedScope(rawArgs, REQUEST_TAG.setStage), {client: client, tag: tag, instanceId: instanceId, targetStage: targetStage, reason: reason, executionContext: executionContext} = args;
|
|
11888
13243
|
return runCommitVerb({
|
|
11889
13244
|
args: args,
|
|
11890
13245
|
op: "setStage",
|
|
11891
|
-
run: async ({actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, record: record, before: before}) => {
|
|
13246
|
+
run: async ({actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, now: now, record: record, before: before}) => {
|
|
11892
13247
|
const result = await setStage({
|
|
11893
13248
|
client: client,
|
|
11894
13249
|
instanceId: instanceId,
|
|
@@ -11908,34 +13263,29 @@ const workflow = {
|
|
|
11908
13263
|
executionContext: executionContext,
|
|
11909
13264
|
telemetry: args.telemetry
|
|
11910
13265
|
})
|
|
11911
|
-
})
|
|
13266
|
+
});
|
|
13267
|
+
return resolveTelemetry(args.telemetry).log(WorkflowStageSet, {
|
|
13268
|
+
...definitionHashFragment(before.pinnedContentHash),
|
|
13269
|
+
instanceId: instanceId,
|
|
13270
|
+
changed: result.fired
|
|
13271
|
+
}), adminOverrideResult({
|
|
11912
13272
|
client: client,
|
|
13273
|
+
tag: tag,
|
|
11913
13274
|
instanceId: instanceId,
|
|
13275
|
+
fired: result.fired,
|
|
11914
13276
|
actor: actor,
|
|
11915
13277
|
clientForGdr: clientForGdr,
|
|
11916
13278
|
refSurface: refSurface,
|
|
11917
13279
|
memberRolesLoader: memberRolesLoader,
|
|
11918
13280
|
clock: clock,
|
|
13281
|
+
now: now,
|
|
11919
13282
|
...executionContext !== void 0 ? {
|
|
11920
13283
|
executionContext: executionContext
|
|
11921
13284
|
} : {},
|
|
11922
13285
|
...args.telemetry !== void 0 ? {
|
|
11923
13286
|
telemetry: args.telemetry
|
|
11924
13287
|
} : {}
|
|
11925
|
-
})
|
|
11926
|
-
return resolveTelemetry(args.telemetry).log(WorkflowStageSet, {
|
|
11927
|
-
...definitionHashFragment(before.pinnedContentHash),
|
|
11928
|
-
instanceId: instanceId,
|
|
11929
|
-
changed: result.fired
|
|
11930
|
-
}), {
|
|
11931
|
-
instance: await reload({
|
|
11932
|
-
client: client,
|
|
11933
|
-
instanceId: instanceId,
|
|
11934
|
-
tag: tag
|
|
11935
|
-
}),
|
|
11936
|
-
cascaded: cascaded,
|
|
11937
|
-
changed: result.fired
|
|
11938
|
-
};
|
|
13288
|
+
});
|
|
11939
13289
|
}
|
|
11940
13290
|
});
|
|
11941
13291
|
},
|
|
@@ -11968,7 +13318,7 @@ const workflow = {
|
|
|
11968
13318
|
...definitionHashFragment(before.pinnedContentHash),
|
|
11969
13319
|
instanceId: instanceId,
|
|
11970
13320
|
changed: changed
|
|
11971
|
-
}), {
|
|
13321
|
+
}), settledResult({
|
|
11972
13322
|
instance: await reload({
|
|
11973
13323
|
client: client,
|
|
11974
13324
|
instanceId: instanceId,
|
|
@@ -11976,7 +13326,7 @@ const workflow = {
|
|
|
11976
13326
|
}),
|
|
11977
13327
|
cascaded: 0,
|
|
11978
13328
|
changed: changed
|
|
11979
|
-
};
|
|
13329
|
+
});
|
|
11980
13330
|
}
|
|
11981
13331
|
});
|
|
11982
13332
|
},
|
|
@@ -11986,7 +13336,7 @@ const workflow = {
|
|
|
11986
13336
|
return runCommitVerb({
|
|
11987
13337
|
args: args,
|
|
11988
13338
|
op: "resetActivity",
|
|
11989
|
-
run: async ({actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, record: record, before: before}) => {
|
|
13339
|
+
run: async ({actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, now: now, record: record, before: before}) => {
|
|
11990
13340
|
const result = await resetActivity({
|
|
11991
13341
|
client: client,
|
|
11992
13342
|
instanceId: instanceId,
|
|
@@ -12004,34 +13354,29 @@ const workflow = {
|
|
|
12004
13354
|
executionContext: executionContext,
|
|
12005
13355
|
telemetry: args.telemetry
|
|
12006
13356
|
})
|
|
12007
|
-
})
|
|
13357
|
+
});
|
|
13358
|
+
return resolveTelemetry(args.telemetry).log(WorkflowActivityReset, {
|
|
13359
|
+
...definitionHashFragment(before.pinnedContentHash),
|
|
13360
|
+
instanceId: instanceId,
|
|
13361
|
+
changed: result.fired
|
|
13362
|
+
}), adminOverrideResult({
|
|
12008
13363
|
client: client,
|
|
13364
|
+
tag: tag,
|
|
12009
13365
|
instanceId: instanceId,
|
|
13366
|
+
fired: result.fired,
|
|
12010
13367
|
actor: actor,
|
|
12011
13368
|
clientForGdr: clientForGdr,
|
|
12012
13369
|
refSurface: refSurface,
|
|
12013
13370
|
memberRolesLoader: memberRolesLoader,
|
|
12014
13371
|
clock: clock,
|
|
13372
|
+
now: now,
|
|
12015
13373
|
...executionContext !== void 0 ? {
|
|
12016
13374
|
executionContext: executionContext
|
|
12017
13375
|
} : {},
|
|
12018
13376
|
...args.telemetry !== void 0 ? {
|
|
12019
13377
|
telemetry: args.telemetry
|
|
12020
13378
|
} : {}
|
|
12021
|
-
})
|
|
12022
|
-
return resolveTelemetry(args.telemetry).log(WorkflowActivityReset, {
|
|
12023
|
-
...definitionHashFragment(before.pinnedContentHash),
|
|
12024
|
-
instanceId: instanceId,
|
|
12025
|
-
changed: result.fired
|
|
12026
|
-
}), {
|
|
12027
|
-
instance: await reload({
|
|
12028
|
-
client: client,
|
|
12029
|
-
instanceId: instanceId,
|
|
12030
|
-
tag: tag
|
|
12031
|
-
}),
|
|
12032
|
-
cascaded: cascaded,
|
|
12033
|
-
changed: result.fired
|
|
12034
|
-
};
|
|
13379
|
+
});
|
|
12035
13380
|
}
|
|
12036
13381
|
});
|
|
12037
13382
|
},
|
|
@@ -12364,8 +13709,8 @@ async function startFreshInstance(args) {
|
|
|
12364
13709
|
extraHistory: fieldDiscards
|
|
12365
13710
|
} : {}
|
|
12366
13711
|
});
|
|
12367
|
-
await client.create(base, SYNC_COMMIT);
|
|
12368
|
-
const
|
|
13712
|
+
await client.create(instanceDocumentForWrite(base), SYNC_COMMIT);
|
|
13713
|
+
const settled = await settleStart({
|
|
12369
13714
|
client: client,
|
|
12370
13715
|
tag: tag,
|
|
12371
13716
|
instanceId: id,
|
|
@@ -12387,15 +13732,15 @@ async function startFreshInstance(args) {
|
|
|
12387
13732
|
viaSpawn: !1
|
|
12388
13733
|
}))
|
|
12389
13734
|
});
|
|
12390
|
-
return {
|
|
13735
|
+
return settledResult({
|
|
13736
|
+
...settled,
|
|
12391
13737
|
instance: await reload({
|
|
12392
13738
|
client: client,
|
|
12393
13739
|
instanceId: id,
|
|
12394
13740
|
tag: tag
|
|
12395
13741
|
}),
|
|
12396
|
-
cascaded: cascaded,
|
|
12397
13742
|
changed: !0
|
|
12398
|
-
};
|
|
13743
|
+
});
|
|
12399
13744
|
}
|
|
12400
13745
|
|
|
12401
13746
|
function assertInitialStageExists(definition) {
|
|
@@ -12492,7 +13837,7 @@ function pendingEntry(args) {
|
|
|
12492
13837
|
}
|
|
12493
13838
|
|
|
12494
13839
|
function createEffectCommitQueue(args) {
|
|
12495
|
-
const {effectKey: effectKey, claimToken: claimToken, commit: commit} = args, pending = [];
|
|
13840
|
+
const {effectKey: effectKey, claimToken: claimToken, attempt: attempt, commit: commit} = args, pending = [];
|
|
12496
13841
|
let admitted = 0, dead, closed = !1, tail = Promise.resolve();
|
|
12497
13842
|
function assertOpen() {
|
|
12498
13843
|
if (dead !== void 0) throw dead;
|
|
@@ -12547,7 +13892,7 @@ function createEffectCommitQueue(args) {
|
|
|
12547
13892
|
value: entry.latestValue
|
|
12548
13893
|
}
|
|
12549
13894
|
} ],
|
|
12550
|
-
idempotencyKey: `effect:${effectKey}:${claimToken}:progress:${coalesceKey}:${admitted}`,
|
|
13895
|
+
idempotencyKey: `effect:${effectKey}:${claimToken}:${attempt}:progress:${coalesceKey}:${admitted}`,
|
|
12551
13896
|
coalesceKey: coalesceKey,
|
|
12552
13897
|
latestValue: value
|
|
12553
13898
|
});
|
|
@@ -12557,6 +13902,29 @@ function createEffectCommitQueue(args) {
|
|
|
12557
13902
|
};
|
|
12558
13903
|
}
|
|
12559
13904
|
|
|
13905
|
+
function effectRetryPolicy(definition, effectName) {
|
|
13906
|
+
return findEffect(definition, effectName)?.retry;
|
|
13907
|
+
}
|
|
13908
|
+
|
|
13909
|
+
function retryWindowClosed(policy, elapsedMs) {
|
|
13910
|
+
return policy.expiryMs !== void 0 && elapsedMs >= policy.expiryMs;
|
|
13911
|
+
}
|
|
13912
|
+
|
|
13913
|
+
function nextRetryDelayMs(policy, at) {
|
|
13914
|
+
if (at.attempt >= policy.attempts) return;
|
|
13915
|
+
const delay = effectBackoffMs(policy, at.attempt);
|
|
13916
|
+
return retryWindowClosed(policy, at.elapsedMs + delay) ? void 0 : delay;
|
|
13917
|
+
}
|
|
13918
|
+
|
|
13919
|
+
function retryFailedDetail(args) {
|
|
13920
|
+
const window = args.windowClosed ? ", retry window expired" : "";
|
|
13921
|
+
return `failed after ${args.at.attempt} of ${args.attempts} attempts${window}: ${args.error.message}`;
|
|
13922
|
+
}
|
|
13923
|
+
|
|
13924
|
+
function retrySucceededDetail(args) {
|
|
13925
|
+
if (args.at.attempt !== 1) return `succeeded on attempt ${args.at.attempt} of ${args.attempts}`;
|
|
13926
|
+
}
|
|
13927
|
+
|
|
12560
13928
|
class MissingHandlerError extends WorkflowError {
|
|
12561
13929
|
info;
|
|
12562
13930
|
constructor(args) {
|
|
@@ -12570,7 +13938,7 @@ function missingHandlerMessage(info) {
|
|
|
12570
13938
|
}
|
|
12571
13939
|
|
|
12572
13940
|
async function verifyDeployedDefinitionsInternal(args) {
|
|
12573
|
-
const {tag: tag,
|
|
13941
|
+
const {tag: tag, handlers: handlers, missingHandler: missingHandler, logger: logger} = args, client = withRequestTag(args.client, REQUEST_TAG.verifyDefinitions);
|
|
12574
13942
|
validateTag(tag);
|
|
12575
13943
|
const log = logger("verifyDeployedDefinitions"), definitions = (await client.fetch(definitionsListGroq("asc"), {
|
|
12576
13944
|
tag: tag
|
|
@@ -12580,7 +13948,7 @@ async function verifyDeployedDefinitionsInternal(args) {
|
|
|
12580
13948
|
version: def.version,
|
|
12581
13949
|
_id: def._id
|
|
12582
13950
|
}), walkEffectNames(def, (name, location) => {
|
|
12583
|
-
if (
|
|
13951
|
+
if (handlers[name] !== void 0) return;
|
|
12584
13952
|
const key = `${name}@@${def._id}`;
|
|
12585
13953
|
let bucket = missingByName.get(key);
|
|
12586
13954
|
bucket === void 0 && (bucket = {
|
|
@@ -12639,7 +14007,7 @@ function isCancelledCompletion(data) {
|
|
|
12639
14007
|
}
|
|
12640
14008
|
|
|
12641
14009
|
async function drainEffectsInternal(args) {
|
|
12642
|
-
const {tag: tag, workflowResource: workflowResource, instanceId: instanceId,
|
|
14010
|
+
const {tag: tag, workflowResource: workflowResource, instanceId: instanceId, handlers: handlers, missingHandler: missingHandler, logger: logger, handlerClient: rawHandlerClient, handlerResourceClients: rawHandlerResourceClients} = 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, sleep = args.sleep ?? wallSleeper, handlerClient = effectHandlerClient(rawHandlerClient), handlerResourceClients = effectHandlerResolver(rawHandlerResourceClients), routeGdr = buildClientForGdr({
|
|
12643
14011
|
client: handlerClient,
|
|
12644
14012
|
workflowResource: workflowResource,
|
|
12645
14013
|
resourceClients: handlerResourceClients
|
|
@@ -12659,12 +14027,13 @@ async function drainEffectsInternal(args) {
|
|
|
12659
14027
|
resourceClients: resourceClients
|
|
12660
14028
|
} : {},
|
|
12661
14029
|
instanceId: instanceId,
|
|
12662
|
-
|
|
14030
|
+
handlers: handlers,
|
|
12663
14031
|
missingHandler: missingHandler,
|
|
12664
14032
|
logger: logger,
|
|
12665
14033
|
log: log,
|
|
12666
14034
|
leaseMs: leaseMs,
|
|
12667
14035
|
clock: clock,
|
|
14036
|
+
sleep: sleep,
|
|
12668
14037
|
...executionContext !== void 0 ? {
|
|
12669
14038
|
executionContext: executionContext
|
|
12670
14039
|
} : {},
|
|
@@ -12672,14 +14041,14 @@ async function drainEffectsInternal(args) {
|
|
|
12672
14041
|
drainerActor: drainerActor,
|
|
12673
14042
|
clientFor: clientFor,
|
|
12674
14043
|
buckets: buckets,
|
|
12675
|
-
|
|
14044
|
+
handledKeys: /* @__PURE__ */ new Set
|
|
12676
14045
|
};
|
|
12677
14046
|
for (;await drainOneCandidate(pass); ) ;
|
|
12678
14047
|
return buckets;
|
|
12679
14048
|
}
|
|
12680
14049
|
|
|
12681
14050
|
async function drainOneCandidate(pass) {
|
|
12682
|
-
const {client: client, tag: tag, instanceId: instanceId, buckets: buckets,
|
|
14051
|
+
const {client: client, tag: tag, instanceId: instanceId, buckets: buckets, handledKeys: handledKeys} = pass, now = pass.clock(), before = await reload({
|
|
12683
14052
|
client: client,
|
|
12684
14053
|
instanceId: instanceId,
|
|
12685
14054
|
tag: tag
|
|
@@ -12688,16 +14057,16 @@ async function drainOneCandidate(pass) {
|
|
|
12688
14057
|
const candidate = findClaimableCandidate({
|
|
12689
14058
|
instance: before,
|
|
12690
14059
|
now: now,
|
|
12691
|
-
|
|
14060
|
+
handledKeys: handledKeys
|
|
12692
14061
|
});
|
|
12693
14062
|
if (candidate === void 0) return !1;
|
|
12694
|
-
const handler = pass.
|
|
14063
|
+
const handler = pass.handlers[candidate.name];
|
|
12695
14064
|
if (handler === void 0) return await assertSkippableOrThrow({
|
|
12696
14065
|
missingHandler: pass.missingHandler,
|
|
12697
14066
|
candidate: candidate,
|
|
12698
14067
|
instanceId: instanceId,
|
|
12699
14068
|
log: pass.log
|
|
12700
|
-
}), buckets.skipped.push(candidate),
|
|
14069
|
+
}), buckets.skipped.push(candidate), handledKeys.add(candidate._key), !0;
|
|
12701
14070
|
const claimed = await claimPendingEffect({
|
|
12702
14071
|
client: client,
|
|
12703
14072
|
instance: before,
|
|
@@ -12710,11 +14079,15 @@ async function drainOneCandidate(pass) {
|
|
|
12710
14079
|
} : {}
|
|
12711
14080
|
});
|
|
12712
14081
|
if (claimed === void 0) return !0;
|
|
12713
|
-
const outcome = await dispatchAndReport({
|
|
14082
|
+
const retry = effectRetryPolicy(parseDefinitionSnapshot(before), candidate.name), outcome = await dispatchAndReport({
|
|
12714
14083
|
handler: handler,
|
|
12715
14084
|
candidate: candidate,
|
|
12716
14085
|
claimToken: claimed.claimToken,
|
|
14086
|
+
...retry !== void 0 ? {
|
|
14087
|
+
retry: retry
|
|
14088
|
+
} : {},
|
|
12717
14089
|
leaseMs: pass.leaseMs,
|
|
14090
|
+
sleep: pass.sleep,
|
|
12718
14091
|
client: client,
|
|
12719
14092
|
handlerClient: pass.handlerClient,
|
|
12720
14093
|
tag: tag,
|
|
@@ -12732,50 +14105,80 @@ async function drainOneCandidate(pass) {
|
|
|
12732
14105
|
telemetry: pass.telemetry,
|
|
12733
14106
|
clock: pass.clock
|
|
12734
14107
|
});
|
|
12735
|
-
return buckets[outcome].push(candidate),
|
|
14108
|
+
return buckets[outcome].push(candidate), outcome === "lost" && handledKeys.add(candidate._key),
|
|
14109
|
+
!0;
|
|
12736
14110
|
}
|
|
12737
14111
|
|
|
12738
|
-
function findClaimableCandidate({instance: instance, now: now,
|
|
12739
|
-
return instance.pendingEffects.find(e => (e.claim === void 0 || isClaimExpired(e.claim, now)) && !
|
|
14112
|
+
function findClaimableCandidate({instance: instance, now: now, handledKeys: handledKeys}) {
|
|
14113
|
+
return instance.pendingEffects.find(e => (e.claim === void 0 || isClaimExpired(e.claim, now)) && !handledKeys.has(e._key));
|
|
12740
14114
|
}
|
|
12741
14115
|
|
|
12742
14116
|
async function dispatchAndReport(args) {
|
|
12743
|
-
const {handler: handler, candidate: candidate, claimToken: claimToken, leaseMs: leaseMs, 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,
|
|
12744
|
-
effectKey: candidate._key,
|
|
12745
|
-
claimToken: claimToken,
|
|
12746
|
-
commit: async req => {
|
|
12747
|
-
await workflow.commitEffectOps({
|
|
12748
|
-
client: client,
|
|
12749
|
-
tag: tag,
|
|
12750
|
-
workflowResource: workflowResource,
|
|
12751
|
-
...resourceClients !== void 0 ? {
|
|
12752
|
-
resourceClients: resourceClients
|
|
12753
|
-
} : {},
|
|
12754
|
-
...executionContext !== void 0 ? {
|
|
12755
|
-
executionContext: executionContext
|
|
12756
|
-
} : {},
|
|
12757
|
-
...telemetry !== void 0 ? {
|
|
12758
|
-
telemetry: telemetry
|
|
12759
|
-
} : {},
|
|
12760
|
-
instanceId: instanceId,
|
|
12761
|
-
effectKey: candidate._key,
|
|
12762
|
-
claimToken: claimToken,
|
|
12763
|
-
ops: req.ops,
|
|
12764
|
-
idempotencyKey: req.idempotencyKey,
|
|
12765
|
-
leaseMs: leaseMs,
|
|
12766
|
-
clock: clock
|
|
12767
|
-
});
|
|
12768
|
-
}
|
|
12769
|
-
}), {outputs: outputs, ops: ops, dispatchError: dispatchError} = await dispatchEffect({
|
|
14117
|
+
const {handler: handler, candidate: candidate, claimToken: claimToken, retry: retry, leaseMs: leaseMs, sleep: sleep, 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, attemptOnce = attempt => dispatchEffect({
|
|
12770
14118
|
handler: handler,
|
|
12771
14119
|
candidate: candidate,
|
|
12772
|
-
queue:
|
|
14120
|
+
queue: createEffectCommitQueue({
|
|
14121
|
+
effectKey: candidate._key,
|
|
14122
|
+
claimToken: claimToken,
|
|
14123
|
+
attempt: attempt,
|
|
14124
|
+
commit: async req => {
|
|
14125
|
+
await workflow.commitEffectOps({
|
|
14126
|
+
client: client,
|
|
14127
|
+
tag: tag,
|
|
14128
|
+
workflowResource: workflowResource,
|
|
14129
|
+
...resourceClients !== void 0 ? {
|
|
14130
|
+
resourceClients: resourceClients
|
|
14131
|
+
} : {},
|
|
14132
|
+
...executionContext !== void 0 ? {
|
|
14133
|
+
executionContext: executionContext
|
|
14134
|
+
} : {},
|
|
14135
|
+
...telemetry !== void 0 ? {
|
|
14136
|
+
telemetry: telemetry
|
|
14137
|
+
} : {},
|
|
14138
|
+
instanceId: instanceId,
|
|
14139
|
+
effectKey: candidate._key,
|
|
14140
|
+
claimToken: claimToken,
|
|
14141
|
+
ops: req.ops,
|
|
14142
|
+
idempotencyKey: req.idempotencyKey,
|
|
14143
|
+
leaseMs: leaseMs,
|
|
14144
|
+
clock: clock
|
|
14145
|
+
});
|
|
14146
|
+
}
|
|
14147
|
+
}),
|
|
12773
14148
|
ctx: {
|
|
12774
14149
|
client: handlerClient,
|
|
12775
14150
|
clientFor: clientFor,
|
|
12776
14151
|
instanceId: instanceId,
|
|
12777
14152
|
logger: logger
|
|
12778
14153
|
}
|
|
14154
|
+
}), claimed = {
|
|
14155
|
+
client: client,
|
|
14156
|
+
tag: tag,
|
|
14157
|
+
instanceId: instanceId,
|
|
14158
|
+
effectKey: candidate._key,
|
|
14159
|
+
claimToken: claimToken
|
|
14160
|
+
}, run = await runRetryPolicy({
|
|
14161
|
+
attemptOnce: attemptOnce,
|
|
14162
|
+
...retry !== void 0 ? {
|
|
14163
|
+
retry: retry
|
|
14164
|
+
} : {},
|
|
14165
|
+
clock: clock,
|
|
14166
|
+
sleep: sleep,
|
|
14167
|
+
holdThrough: holdMs => renewClaimLease({
|
|
14168
|
+
...claimed,
|
|
14169
|
+
holdMs: holdMs,
|
|
14170
|
+
clock: clock
|
|
14171
|
+
}),
|
|
14172
|
+
stillHeld: () => claimStillHeld(claimed),
|
|
14173
|
+
leaseMs: leaseMs
|
|
14174
|
+
});
|
|
14175
|
+
if (run.abandoned !== void 0) return log.warn(run.abandoned === "lease" ? `Effect "${candidate.name}" could not hold its claim between retry attempts — the run stopped with attempts left` : `Effect "${candidate.name}" was settled by another party during a retry backoff — the run stopped without dispatching again`, {
|
|
14176
|
+
effectKey: candidate._key,
|
|
14177
|
+
instanceId: instanceId
|
|
14178
|
+
}), "lost";
|
|
14179
|
+
const {outputs: outputs, ops: ops, dispatchError: dispatchError} = run.dispatch, detail = retry === void 0 ? void 0 : completionDetail({
|
|
14180
|
+
run: run,
|
|
14181
|
+
retry: retry
|
|
12779
14182
|
});
|
|
12780
14183
|
try {
|
|
12781
14184
|
await reportEffectOutcome({
|
|
@@ -12796,6 +14199,9 @@ async function dispatchAndReport(args) {
|
|
|
12796
14199
|
outputs: outputs,
|
|
12797
14200
|
ops: ops,
|
|
12798
14201
|
dispatchError: dispatchError,
|
|
14202
|
+
...detail !== void 0 ? {
|
|
14203
|
+
detail: detail
|
|
14204
|
+
} : {},
|
|
12799
14205
|
clock: clock
|
|
12800
14206
|
});
|
|
12801
14207
|
} catch (error) {
|
|
@@ -12808,6 +14214,71 @@ async function dispatchAndReport(args) {
|
|
|
12808
14214
|
return dispatchError === void 0 ? "drained" : "failed";
|
|
12809
14215
|
}
|
|
12810
14216
|
|
|
14217
|
+
async function runRetryPolicy(args) {
|
|
14218
|
+
const {attemptOnce: attemptOnce, retry: retry, clock: clock} = args, startedAt = Date.parse(clock()), elapsed = () => Date.parse(clock()) - startedAt;
|
|
14219
|
+
let attempt = 1, dispatch = await attemptOnce(attempt);
|
|
14220
|
+
for (;dispatch.dispatchError !== void 0 && retry !== void 0; ) {
|
|
14221
|
+
const at = {
|
|
14222
|
+
attempt: attempt,
|
|
14223
|
+
elapsedMs: elapsed()
|
|
14224
|
+
}, stop = await waitForNextAttempt({
|
|
14225
|
+
retry: retry,
|
|
14226
|
+
at: at,
|
|
14227
|
+
elapsed: elapsed,
|
|
14228
|
+
pace: args
|
|
14229
|
+
});
|
|
14230
|
+
if (stop !== void 0) return {
|
|
14231
|
+
...stop,
|
|
14232
|
+
dispatch: dispatch,
|
|
14233
|
+
at: at
|
|
14234
|
+
};
|
|
14235
|
+
attempt += 1, dispatch = await attemptOnce(attempt);
|
|
14236
|
+
}
|
|
14237
|
+
return {
|
|
14238
|
+
dispatch: dispatch,
|
|
14239
|
+
at: {
|
|
14240
|
+
attempt: attempt,
|
|
14241
|
+
elapsedMs: elapsed()
|
|
14242
|
+
},
|
|
14243
|
+
windowClosed: !1
|
|
14244
|
+
};
|
|
14245
|
+
}
|
|
14246
|
+
|
|
14247
|
+
async function waitForNextAttempt(args) {
|
|
14248
|
+
const {retry: retry, at: at, elapsed: elapsed} = args, {clock: clock, sleep: sleep, holdThrough: holdThrough, stillHeld: stillHeld, leaseMs: leaseMs} = args.pace, delay = nextRetryDelayMs(retry, at);
|
|
14249
|
+
if (delay === void 0) return {
|
|
14250
|
+
windowClosed: at.attempt < retry.attempts
|
|
14251
|
+
};
|
|
14252
|
+
const heldUntil = await holdThrough(delay + leaseMs);
|
|
14253
|
+
if (heldUntil === void 0 || hasPassed(heldUntil, addMs(clock(), delay))) return {
|
|
14254
|
+
windowClosed: !1,
|
|
14255
|
+
abandoned: "lease"
|
|
14256
|
+
};
|
|
14257
|
+
if (retryWindowClosed(retry, elapsed() + delay)) return {
|
|
14258
|
+
windowClosed: !0
|
|
14259
|
+
};
|
|
14260
|
+
if (await sleep(delay), !await stillHeld()) return {
|
|
14261
|
+
windowClosed: !1,
|
|
14262
|
+
abandoned: "settled"
|
|
14263
|
+
};
|
|
14264
|
+
if (retryWindowClosed(retry, elapsed())) return {
|
|
14265
|
+
windowClosed: !0
|
|
14266
|
+
};
|
|
14267
|
+
}
|
|
14268
|
+
|
|
14269
|
+
function completionDetail(args) {
|
|
14270
|
+
const {run: run, retry: retry} = args, {dispatchError: dispatchError} = run.dispatch;
|
|
14271
|
+
return dispatchError === void 0 ? retrySucceededDetail({
|
|
14272
|
+
at: run.at,
|
|
14273
|
+
attempts: retry.attempts
|
|
14274
|
+
}) : retryFailedDetail({
|
|
14275
|
+
at: run.at,
|
|
14276
|
+
attempts: retry.attempts,
|
|
14277
|
+
error: dispatchError,
|
|
14278
|
+
windowClosed: run.windowClosed
|
|
14279
|
+
});
|
|
14280
|
+
}
|
|
14281
|
+
|
|
12811
14282
|
async function reportEffectOutcome(args) {
|
|
12812
14283
|
const {outputs: outputs, ops: ops, dispatchError: dispatchError, resourceClients: resourceClients, ...rest} = args;
|
|
12813
14284
|
await workflow.completeEffect({
|
|
@@ -13098,8 +14569,12 @@ function createInstanceSession(args) {
|
|
|
13098
14569
|
definition: definitionOf(),
|
|
13099
14570
|
actor: actor,
|
|
13100
14571
|
snapshot: snapshotFrom(held, normalizedSelf),
|
|
14572
|
+
documentAvailability: documentAvailabilityReader({
|
|
14573
|
+
clientForGdr: evalScope.clientForGdr
|
|
14574
|
+
}),
|
|
13101
14575
|
guards: guards,
|
|
13102
14576
|
resourceGrants: resourceGrants,
|
|
14577
|
+
guardDereference: clientGuardDereference(evalScope.client),
|
|
13103
14578
|
...localPrincipalId !== void 0 ? {
|
|
13104
14579
|
localPrincipalId: localPrincipalId
|
|
13105
14580
|
} : {},
|
|
@@ -13114,7 +14589,7 @@ function createInstanceSession(args) {
|
|
|
13114
14589
|
} : {}
|
|
13115
14590
|
});
|
|
13116
14591
|
}, settleAfterApply = async ({actor: actor, held: held, ranOps: ranOps, scope: scope, memberRolesLoader: memberRolesLoader}) => {
|
|
13117
|
-
const
|
|
14592
|
+
const settled = await cascadeHeld({
|
|
13118
14593
|
scope: scope,
|
|
13119
14594
|
actor: actor,
|
|
13120
14595
|
held: held,
|
|
@@ -13124,14 +14599,14 @@ function createInstanceSession(args) {
|
|
|
13124
14599
|
client: scope.client,
|
|
13125
14600
|
instanceId: instance._id,
|
|
13126
14601
|
tag: tag
|
|
13127
|
-
}), {
|
|
14602
|
+
}), settledResult({
|
|
14603
|
+
...settled,
|
|
13128
14604
|
instance: instance,
|
|
13129
|
-
cascaded: cascaded,
|
|
13130
14605
|
changed: !0,
|
|
13131
14606
|
...ranOps !== void 0 ? {
|
|
13132
14607
|
ranOps: ranOps
|
|
13133
14608
|
} : {}
|
|
13134
|
-
};
|
|
14609
|
+
});
|
|
13135
14610
|
}, cascadeHeld = ({scope: scope, actor: actor, held: held, memberRolesLoader: memberRolesLoader}) => cascade({
|
|
13136
14611
|
client: scope.client,
|
|
13137
14612
|
instanceId: instance._id,
|
|
@@ -13242,13 +14717,14 @@ function createInstanceSession(args) {
|
|
|
13242
14717
|
identity: lakePrincipalId({
|
|
13243
14718
|
actor: actor,
|
|
13244
14719
|
localPrincipalId: localPrincipalId
|
|
13245
|
-
})
|
|
14720
|
+
}),
|
|
14721
|
+
dereference: clientGuardDereference(tickScope.client)
|
|
13246
14722
|
});
|
|
13247
14723
|
const before = await reload({
|
|
13248
14724
|
client: tickScope.client,
|
|
13249
14725
|
instanceId: instance._id,
|
|
13250
14726
|
tag: tag
|
|
13251
|
-
}),
|
|
14727
|
+
}), settled = await cascadeHeld({
|
|
13252
14728
|
scope: tickScope,
|
|
13253
14729
|
actor: actor,
|
|
13254
14730
|
held: held,
|
|
@@ -13261,12 +14737,12 @@ function createInstanceSession(args) {
|
|
|
13261
14737
|
}), telemetry.log(WorkflowInstanceTicked, {
|
|
13262
14738
|
...definitionHashFragment(instance.pinnedContentHash),
|
|
13263
14739
|
instanceId: instance._id,
|
|
13264
|
-
cascaded: cascaded
|
|
13265
|
-
}), {
|
|
14740
|
+
cascaded: settled.cascaded
|
|
14741
|
+
}), settledResult({
|
|
14742
|
+
...settled,
|
|
13266
14743
|
instance: instance,
|
|
13267
|
-
cascaded: cascaded,
|
|
13268
14744
|
changed: instance._rev !== before._rev
|
|
13269
|
-
};
|
|
14745
|
+
});
|
|
13270
14746
|
});
|
|
13271
14747
|
},
|
|
13272
14748
|
fireAction({activity: activity, action: action, params: params}) {
|
|
@@ -13416,7 +14892,10 @@ const silentLogger = {
|
|
|
13416
14892
|
function createEngine(args) {
|
|
13417
14893
|
const {workflowResource: workflowResource, clock: clock, tag: tag, executionContext: executionContext} = args, {client: client, resourceClients: resourceClients} = taggedScope(args, REQUEST_TAG.engine);
|
|
13418
14894
|
validateTag(tag);
|
|
13419
|
-
const
|
|
14895
|
+
const effects = {
|
|
14896
|
+
handlers: args.effects?.handlers ?? {},
|
|
14897
|
+
missingHandler: args.effects?.missingHandler ?? "fail"
|
|
14898
|
+
}, logger = args.loggerFactory ?? defaultLoggerFactory, telemetry = resolveTelemetry(args.telemetry), EMPTY_DRAIN_LOG_INTERVAL_MS = 6e4;
|
|
13420
14899
|
let lastEmptyDrainLoggedAt;
|
|
13421
14900
|
const emptyDrainDue = () => {
|
|
13422
14901
|
const now = Date.parse((clock ?? wallClock)());
|
|
@@ -13447,8 +14926,7 @@ function createEngine(args) {
|
|
|
13447
14926
|
client: client,
|
|
13448
14927
|
tag: tag,
|
|
13449
14928
|
workflowResource: workflowResource,
|
|
13450
|
-
|
|
13451
|
-
missingHandler: missingHandler,
|
|
14929
|
+
effects: effects,
|
|
13452
14930
|
logger: logger,
|
|
13453
14931
|
telemetry: telemetry,
|
|
13454
14932
|
resolveActor: rest => resolveClientActor(client, rest),
|
|
@@ -13502,12 +14980,14 @@ function createEngine(args) {
|
|
|
13502
14980
|
workflowResource: workflowResource,
|
|
13503
14981
|
...optionalScope,
|
|
13504
14982
|
instanceId: instanceId,
|
|
13505
|
-
|
|
13506
|
-
missingHandler: missingHandler,
|
|
14983
|
+
...effects,
|
|
13507
14984
|
logger: logger,
|
|
13508
14985
|
telemetry: telemetry,
|
|
13509
|
-
...args.
|
|
13510
|
-
leaseMs: args.
|
|
14986
|
+
...args.effects?.leaseMs !== void 0 ? {
|
|
14987
|
+
leaseMs: args.effects.leaseMs
|
|
14988
|
+
} : {},
|
|
14989
|
+
...args.sleep !== void 0 ? {
|
|
14990
|
+
sleep: args.sleep
|
|
13511
14991
|
} : {}
|
|
13512
14992
|
}), drained = result.drained;
|
|
13513
14993
|
return (drained.length > 0 || emptyDrainDue()) && telemetry.log(WorkflowEffectsDrained, {
|
|
@@ -13520,8 +15000,7 @@ function createEngine(args) {
|
|
|
13520
15000
|
verifyDeployedDefinitions: () => verifyDeployedDefinitionsInternal({
|
|
13521
15001
|
client: client,
|
|
13522
15002
|
tag: tag,
|
|
13523
|
-
|
|
13524
|
-
missingHandler: missingHandler,
|
|
15003
|
+
...effects,
|
|
13525
15004
|
logger: logger
|
|
13526
15005
|
})
|
|
13527
15006
|
};
|
|
@@ -13530,27 +15009,43 @@ function createEngine(args) {
|
|
|
13530
15009
|
const CONSENT_TAG = "telemetry.consent", BATCH_TAG = "telemetry.batch";
|
|
13531
15010
|
|
|
13532
15011
|
function createTelemetryIntake(args) {
|
|
13533
|
-
const {client: client, projectId: projectId, denied: denied = !1} = args;
|
|
15012
|
+
const {client: client, projectId: projectId, denied: denied = !1, context: context} = args;
|
|
13534
15013
|
let cachedConsent;
|
|
13535
15014
|
return {
|
|
13536
15015
|
resolveConsent: async () => denied ? {
|
|
13537
15016
|
status: "denied"
|
|
13538
15017
|
} : (cachedConsent !== void 0 || (cachedConsent = await client.request({
|
|
13539
|
-
|
|
15018
|
+
url: "/intake/telemetry-status",
|
|
13540
15019
|
tag: CONSENT_TAG
|
|
13541
15020
|
})), cachedConsent),
|
|
13542
15021
|
sendEvents: batch => client.request({
|
|
13543
|
-
|
|
15022
|
+
url: "/intake/batch",
|
|
13544
15023
|
method: "POST",
|
|
13545
15024
|
body: {
|
|
13546
15025
|
projectId: projectId,
|
|
13547
|
-
batch: batch
|
|
15026
|
+
batch: context === void 0 ? batch : batch.map(event => withContext(event, context))
|
|
13548
15027
|
},
|
|
13549
15028
|
tag: BATCH_TAG
|
|
13550
15029
|
})
|
|
13551
15030
|
};
|
|
13552
15031
|
}
|
|
13553
15032
|
|
|
15033
|
+
function withContext(event, context) {
|
|
15034
|
+
if (!isRecord(event)) return event;
|
|
15035
|
+
const existing = isRecord(event.context) ? event.context : {};
|
|
15036
|
+
return {
|
|
15037
|
+
...event,
|
|
15038
|
+
context: {
|
|
15039
|
+
...existing,
|
|
15040
|
+
...context
|
|
15041
|
+
}
|
|
15042
|
+
};
|
|
15043
|
+
}
|
|
15044
|
+
|
|
15045
|
+
function _resolveTelemetryEnvironment(nodeEnv, fallback) {
|
|
15046
|
+
return nodeEnv === "production" ? "production" : nodeEnv === "development" || nodeEnv === "test" ? "development" : fallback;
|
|
15047
|
+
}
|
|
15048
|
+
|
|
13554
15049
|
function isTelemetryEnvDenied(env) {
|
|
13555
15050
|
return isTrueish(env.CI) || isTrueish(env.DO_NOT_TRACK);
|
|
13556
15051
|
}
|
|
@@ -13573,6 +15068,16 @@ function isTrueish(value) {
|
|
|
13573
15068
|
return normalized !== "0" && normalized !== "false";
|
|
13574
15069
|
}
|
|
13575
15070
|
|
|
15071
|
+
function summary(documents, deleted) {
|
|
15072
|
+
if (documents.length === 0) return "";
|
|
15073
|
+
const titles = [ ...new Set(documents.map(document => document.title ?? document.target.field)) ].map(title => `“${title}”`).join(", ");
|
|
15074
|
+
return documents.length === 1 ? `A document selected for ${titles} ${deleted ? "has been deleted" : "can’t be read by this workflow"}` : `${documents.length} documents selected for ${titles} ${deleted ? "have been deleted" : "can’t be read by this workflow"}`;
|
|
15075
|
+
}
|
|
15076
|
+
|
|
15077
|
+
function _missingDocumentsSummary(documents) {
|
|
15078
|
+
return [ summary(documents.filter(document => document.availability === "deleted"), !0), summary(documents.filter(document => document.availability !== "deleted"), !1) ].filter(Boolean).join(". ");
|
|
15079
|
+
}
|
|
15080
|
+
|
|
13576
15081
|
function groupSitesOf(definition) {
|
|
13577
15082
|
const sites = [], workflow2 = declarationIndex({
|
|
13578
15083
|
groups: definition.groups,
|
|
@@ -14060,4 +15565,4 @@ function displayDescription(typeKey) {
|
|
|
14060
15565
|
if (typeKey) return DISPLAY[typeKey]?.description;
|
|
14061
15566
|
}
|
|
14062
15567
|
|
|
14063
|
-
export { ACTION_SEMANTICS, ACTIVITY_KINDS, ACTIVITY_KIND_DISPLAY, ACTOR_KINDS, ANONYMOUS_IDENTITY, AUTHORING_DISPLAY, ActionDisabledError, ActionParamsInvalidError, CONDITION_VARS, CONTEXT_ENTRY_DISPLAY, CascadeLimitError, ConcurrentCommitEffectOpsError, ConcurrentCompleteEffectError, ConcurrentEditFieldError, ConcurrentFireActionError, ContractViolationError, DATA_MODEL_CHANGES, DATA_MODEL_MAX_READER, DATA_MODEL_MIN_READER, DATA_MODEL_VERSION, DECISION_SEMANTICS, DEFAULT_CONTENT_PERSPECTIVE, DEFAULT_EFFECT_LEASE_MS, DEFAULT_IDEMPOTENCY_TTL_MS, DEFAULT_TRANSITION_WHEN, DISPLAY, DRIVER_KINDS, DRIVER_KIND_DISPLAY, DefinitionInUseError, DefinitionNotFoundError, EFFECT_COMMIT_DISPATCH_CAP, EFFECT_COMMIT_QUEUE_DEPTH, ENGINE_API_VERSION, EXECUTION_KINDS, EXECUTOR_CLASSIFICATIONS, EXECUTOR_CLASSIFICATION_DISPLAY, EditFieldDeniedError, EffectCommitQueueOverflowError, 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, NEUTRAL_MARK, OP_DISPLAY, OUTCOME_MARKS, PartialGuardDeployError, PersistedDocShapeError, READER_MODEL_ROLLOUT_URL, RESERVED_CONDITION_VARS, ReaderModelAcknowledgementError, RefResourceUndeclaredError, RequiredFieldNotProvidedError, SIGNAL_SEMANTICS, START_FILTER_VARS, START_REQUIREMENT_VARS, SYSTEM_IDENTITY, SpawnContractsInvalidError, StaleEffectClaimError, StartNotAllowedError, StartNotPrimedError, StartNotSettledError, WORKFLOW_DEFINITION_TYPE, WORKFLOW_INSTANCE_TYPE, WorkflowActionFired, WorkflowActivityReset, WorkflowDefinitionDeleted, WorkflowDefinitionDeployed, WorkflowEffectCompleted, WorkflowEffectStateReported, WorkflowEffectsDrained, WorkflowError, WorkflowFieldEdited, WorkflowInstanceAborted, WorkflowInstancePreviewSchema, WorkflowInstanceSchema, WorkflowInstanceStarted, WorkflowInstanceTicked, WorkflowStageSet, WorkflowStageTransitioned, WorkflowStateDivergedError, abortReason, acceptsDocumentType, aclPathForResource, actionDisabledDetail, actionRendering, actionVerdict, activityAutonomyOf, actorFulfillsRole, analyzeCondition2 as analyzeCondition, applicableDefinitions, assertReadableModel, assertReaderModelAcknowledgement, atomReadsDataset2 as atomReadsDataset, autonomySummary, availableActions, buildInitialFields, buildSnapshot, checklistLines, classifyPrincipalId, clientConfigFromResource, clientProjectUserDirectory,
|
|
15568
|
+
export { ACTION_SEMANTICS, ACTIVITY_KINDS, ACTIVITY_KIND_DISPLAY, ACTOR_KINDS, ANONYMOUS_IDENTITY, AUTHORING_DISPLAY, ActionDisabledError, ActionParamsInvalidError, CONDITION_VARS, CONTEXT_ENTRY_DISPLAY, CascadeLimitError, ConcurrentCascadeError, ConcurrentCommitEffectOpsError, ConcurrentCompleteEffectError, ConcurrentEditFieldError, ConcurrentFireActionError, ContractViolationError, DATA_MODEL_CHANGES, DATA_MODEL_MAX_READER, DATA_MODEL_MIN_READER, DATA_MODEL_VERSION, DECISION_SEMANTICS, DEFAULT_CONTENT_PERSPECTIVE, DEFAULT_EFFECT_LEASE_MS, DEFAULT_IDEMPOTENCY_TTL_MS, DEFAULT_TRANSITION_WHEN, DISPLAY, DOCUMENT_VALUE_PERMISSIONS, DRIVER_KINDS, DRIVER_KIND_DISPLAY, DefinitionInUseError, DefinitionNotFoundError, EFFECT_COMMIT_DISPATCH_CAP, EFFECT_COMMIT_QUEUE_DEPTH, ENGINE_API_VERSION, EXECUTION_KINDS, EXECUTOR_CLASSIFICATIONS, EXECUTOR_CLASSIFICATION_DISPLAY, EditFieldDeniedError, EffectCommitQueueOverflowError, 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, NEUTRAL_MARK, OP_DISPLAY, OUTCOME_MARKS, PartialGuardDeployError, PersistedDocShapeError, READER_MODEL_ROLLOUT_URL, RESERVED_CONDITION_VARS, ReaderModelAcknowledgementError, RefResourceUndeclaredError, RequiredFieldNotProvidedError, SIGNAL_SEMANTICS, START_FILTER_VARS, START_REQUIREMENT_VARS, SYSTEM_IDENTITY, SpawnContractsInvalidError, StaleEffectClaimError, StartNotAllowedError, StartNotPrimedError, StartNotSettledError, WORKFLOW_DEFINITION_TYPE, WORKFLOW_INSTANCE_TYPE, WorkflowActionFired, WorkflowActivityReset, WorkflowDefinitionDeleted, WorkflowDefinitionDeployed, WorkflowEffectCompleted, WorkflowEffectStateReported, WorkflowEffectsDrained, WorkflowError, WorkflowFieldEdited, WorkflowInstanceAborted, WorkflowInstancePreviewSchema, WorkflowInstanceSchema, WorkflowInstanceStarted, WorkflowInstanceTicked, WorkflowStageSet, WorkflowStageTransitioned, WorkflowStateDivergedError, _additionalMissingDocuments, _fieldTargetLabel, _missingDocumentsSummary, _requestProjectMembers, _resolveTelemetryEnvironment, abortReason, acceptsDocumentType, aclPathForResource, actionDisabledDetail, actionRendering, actionVerdict, activeAssignmentMembers, activityAutonomyOf, actorFulfillsRole, actorMatchesAssignment, analyzeCondition2 as analyzeCondition, applicableDefinitions, assertReadableModel, assertReaderModelAcknowledgement, assignmentMatch, assignmentMembers, assignmentPrefilter, assignmentState, assignmentStateCounts, atomReadsDataset2 as atomReadsDataset, autonomySummary, availableActions, buildInitialFields, buildSnapshot, checklistLines, classifyPrincipalId, clientConfigFromResource, clientGuardDereference, clientProjectUserDirectory, compileGuards, computeDiffEntries, conditionFieldReadNames, conditionSitesOf, contentDocQuery, contentDraftFallback, contentReleaseName, contextMap, createEngine, createTelemetryIntake, datasetResourceParts, defaultLoggerFactory, definitionDeployedData, definitionLookupGroq, definitionRoleNames, definitionTagsGroq, definitionsListGroq, deniedGuardLabels, deniedGuardRefs, denyingGuards, deployStageGuards, deployedTagsGroq, deriveActivityKind, deriveExecutorClassification, deriveWorkflowAutonomy, describeAtom, describeAutonomyWait, describeCondition, describeDefinition, describeFieldInsight, describeNode, describeSite, describeSiteHeading, diagnoseInputFromEvaluation, diagnoseInstance, diffEntry, displayDescription, displayTitle, documentActionDenials, documentPrefilter, documentStuckCause, driverKind, effectOutputsMap, effectSites, entryDocRefs, errorMessage, evaluateFromSnapshot, evaluateMutationGuard, evaluateStartFilter, expandResourceAliases, explainCondition2 as explainCondition, explainStartRequirement, extractDocumentId, fieldTreeShape, findActivityNode, findCurrentActivityEntry, findOpenStageEntry, findStageNode, formatRead2 as formatRead, gdrFromResource, gdrRef, gdrUri, groupMembershipNames, groupSitesOf, guardMatches, guardsForDefinition, guardsForInstance, guardsForResource, guillemets2 as guillemets, hasSingleSubjectRequirement, hashDefinitionContent, humanize2 as humanize, identityMatchesAssignment, inFlightFilter, initialFieldIssues, instanceAssignmentStateCounts, instanceChangesQuery, instanceDocId, instanceGuardQuery, instancePreviewsQuery, instanceWatchesDocument, instancesGuardQuery, instancesQuery, isCascadeFired, isClaimExpired, isClientProjectUser, isComparisonOp, isDefinitionApplicable, isFilterScopedOut, isGdr, isInputSourced, isNotesEntry, isProjectUserNotFoundError, isRevisionConflict, isSingleDocRefEntry, isSingleDocRefKind, isStartableDefinition, isSubjectEntry, isTelemetryEnvDenied, isTerminalActivityStatus, isTerminalStage, isTodoListEntry, isTodoListItem, isUnprimed, lakeGuardId, lakePrincipalId, latestDefinitionsGroq, latestDeployedDefinitions, lintEffectOutputs, minReaderModelOf, missingRequiredInputs, modelVersionOf, narrateAutonomyWaits, noopTelemetry, openActivityAssignments, parentRef, parseDefinitionInput, parseDefinitionSnapshot, parseDefinitionSnapshotValue, parseGdr, parseGuardDocument, parseInstanceDocument, parseInstancePreviewDocument, parseResourceGdr, processShellUserProperties, projectStartSliceRow, projectToWatchRef, quoted2 as quoted, readInstanceDoc, readInstancePreviewDoc, readsRaw, readsRootDocument, refCanvas, refDashboard, refDataset, refKindAcceptsTypes, refMediaLibrary, refsOf, rejectedRefTypes, releaseDocId, releaseRef, remediationsFor, requiredDefinitionReaderModel, requiredModelFeatures, requiredReaderModel, resolveAccess, resolveActor, resolveClientActor, resolveFieldEntry$1 as resolveFieldEntry, resolveUserAttributes, resourceAliasesToMap, resourceFromParsed, resourceGdr, retractStageGuards, sameResource, scalarValidationIssues, schemaTreeShape, sentenceCase, silentLogger, singleSubjectRequirementRefused, snapshotGuardDereference, stageAutonomyOf, startFieldsParam, startKindOf, startRefusal, stripSystemFields, subjectDenialLabels, subscriptionDocument, subscriptionDocumentsForInstance, sweepStaleClaims, tagScopeFilter, terminalState, toBareId, tryParseGdr, unboundRequirementReads, unsatisfiedTransitionSummaries, userLoginProvider, validateDefinition, validateTag, verdictGuardsForInstance, wallClock, whatIfCondition2 as whatIfCondition, withAssignment, workflow };
|