@sanity/workflow-engine 0.15.0 → 0.16.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 +36 -0
- package/DATAMODEL.md +73 -4
- package/dist/_chunks-cjs/invariants.cjs +982 -235
- package/dist/_chunks-es/invariants.js +956 -231
- package/dist/define.cjs +3 -608
- package/dist/define.d.cts +95 -409
- package/dist/define.d.ts +95 -409
- package/dist/define.js +2 -607
- package/dist/index.cjs +1877 -1733
- package/dist/index.d.cts +762 -526
- package/dist/index.d.ts +762 -526
- package/dist/index.js +2078 -1979
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -24,20 +24,8 @@ function _interopNamespaceCompat(e) {
|
|
|
24
24
|
|
|
25
25
|
var v__namespace = /* @__PURE__ */ _interopNamespaceCompat(v);
|
|
26
26
|
|
|
27
|
-
function
|
|
28
|
-
return
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function deriveActivityKind(activity) {
|
|
32
|
-
return hasItems(activity.actions) ? "user" : hasItems(activity.effects) ? "service" : activity.completeWhen !== void 0 || activity.subworkflows !== void 0 ? "receive" : "script";
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function activityKind(activity) {
|
|
36
|
-
return activity.kind ?? deriveActivityKind(activity);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function driverKind(actor) {
|
|
40
|
-
return actor.kind === "person" || actor.kind === "agent" ? actor.kind : "service";
|
|
27
|
+
function isFilterScopedOut(entry) {
|
|
28
|
+
return entry.status === "skipped" && entry.startedAt === void 0;
|
|
41
29
|
}
|
|
42
30
|
|
|
43
31
|
function liveSubworkflows(host) {
|
|
@@ -302,7 +290,7 @@ const HIGH_FREQUENCY_SAMPLE_MS = 6e4, WorkflowDefinitionDeployed = defineWorkflo
|
|
|
302
290
|
});
|
|
303
291
|
|
|
304
292
|
function definitionDeployedData(definition, outcome) {
|
|
305
|
-
const stages = definition.stages, activities = stages.flatMap(stage => stage.activities ?? []), actions = activities.flatMap(activity => activity.actions ?? []), transitions = stages.flatMap(stage => stage.transitions ?? []), fieldEntries2 = [ ...definition.fields ?? [], ...stages.flatMap(stage => stage.fields ?? []), ...activities.flatMap(activity => activity.fields ?? []) ]
|
|
293
|
+
const stages = definition.stages, activities = stages.flatMap(stage => stage.activities ?? []), actions = activities.flatMap(activity => activity.actions ?? []), transitions = stages.flatMap(stage => stage.transitions ?? []), fieldEntries2 = [ ...definition.fields ?? [], ...stages.flatMap(stage => stage.fields ?? []), ...activities.flatMap(activity => activity.fields ?? []) ];
|
|
306
294
|
return {
|
|
307
295
|
contentHash: outcome.contentHash,
|
|
308
296
|
status: outcome.status,
|
|
@@ -312,11 +300,11 @@ function definitionDeployedData(definition, outcome) {
|
|
|
312
300
|
actionCount: actions.length,
|
|
313
301
|
transitionCount: transitions.length,
|
|
314
302
|
fieldCount: fieldEntries2.length,
|
|
315
|
-
activityKinds: [ ...new Set(activities.map(
|
|
303
|
+
activityKinds: [ ...new Set(activities.map(invariants.deriveActivityKind)) ].sort(),
|
|
316
304
|
fieldKinds: [ ...new Set(fieldEntries2.map(entry => entry.type)) ].sort(),
|
|
317
305
|
guardCount: stages.reduce((n, stage) => n + (stage.guards ?? []).length, 0),
|
|
318
|
-
effectCount:
|
|
319
|
-
subworkflowCount:
|
|
306
|
+
effectCount: actions.reduce((n, action) => n + (action.effects ?? []).length, 0),
|
|
307
|
+
subworkflowCount: actions.filter(action => action.spawn !== void 0).length,
|
|
320
308
|
lifecycle: definition.lifecycle ?? "standalone"
|
|
321
309
|
};
|
|
322
310
|
}
|
|
@@ -363,7 +351,7 @@ function openStage$1(instance) {
|
|
|
363
351
|
|
|
364
352
|
function firedActivityKind(instance, activity) {
|
|
365
353
|
const node = openStage$1(instance).stage?.activities?.find(a => a.name === activity);
|
|
366
|
-
return node === void 0 ? void 0 :
|
|
354
|
+
return node === void 0 ? void 0 : invariants.deriveActivityKind(node);
|
|
367
355
|
}
|
|
368
356
|
|
|
369
357
|
function editedFieldKind(instance, target) {
|
|
@@ -401,20 +389,11 @@ function fieldEditedData(args) {
|
|
|
401
389
|
}
|
|
402
390
|
|
|
403
391
|
function effectSites(def) {
|
|
404
|
-
const sites = []
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
};
|
|
410
|
-
for (const stage of def.stages ?? []) {
|
|
411
|
-
for (const activity of stage.activities ?? []) {
|
|
412
|
-
const at = `stage[${stage.name}].activity[${activity.name}]`;
|
|
413
|
-
collect(activity.effects, `${at}.effects`);
|
|
414
|
-
for (const action of activity.actions ?? []) collect(action.effects, `${at}.action[${action.name}].effects`);
|
|
415
|
-
}
|
|
416
|
-
for (const transition of stage.transitions ?? []) collect(transition.effects, `stage[${stage.name}].transition[${transition.name}].effects`);
|
|
417
|
-
}
|
|
392
|
+
const sites = [];
|
|
393
|
+
for (const stage of def.stages ?? []) for (const activity of stage.activities ?? []) for (const action of activity.actions ?? []) for (const effect of action.effects ?? []) sites.push({
|
|
394
|
+
effect: effect,
|
|
395
|
+
location: `stage[${stage.name}].activity[${activity.name}].action[${action.name}].effects`
|
|
396
|
+
});
|
|
418
397
|
return sites;
|
|
419
398
|
}
|
|
420
399
|
|
|
@@ -640,25 +619,15 @@ function describeSiteHeading(address, ctx) {
|
|
|
640
619
|
});
|
|
641
620
|
}
|
|
642
621
|
|
|
643
|
-
case "
|
|
644
|
-
{
|
|
645
|
-
const activity = activityTitle(address.activity, ctx);
|
|
646
|
-
return groqConditionDescribe.phrase(id, {
|
|
647
|
-
params: {
|
|
648
|
-
activity: activity
|
|
649
|
-
},
|
|
650
|
-
text: `${groqConditionDescribe.quoted(activity)} completes automatically when:`
|
|
651
|
-
});
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
case "fail-when":
|
|
622
|
+
case "action-when":
|
|
655
623
|
{
|
|
656
|
-
const activity = activityTitle(address.activity, ctx);
|
|
624
|
+
const title = actionTitle(address, ctx), activity = activityTitle(address.activity, ctx);
|
|
657
625
|
return groqConditionDescribe.phrase(id, {
|
|
658
626
|
params: {
|
|
627
|
+
title: title,
|
|
659
628
|
activity: activity
|
|
660
629
|
},
|
|
661
|
-
text:
|
|
630
|
+
text: `action ${groqConditionDescribe.quoted(title)} on ${groqConditionDescribe.quoted(activity)} fires automatically when:`
|
|
662
631
|
});
|
|
663
632
|
}
|
|
664
633
|
|
|
@@ -728,20 +697,26 @@ function describeSite(site, ctx) {
|
|
|
728
697
|
});
|
|
729
698
|
}
|
|
730
699
|
|
|
700
|
+
case "action-when":
|
|
701
|
+
{
|
|
702
|
+
const title = actionTitle(site, ctx), activity = activityTitle(site.activity, ctx);
|
|
703
|
+
return groqConditionDescribe.phrase(id, {
|
|
704
|
+
params: {
|
|
705
|
+
title: title,
|
|
706
|
+
activity: activity
|
|
707
|
+
},
|
|
708
|
+
text: `the ${groqConditionDescribe.quoted(title)} trigger on ${groqConditionDescribe.quoted(activity)}`
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
|
|
731
712
|
case "activity-filter":
|
|
732
|
-
case "complete-when":
|
|
733
|
-
case "fail-when":
|
|
734
713
|
{
|
|
735
|
-
const activity = activityTitle(site.activity, ctx)
|
|
736
|
-
"activity-filter": "visibility of",
|
|
737
|
-
"complete-when": "auto-completion of",
|
|
738
|
-
"fail-when": "auto-failure of"
|
|
739
|
-
}[site.kind];
|
|
714
|
+
const activity = activityTitle(site.activity, ctx);
|
|
740
715
|
return groqConditionDescribe.phrase(id, {
|
|
741
716
|
params: {
|
|
742
717
|
activity: activity
|
|
743
718
|
},
|
|
744
|
-
text:
|
|
719
|
+
text: `existence of ${groqConditionDescribe.quoted(activity)}`
|
|
745
720
|
});
|
|
746
721
|
}
|
|
747
722
|
|
|
@@ -790,7 +765,7 @@ function unsatisfiedTransitionSummaries(evaluation) {
|
|
|
790
765
|
const ctx = {
|
|
791
766
|
definition: evaluation.definition
|
|
792
767
|
};
|
|
793
|
-
return evaluation.currentStage.transitions.filter(t => !t.
|
|
768
|
+
return evaluation.currentStage.transitions.filter(t => !t.whenSatisfied).flatMap(t => {
|
|
794
769
|
const summary = describeCondition(t.insight, ctx).summary;
|
|
795
770
|
return summary !== void 0 ? [ {
|
|
796
771
|
transition: t.transition.name,
|
|
@@ -804,7 +779,12 @@ function describeNode(node, ctx) {
|
|
|
804
779
|
}
|
|
805
780
|
|
|
806
781
|
function dispatchDescribe(node, ctx) {
|
|
807
|
-
|
|
782
|
+
if ("involvedIn" in node) return describeFieldInsight(node, ctx);
|
|
783
|
+
if ("actions" in node) return describeActivity(node, ctx);
|
|
784
|
+
if ("transition" in node) return describeCondition(node.insight, ctx);
|
|
785
|
+
if ("analysis" in node) return describeCondition(node, ctx);
|
|
786
|
+
const gate = "whenInsight" in node ? node.whenInsight ?? node.insight : node.insight;
|
|
787
|
+
return gate !== void 0 ? describeCondition(gate, ctx) : void 0;
|
|
808
788
|
}
|
|
809
789
|
|
|
810
790
|
function describeActivity(node, ctx) {
|
|
@@ -815,12 +795,6 @@ function describeActivity(node, ctx) {
|
|
|
815
795
|
} : {},
|
|
816
796
|
...node.filterInsight !== void 0 ? {
|
|
817
797
|
filter: describeCondition(node.filterInsight, ctx)
|
|
818
|
-
} : {},
|
|
819
|
-
...node.completeWhenInsight !== void 0 ? {
|
|
820
|
-
completeWhen: describeCondition(node.completeWhenInsight, ctx)
|
|
821
|
-
} : {},
|
|
822
|
-
...node.failWhenInsight !== void 0 ? {
|
|
823
|
-
failWhen: describeCondition(node.failWhenInsight, ctx)
|
|
824
798
|
} : {}
|
|
825
799
|
};
|
|
826
800
|
}
|
|
@@ -867,7 +841,7 @@ function stageSites(stage, definition) {
|
|
|
867
841
|
kind: "transition",
|
|
868
842
|
transition: transition.name
|
|
869
843
|
},
|
|
870
|
-
condition: transition.
|
|
844
|
+
condition: transition.when
|
|
871
845
|
});
|
|
872
846
|
for (const activity of stage.activities ?? []) sites.push(...activitySites(activity, stage.name));
|
|
873
847
|
for (const site of editableFieldsInStage(definition, stage)) typeof site.effective == "string" && sites.push({
|
|
@@ -894,15 +868,14 @@ function stageSites(stage, definition) {
|
|
|
894
868
|
}
|
|
895
869
|
|
|
896
870
|
function activitySites(activity, stage) {
|
|
897
|
-
|
|
871
|
+
return [ ...activity.filter !== void 0 ? [ {
|
|
898
872
|
stage: stage,
|
|
899
873
|
address: {
|
|
900
|
-
kind:
|
|
874
|
+
kind: "activity-filter",
|
|
901
875
|
activity: activity.name
|
|
902
876
|
},
|
|
903
|
-
condition:
|
|
904
|
-
} ] : []
|
|
905
|
-
return [ ...named("activity-filter", activity.filter), ...Object.entries(activity.requirements ?? {}).map(([requirement, condition]) => ({
|
|
877
|
+
condition: activity.filter
|
|
878
|
+
} ] : [], ...Object.entries(activity.requirements ?? {}).map(([requirement, condition]) => ({
|
|
906
879
|
stage: stage,
|
|
907
880
|
address: {
|
|
908
881
|
kind: "requirement",
|
|
@@ -910,7 +883,7 @@ function activitySites(activity, stage) {
|
|
|
910
883
|
requirement: requirement
|
|
911
884
|
},
|
|
912
885
|
condition: condition
|
|
913
|
-
})), ...
|
|
886
|
+
})), ...(activity.actions ?? []).flatMap(action => [ ...action.filter !== void 0 ? [ {
|
|
914
887
|
stage: stage,
|
|
915
888
|
address: {
|
|
916
889
|
kind: "action",
|
|
@@ -918,7 +891,15 @@ function activitySites(activity, stage) {
|
|
|
918
891
|
action: action.name
|
|
919
892
|
},
|
|
920
893
|
condition: action.filter
|
|
921
|
-
} ] : []
|
|
894
|
+
} ] : [], ...action.when !== void 0 ? [ {
|
|
895
|
+
stage: stage,
|
|
896
|
+
address: {
|
|
897
|
+
kind: "action-when",
|
|
898
|
+
activity: activity.name,
|
|
899
|
+
action: action.name
|
|
900
|
+
},
|
|
901
|
+
condition: action.when
|
|
902
|
+
} ] : [] ]) ];
|
|
922
903
|
}
|
|
923
904
|
|
|
924
905
|
async function describeDefinition(definition) {
|
|
@@ -940,58 +921,111 @@ async function describeDefinition(definition) {
|
|
|
940
921
|
}
|
|
941
922
|
|
|
942
923
|
function acceptsDocumentType(definition, documentType) {
|
|
943
|
-
return (definition.fields ?? []).some(entry =>
|
|
924
|
+
return (definition.fields ?? []).some(entry => invariants.isSubjectEntry(entry) && (entry.types === void 0 || entry.types.includes(documentType)));
|
|
944
925
|
}
|
|
945
926
|
|
|
946
927
|
async function isDefinitionApplicable(args) {
|
|
947
|
-
const {definition: definition, document: document} = args;
|
|
948
|
-
return assertCandidateDocument(document), !invariants.isStartableDefinition(definition) || !acceptsDocumentType(definition, document._type) ? !1 :
|
|
928
|
+
const {definition: definition, document: document, scope: scope} = args;
|
|
929
|
+
return assertCandidateDocument(document), !invariants.isStartableDefinition(definition) || !acceptsDocumentType(definition, document._type) ? !1 : evaluateDeclaredStartFilter({
|
|
930
|
+
definition: definition,
|
|
931
|
+
document: document,
|
|
932
|
+
scope: scope
|
|
933
|
+
});
|
|
949
934
|
}
|
|
950
935
|
|
|
951
936
|
async function applicableDefinitions(args) {
|
|
952
|
-
const {definitions: definitions, document: document} = args;
|
|
937
|
+
const {definitions: definitions, document: document, scope: scope} = args;
|
|
953
938
|
assertCandidateDocument(document);
|
|
954
939
|
const verdicts = await Promise.all(definitions.map(definition => isDefinitionApplicable({
|
|
955
940
|
definition: definition,
|
|
956
|
-
document: document
|
|
941
|
+
document: document,
|
|
942
|
+
...scope !== void 0 ? {
|
|
943
|
+
scope: scope
|
|
944
|
+
} : {}
|
|
957
945
|
})));
|
|
958
946
|
return definitions.filter((_, i) => verdicts[i]);
|
|
959
947
|
}
|
|
960
948
|
|
|
961
|
-
async function
|
|
962
|
-
const
|
|
963
|
-
|
|
949
|
+
async function evaluateStartFilter(args) {
|
|
950
|
+
const {filter: filter, definition: definition, document: document, scope: scope} = args;
|
|
951
|
+
let parsed;
|
|
952
|
+
try {
|
|
953
|
+
parsed = {
|
|
954
|
+
tree: groqJs.parse(filter),
|
|
955
|
+
needsDataset: groqConditionDescribe.analyzeCondition(filter).readsDataset
|
|
956
|
+
};
|
|
957
|
+
} catch (err) {
|
|
958
|
+
rethrowNamingDefinition(err, definition);
|
|
959
|
+
}
|
|
960
|
+
let dataset = [];
|
|
961
|
+
if (parsed.needsDataset) {
|
|
962
|
+
if (scope?.fetchDataset === void 0) return !1;
|
|
963
|
+
dataset = await scope.fetchDataset();
|
|
964
|
+
}
|
|
964
965
|
try {
|
|
965
|
-
const
|
|
966
|
-
|
|
967
|
-
|
|
966
|
+
const result = await (await groqJs.evaluate(parsed.tree, {
|
|
967
|
+
...document !== void 0 ? {
|
|
968
|
+
root: document
|
|
969
|
+
} : {},
|
|
970
|
+
dataset: dataset,
|
|
971
|
+
params: startFilterParams({
|
|
972
|
+
definition: definition,
|
|
973
|
+
scope: scope
|
|
974
|
+
})
|
|
968
975
|
})).get();
|
|
969
976
|
return invariants.isUnevaluable(result) ? !1 : !!result;
|
|
970
977
|
} catch (err) {
|
|
971
|
-
|
|
978
|
+
rethrowNamingDefinition(err, definition);
|
|
972
979
|
}
|
|
973
980
|
}
|
|
974
981
|
|
|
982
|
+
function rethrowNamingDefinition(err, definition) {
|
|
983
|
+
invariants.rethrowWithContext(err, `start.filter on definition "${definition.name ?? "<unnamed>"}" failed to evaluate (deploy validation rejects this — was the definition written around it?)`);
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
function startFilterParams({definition: definition, scope: scope}) {
|
|
987
|
+
return {
|
|
988
|
+
...definition.name !== void 0 ? {
|
|
989
|
+
definition: definition.name
|
|
990
|
+
} : {},
|
|
991
|
+
...scope?.tag !== void 0 ? {
|
|
992
|
+
tag: scope.tag
|
|
993
|
+
} : {},
|
|
994
|
+
...scope?.now !== void 0 ? {
|
|
995
|
+
now: scope.now
|
|
996
|
+
} : {},
|
|
997
|
+
...scope?.fields !== void 0 ? {
|
|
998
|
+
fields: scope.fields
|
|
999
|
+
} : {}
|
|
1000
|
+
};
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
async function evaluateDeclaredStartFilter(args) {
|
|
1004
|
+
const {definition: definition, document: document, scope: scope} = args, filter = definition.start?.filter;
|
|
1005
|
+
return filter === void 0 ? !0 : evaluateStartFilter({
|
|
1006
|
+
filter: filter,
|
|
1007
|
+
definition: definition,
|
|
1008
|
+
document: document,
|
|
1009
|
+
...scope !== void 0 ? {
|
|
1010
|
+
scope: scope
|
|
1011
|
+
} : {}
|
|
1012
|
+
});
|
|
1013
|
+
}
|
|
1014
|
+
|
|
975
1015
|
function assertCandidateDocument(document) {
|
|
976
1016
|
const docType = document._type;
|
|
977
1017
|
if (typeof docType != "string" || docType.length === 0) throw new invariants.ContractViolationError("Applicability: the candidate document must carry a non-empty `_type` — pass the loaded document, not a reference or bare id.");
|
|
978
1018
|
}
|
|
979
1019
|
|
|
980
|
-
|
|
981
|
-
|
|
1020
|
+
const START_FILTER_VAR_NAMES = new Set(invariants.START_FILTER_VARS.map(v2 => v2.name));
|
|
1021
|
+
|
|
1022
|
+
function startFilterSyntaxIssues(groq) {
|
|
982
1023
|
try {
|
|
983
|
-
|
|
1024
|
+
groqJs.parse(groq);
|
|
984
1025
|
} catch (err) {
|
|
985
1026
|
return [ err instanceof Error ? err.message : String(err) ];
|
|
986
1027
|
}
|
|
987
|
-
|
|
988
|
-
hasEverythingNode(tree) && issues.push("scans the dataset with `*` — `applicableWhen` evaluates against the candidate document alone (there is no lake in this context), so a scan reads a phantom dataset and silently misleads. Read the document's own attributes instead (e.g. `_type == 'article' && articleType == 'finance'`)");
|
|
989
|
-
for (const name of invariants.conditionParameterNames(groq)) issues.push(`reads $${name} — \`applicableWhen\` evaluates against the candidate document alone; no \`$\` vars are bound (an unbound variable is GROQ null, so the predicate would silently never match)`);
|
|
990
|
-
return issues;
|
|
991
|
-
}
|
|
992
|
-
|
|
993
|
-
function hasEverythingNode(node) {
|
|
994
|
-
return Array.isArray(node) ? node.some(hasEverythingNode) : typeof node != "object" || node === null ? !1 : node.type === "Everything" ? !0 : Object.values(node).some(hasEverythingNode);
|
|
1028
|
+
return [ ...invariants.conditionParameterNames(groq) ].filter(name => !START_FILTER_VAR_NAMES.has(name)).map(name => `reads $${name}, which the start-filter context does not bind (an unbound variable is GROQ null, so the predicate would silently never match). Bound here: ` + invariants.START_FILTER_VARS.map(v2 => `$${v2.name}`).join(", "));
|
|
995
1029
|
}
|
|
996
1030
|
|
|
997
1031
|
function getPath(value, path) {
|
|
@@ -1069,10 +1103,25 @@ function rewriteRefsRecursive(value, resource) {
|
|
|
1069
1103
|
return mapJsonStrings(value, (str, key) => key === "_ref" && !str.includes(":") ? invariants.gdrFromResource(resource, str) : str);
|
|
1070
1104
|
}
|
|
1071
1105
|
|
|
1106
|
+
function overlayInstanceInSnapshot(snapshot, instance) {
|
|
1107
|
+
const uri = invariants.selfGdr(instance), restamped = {
|
|
1108
|
+
...instance,
|
|
1109
|
+
_id: uri
|
|
1110
|
+
};
|
|
1111
|
+
return snapshot.knownIds.has(uri) ? {
|
|
1112
|
+
docs: snapshot.docs.map(doc => doc._id === uri ? restamped : doc),
|
|
1113
|
+
knownIds: snapshot.knownIds
|
|
1114
|
+
} : {
|
|
1115
|
+
docs: [ ...snapshot.docs, restamped ],
|
|
1116
|
+
knownIds: /* @__PURE__ */ new Set([ ...snapshot.knownIds, uri ])
|
|
1117
|
+
};
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1072
1120
|
function resolveFieldRead(args) {
|
|
1073
|
-
const {kind: kind, value: value, path: path, snapshot: snapshot} = args;
|
|
1121
|
+
const {kind: kind, value: value, path: path, snapshot: snapshot, targetKind: targetKind} = args;
|
|
1074
1122
|
if (kind === "doc.ref") {
|
|
1075
1123
|
if (!invariants.isGdr(value)) return null;
|
|
1124
|
+
if (path === void 0 && targetKind === "doc.ref") return value;
|
|
1076
1125
|
const base = derefBase(value, snapshot);
|
|
1077
1126
|
return path === void 0 ? base : walkDocPath(base, path);
|
|
1078
1127
|
}
|
|
@@ -1095,7 +1144,8 @@ function buildParams(args) {
|
|
|
1095
1144
|
ancestors: instance.ancestors.map(a => a.id),
|
|
1096
1145
|
stage: instance.currentStage,
|
|
1097
1146
|
now: now,
|
|
1098
|
-
|
|
1147
|
+
context: contextMap(instance),
|
|
1148
|
+
effects: effectOutputsMap(instance),
|
|
1099
1149
|
effectStatus: effectStatusMap(instance),
|
|
1100
1150
|
activities: currentActivities2,
|
|
1101
1151
|
subworkflows: subworkflowVarRows(instance, snapshot),
|
|
@@ -1110,6 +1160,7 @@ function subworkflowVarRows(instance, snapshot) {
|
|
|
1110
1160
|
_id: invariants.toBareId(row.ref.id),
|
|
1111
1161
|
rowKey: row.rowKey,
|
|
1112
1162
|
activity: row.activity,
|
|
1163
|
+
action: row.action,
|
|
1113
1164
|
definition: row.definition,
|
|
1114
1165
|
current: row.stageEntry === openKey,
|
|
1115
1166
|
...childState(row, snapshot),
|
|
@@ -1182,9 +1233,15 @@ function effectStatusMap(instance) {
|
|
|
1182
1233
|
return out;
|
|
1183
1234
|
}
|
|
1184
1235
|
|
|
1185
|
-
function
|
|
1236
|
+
function contextMap(instance) {
|
|
1237
|
+
const out = {};
|
|
1238
|
+
for (const entry of instance.context) out[entry.name] = entry._type === "context.json" ? parseJsonContextEntry(entry) : entry.value;
|
|
1239
|
+
return out;
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
function effectOutputsMap(instance) {
|
|
1186
1243
|
const out = {};
|
|
1187
|
-
for (const
|
|
1244
|
+
for (const run of instance.effectHistory) run.outputs !== void 0 && (out[run.name] = run.outputs);
|
|
1188
1245
|
return out;
|
|
1189
1246
|
}
|
|
1190
1247
|
|
|
@@ -1192,7 +1249,7 @@ function parseJsonContextEntry(entry) {
|
|
|
1192
1249
|
try {
|
|
1193
1250
|
return JSON.parse(entry.value);
|
|
1194
1251
|
} catch (err) {
|
|
1195
|
-
invariants.rethrowWithContext(err, `
|
|
1252
|
+
invariants.rethrowWithContext(err, `context entry "${entry.name}" holds unparseable JSON`);
|
|
1196
1253
|
}
|
|
1197
1254
|
}
|
|
1198
1255
|
|
|
@@ -1257,8 +1314,9 @@ function recordFieldDiscards({target: target, scope: scope, at: at}) {
|
|
|
1257
1314
|
|
|
1258
1315
|
function applyActivityStatusChange(args) {
|
|
1259
1316
|
const {entry: entry, history: history, stage: stage, to: to, at: at, actor: actor} = args, from = entry.status;
|
|
1260
|
-
entry.status = to, invariants.isTerminalActivityStatus(to)
|
|
1261
|
-
actor !== void 0 && (entry.completedBy = actor.id)),
|
|
1317
|
+
entry.status = to, invariants.isTerminalActivityStatus(to) ? (entry.completedAt = at,
|
|
1318
|
+
actor !== void 0 && (entry.completedBy = actor.id)) : (delete entry.completedAt,
|
|
1319
|
+
delete entry.completedBy), history.push({
|
|
1262
1320
|
_key: randomKey(),
|
|
1263
1321
|
_type: "activityStatusChanged",
|
|
1264
1322
|
at: at,
|
|
@@ -1346,7 +1404,7 @@ class RequiredFieldNotProvidedError extends invariants.WorkflowError {
|
|
|
1346
1404
|
missing;
|
|
1347
1405
|
constructor(args) {
|
|
1348
1406
|
const lines = args.missing.map(m => ` - ${m.name} (${m.type})`).join(`\n`), where = args.definition !== void 0 ? ` for workflow "${args.definition}"` : "";
|
|
1349
|
-
super("required-field-not-provided", `Required input fields${where} were not provided:\n${lines}\nProvide each via \`initialFields\` when starting standalone, or via the parent's \`
|
|
1407
|
+
super("required-field-not-provided", `Required input fields${where} were not provided:\n${lines}\nProvide each via \`initialFields\` when starting standalone, or via the parent's \`spawn.with\` when spawned.`),
|
|
1350
1408
|
this.name = "RequiredFieldNotProvidedError", args.definition !== void 0 && (this.definition = args.definition),
|
|
1351
1409
|
this.missing = args.missing;
|
|
1352
1410
|
}
|
|
@@ -1374,7 +1432,11 @@ class PartialGuardDeployError extends invariants.WorkflowError {
|
|
|
1374
1432
|
}
|
|
1375
1433
|
}
|
|
1376
1434
|
|
|
1377
|
-
const
|
|
1435
|
+
const CONCURRENT_COMMIT_MAX_ATTEMPTS = 3;
|
|
1436
|
+
|
|
1437
|
+
function lostRaceMessage(args) {
|
|
1438
|
+
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.`;
|
|
1439
|
+
}
|
|
1378
1440
|
|
|
1379
1441
|
class ConcurrentFireActionError extends invariants.WorkflowError {
|
|
1380
1442
|
instanceId;
|
|
@@ -1382,9 +1444,12 @@ class ConcurrentFireActionError extends invariants.WorkflowError {
|
|
|
1382
1444
|
action;
|
|
1383
1445
|
attempts;
|
|
1384
1446
|
constructor(args) {
|
|
1385
|
-
super("concurrent-fire-action",
|
|
1386
|
-
|
|
1387
|
-
|
|
1447
|
+
super("concurrent-fire-action", lostRaceMessage({
|
|
1448
|
+
what: `Action "${args.action}" on activity "${args.activity}"`,
|
|
1449
|
+
instanceId: args.instanceId,
|
|
1450
|
+
attempts: args.attempts
|
|
1451
|
+
})), this.name = "ConcurrentFireActionError", this.instanceId = args.instanceId,
|
|
1452
|
+
this.activity = args.activity, this.action = args.action, this.attempts = args.attempts;
|
|
1388
1453
|
}
|
|
1389
1454
|
}
|
|
1390
1455
|
|
|
@@ -1400,9 +1465,26 @@ class ConcurrentEditFieldError extends invariants.WorkflowError {
|
|
|
1400
1465
|
attempts;
|
|
1401
1466
|
constructor(args) {
|
|
1402
1467
|
const where = args.target.activity !== void 0 ? `${args.target.activity}.${args.target.field}` : args.target.field;
|
|
1403
|
-
super("concurrent-edit-field",
|
|
1404
|
-
|
|
1405
|
-
|
|
1468
|
+
super("concurrent-edit-field", lostRaceMessage({
|
|
1469
|
+
what: `Edit of "${args.target.scope}:${where}"`,
|
|
1470
|
+
instanceId: args.instanceId,
|
|
1471
|
+
attempts: args.attempts
|
|
1472
|
+
})), this.name = "ConcurrentEditFieldError", this.instanceId = args.instanceId,
|
|
1473
|
+
this.target = args.target, this.attempts = args.attempts;
|
|
1474
|
+
}
|
|
1475
|
+
}
|
|
1476
|
+
|
|
1477
|
+
class ConcurrentCompleteEffectError extends invariants.WorkflowError {
|
|
1478
|
+
instanceId;
|
|
1479
|
+
effectKey;
|
|
1480
|
+
attempts;
|
|
1481
|
+
constructor(args) {
|
|
1482
|
+
super("concurrent-complete-effect", lostRaceMessage({
|
|
1483
|
+
what: `Completion of effect entry "${args.effectKey}"`,
|
|
1484
|
+
instanceId: args.instanceId,
|
|
1485
|
+
attempts: args.attempts
|
|
1486
|
+
})), this.name = "ConcurrentCompleteEffectError", this.instanceId = args.instanceId,
|
|
1487
|
+
this.effectKey = args.effectKey, this.attempts = args.attempts;
|
|
1406
1488
|
}
|
|
1407
1489
|
}
|
|
1408
1490
|
|
|
@@ -1410,7 +1492,7 @@ class CascadeLimitError extends invariants.WorkflowError {
|
|
|
1410
1492
|
instanceId;
|
|
1411
1493
|
limit;
|
|
1412
1494
|
constructor(args) {
|
|
1413
|
-
super("cascade-limit", `Cascade did not stabilise after ${args.limit}
|
|
1495
|
+
super("cascade-limit", `Cascade did not stabilise after ${args.limit} hops on ${args.instanceId} — likely a runaway loop (transition \`when\`s that stay mutually satisfied). Check that each transition's \`when\` eventually goes false.`),
|
|
1414
1496
|
this.name = "CascadeLimitError", this.instanceId = args.instanceId, this.limit = args.limit;
|
|
1415
1497
|
}
|
|
1416
1498
|
}
|
|
@@ -1520,9 +1602,6 @@ function opAppliedEntry(args) {
|
|
|
1520
1602
|
...origin.action !== void 0 ? {
|
|
1521
1603
|
action: origin.action
|
|
1522
1604
|
} : {},
|
|
1523
|
-
...origin.transition !== void 0 ? {
|
|
1524
|
-
transition: origin.transition
|
|
1525
|
-
} : {},
|
|
1526
1605
|
...origin.edit === !0 ? {
|
|
1527
1606
|
edit: !0
|
|
1528
1607
|
} : {},
|
|
@@ -1584,7 +1663,11 @@ async function applyOp(op, ctx) {
|
|
|
1584
1663
|
}
|
|
1585
1664
|
|
|
1586
1665
|
function applyFieldSet(op, ctx) {
|
|
1587
|
-
const
|
|
1666
|
+
const entry = locateEntry(ctx, op.target), value = resolveOpValue({
|
|
1667
|
+
src: op.value,
|
|
1668
|
+
ctx: ctx,
|
|
1669
|
+
target: entrySlot(entry)
|
|
1670
|
+
});
|
|
1588
1671
|
return invariants.validateFieldValue({
|
|
1589
1672
|
entryType: entry._type,
|
|
1590
1673
|
entryName: entry.name,
|
|
@@ -1609,6 +1692,24 @@ function entryShape(entry) {
|
|
|
1609
1692
|
} : {} : {};
|
|
1610
1693
|
}
|
|
1611
1694
|
|
|
1695
|
+
function entrySlot(entry) {
|
|
1696
|
+
return entry._type === "object" ? {
|
|
1697
|
+
kind: entry._type,
|
|
1698
|
+
fields: entry.fields
|
|
1699
|
+
} : {
|
|
1700
|
+
kind: entry._type
|
|
1701
|
+
};
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
function appendItemSlot(entry) {
|
|
1705
|
+
if (entry._type === "doc.refs") return {
|
|
1706
|
+
kind: "doc.ref"
|
|
1707
|
+
};
|
|
1708
|
+
if (entry._type === "array") return {
|
|
1709
|
+
fields: entry.of
|
|
1710
|
+
};
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1612
1713
|
const EMPTY_BY_KIND = {
|
|
1613
1714
|
"doc.refs": [],
|
|
1614
1715
|
array: [],
|
|
@@ -1624,7 +1725,13 @@ function applyFieldUnset(op, ctx) {
|
|
|
1624
1725
|
}
|
|
1625
1726
|
|
|
1626
1727
|
function applyFieldAppend(op, ctx) {
|
|
1627
|
-
const
|
|
1728
|
+
const entry = locateEntry(ctx, op.target), slot = appendItemSlot(entry), item = resolveOpValue({
|
|
1729
|
+
src: op.value,
|
|
1730
|
+
ctx: ctx,
|
|
1731
|
+
...slot !== void 0 ? {
|
|
1732
|
+
target: slot
|
|
1733
|
+
} : {}
|
|
1734
|
+
});
|
|
1628
1735
|
invariants.validateFieldAppendItem({
|
|
1629
1736
|
entryType: entry._type,
|
|
1630
1737
|
entryName: entry.name,
|
|
@@ -1650,17 +1757,31 @@ function withRowKey(item) {
|
|
|
1650
1757
|
}
|
|
1651
1758
|
|
|
1652
1759
|
async function applyFieldUpdateWhere(op, ctx) {
|
|
1653
|
-
const entry = locateEntry(ctx, op.target)
|
|
1760
|
+
const entry = locateEntry(ctx, op.target);
|
|
1761
|
+
requireMergeableRows(entry, op);
|
|
1762
|
+
const rows = requireArrayValue(entry, op), merge = resolveOpValue({
|
|
1763
|
+
src: op.value,
|
|
1764
|
+
ctx: ctx,
|
|
1765
|
+
target: {
|
|
1766
|
+
fields: entry.of
|
|
1767
|
+
}
|
|
1768
|
+
});
|
|
1654
1769
|
if (merge === null || typeof merge != "object" || Array.isArray(merge)) throw new Error(`field.updateWhere value must resolve to an object of fields to merge (target "${op.target.field}")`);
|
|
1770
|
+
requireNoReservedMergeKeys(merge, op);
|
|
1655
1771
|
const matches = await rowMatches({
|
|
1656
1772
|
where: op.where,
|
|
1657
1773
|
rows: rows,
|
|
1658
1774
|
ctx: ctx
|
|
1659
|
-
})
|
|
1660
|
-
return setEntryValue(entry, rows.map((row, index) => matches[index] ? {
|
|
1775
|
+
}), next = rows.map((row, index) => matches[index] ? {
|
|
1661
1776
|
...row,
|
|
1662
1777
|
...merge
|
|
1663
|
-
} : row)
|
|
1778
|
+
} : row);
|
|
1779
|
+
return invariants.validateFieldValue({
|
|
1780
|
+
entryType: entry._type,
|
|
1781
|
+
entryName: entry.name,
|
|
1782
|
+
value: next,
|
|
1783
|
+
of: entry.of
|
|
1784
|
+
}), setEntryValue(entry, next), {
|
|
1664
1785
|
opType: op.type,
|
|
1665
1786
|
target: op.target,
|
|
1666
1787
|
resolved: {
|
|
@@ -1681,8 +1802,17 @@ async function applyFieldRemoveWhere(op, ctx) {
|
|
|
1681
1802
|
};
|
|
1682
1803
|
}
|
|
1683
1804
|
|
|
1805
|
+
function requireMergeableRows(entry, op) {
|
|
1806
|
+
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`);
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1809
|
+
function requireNoReservedMergeKeys(merge, op) {
|
|
1810
|
+
const reserved = [ "_key", "_type" ].filter(key => key in merge);
|
|
1811
|
+
if (reserved.length !== 0) throw new Error(`field.updateWhere merge writes the reserved row key${reserved.length === 1 ? "" : "s"} ${reserved.map(k => `"${k}"`).join(", ")} (target "${op.target.field}") — row identity and bookkeeping are engine-stamped, never merged`);
|
|
1812
|
+
}
|
|
1813
|
+
|
|
1684
1814
|
function requireArrayValue(entry, op) {
|
|
1685
|
-
if (!Array.isArray(entry.value)) throw new Error(`${op.type} target ${op.target.scope}:"${op.target.field}" holds a non-array
|
|
1815
|
+
if (!Array.isArray(entry.value)) throw new Error(`${op.type} target ${op.target.scope}:"${op.target.field}" (${entry._type}) holds a non-array value — where-ops operate on array entries`);
|
|
1686
1816
|
return entry.value;
|
|
1687
1817
|
}
|
|
1688
1818
|
|
|
@@ -1726,7 +1856,18 @@ function fieldHost(ctx, scope) {
|
|
|
1726
1856
|
activity?.fields;
|
|
1727
1857
|
}
|
|
1728
1858
|
|
|
1729
|
-
function
|
|
1859
|
+
function subSlot(target, field) {
|
|
1860
|
+
const shape = target?.fields?.find(f => f.name === field);
|
|
1861
|
+
if (shape !== void 0) return {
|
|
1862
|
+
kind: shape.type,
|
|
1863
|
+
...shape.fields !== void 0 ? {
|
|
1864
|
+
fields: shape.fields
|
|
1865
|
+
} : {}
|
|
1866
|
+
};
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
function resolveOpValue(args) {
|
|
1870
|
+
const {src: src, ctx: ctx, target: target} = args;
|
|
1730
1871
|
switch (src.type) {
|
|
1731
1872
|
case "literal":
|
|
1732
1873
|
case "param":
|
|
@@ -1747,14 +1888,26 @@ function resolveOpValue(src, ctx) {
|
|
|
1747
1888
|
kind: entry._type,
|
|
1748
1889
|
value: entry.value,
|
|
1749
1890
|
path: src.path,
|
|
1750
|
-
snapshot: ctx.snapshot
|
|
1891
|
+
snapshot: ctx.snapshot,
|
|
1892
|
+
...target?.kind !== void 0 ? {
|
|
1893
|
+
targetKind: target.kind
|
|
1894
|
+
} : {}
|
|
1751
1895
|
});
|
|
1752
1896
|
}
|
|
1753
1897
|
|
|
1754
1898
|
case "object":
|
|
1755
1899
|
{
|
|
1756
1900
|
const out = {};
|
|
1757
|
-
for (const [field, fieldSrc] of Object.entries(src.fields))
|
|
1901
|
+
for (const [field, fieldSrc] of Object.entries(src.fields)) {
|
|
1902
|
+
const slot = subSlot(target, field);
|
|
1903
|
+
out[field] = resolveOpValue({
|
|
1904
|
+
src: fieldSrc,
|
|
1905
|
+
ctx: ctx,
|
|
1906
|
+
...slot !== void 0 ? {
|
|
1907
|
+
target: slot
|
|
1908
|
+
} : {}
|
|
1909
|
+
});
|
|
1910
|
+
}
|
|
1758
1911
|
return out;
|
|
1759
1912
|
}
|
|
1760
1913
|
}
|
|
@@ -1782,7 +1935,7 @@ async function rowMatches({where: where, rows: rows, ctx: ctx}) {
|
|
|
1782
1935
|
})));
|
|
1783
1936
|
}
|
|
1784
1937
|
|
|
1785
|
-
const WHERE_SCOPE_PARAM_NAMES = [ "row", "params", "actor", "now", "self", "stage", "fields", "effects", "effectStatus", "activities", "allActivitiesDone", "anyActivityFailed" ];
|
|
1938
|
+
const WHERE_SCOPE_PARAM_NAMES = [ "row", "params", "actor", "now", "self", "stage", "fields", "context", "effects", "effectStatus", "activities", "allActivitiesDone", "anyActivityFailed" ];
|
|
1786
1939
|
|
|
1787
1940
|
function whereParams(ctx) {
|
|
1788
1941
|
const activities = findOpenStageEntry(ctx.mutation)?.activities ?? [];
|
|
@@ -1800,7 +1953,8 @@ function whereParams(ctx) {
|
|
|
1800
1953
|
activityName: ctx.activityName
|
|
1801
1954
|
})
|
|
1802
1955
|
},
|
|
1803
|
-
|
|
1956
|
+
context: contextMap(ctx.mutation),
|
|
1957
|
+
effects: effectOutputsMap(ctx.mutation),
|
|
1804
1958
|
effectStatus: effectStatusMap(ctx.mutation),
|
|
1805
1959
|
activities: activities,
|
|
1806
1960
|
...activityGateParams(activities)
|
|
@@ -1816,7 +1970,7 @@ function validateDefinition(definition) {
|
|
|
1816
1970
|
address: address
|
|
1817
1971
|
}));
|
|
1818
1972
|
}
|
|
1819
|
-
definition.
|
|
1973
|
+
definition.start?.filter !== void 0 && v2.report("start.filter", startFilterSyntaxIssues(definition.start.filter));
|
|
1820
1974
|
for (const entry of definition.fields ?? []) v2.checkEntry(entry, `workflow.fields "${entry.name}"`);
|
|
1821
1975
|
for (const stage of definition.stages) validateStage(v2, stage);
|
|
1822
1976
|
for (const issue of invariants.checkWorkflowInvariants(definition)) v2.errors.push(` · ${invariants.formatIssuePath(issue.path)}: ${issue.message}`);
|
|
@@ -1856,23 +2010,20 @@ function conditionSiteLabel({stage: stage, address: address}) {
|
|
|
1856
2010
|
return `predicate "${address.predicate}"`;
|
|
1857
2011
|
|
|
1858
2012
|
case "transition":
|
|
1859
|
-
return `${at} transition "${address.transition}"
|
|
2013
|
+
return `${at} transition "${address.transition}" when`;
|
|
1860
2014
|
|
|
1861
2015
|
case "activity-filter":
|
|
1862
2016
|
return `${at} activity "${address.activity}".filter`;
|
|
1863
2017
|
|
|
1864
|
-
case "complete-when":
|
|
1865
|
-
return `${at} activity "${address.activity}".completeWhen`;
|
|
1866
|
-
|
|
1867
|
-
case "fail-when":
|
|
1868
|
-
return `${at} activity "${address.activity}".failWhen`;
|
|
1869
|
-
|
|
1870
2018
|
case "requirement":
|
|
1871
2019
|
return `${at} activity "${address.activity}".requirements "${address.requirement}"`;
|
|
1872
2020
|
|
|
1873
2021
|
case "action":
|
|
1874
2022
|
return `${at} activity "${address.activity}" action "${address.action}".filter`;
|
|
1875
2023
|
|
|
2024
|
+
case "action-when":
|
|
2025
|
+
return `${at} activity "${address.activity}" action "${address.action}".when`;
|
|
2026
|
+
|
|
1876
2027
|
case "editable-override":
|
|
1877
2028
|
return `${at}.editable "${address.name}"`;
|
|
1878
2029
|
}
|
|
@@ -1885,15 +2036,6 @@ function validateStage(v2, stage) {
|
|
|
1885
2036
|
stageName: stage.name,
|
|
1886
2037
|
guard: guard
|
|
1887
2038
|
});
|
|
1888
|
-
for (const t of stage.transitions ?? []) {
|
|
1889
|
-
const where = `stage "${stage.name}" transition "${t.name}"`;
|
|
1890
|
-
checkOpWheres({
|
|
1891
|
-
v: v2,
|
|
1892
|
-
ops: t.ops,
|
|
1893
|
-
label: where
|
|
1894
|
-
});
|
|
1895
|
-
for (const [key, groq] of effectBindings(t.effects)) v2.tryParse(groq, `${where} effect binding "${key}"`);
|
|
1896
|
-
}
|
|
1897
2039
|
for (const activity of stage.activities ?? []) validateActivity({
|
|
1898
2040
|
v: v2,
|
|
1899
2041
|
stageName: stage.name,
|
|
@@ -1910,20 +2052,10 @@ function validateGuardReads({v: v2, stageName: stageName, guard: guard}) {
|
|
|
1910
2052
|
function validateActivity({v: v2, stageName: stageName, activity: activity}) {
|
|
1911
2053
|
const where = `stage "${stageName}" activity "${activity.name}"`;
|
|
1912
2054
|
for (const entry of activity.fields ?? []) v2.checkEntry(entry, `${where}.fields "${entry.name}"`);
|
|
1913
|
-
checkOpWheres({
|
|
1914
|
-
v: v2,
|
|
1915
|
-
ops: activity.ops,
|
|
1916
|
-
label: where
|
|
1917
|
-
});
|
|
1918
|
-
for (const [key, groq] of effectBindings(activity.effects)) v2.tryParse(groq, `${where} effect binding "${key}"`);
|
|
1919
2055
|
validateActivityActions({
|
|
1920
2056
|
v: v2,
|
|
1921
2057
|
where: where,
|
|
1922
2058
|
activity: activity
|
|
1923
|
-
}), activity.subworkflows !== void 0 && validateSubworkflows({
|
|
1924
|
-
v: v2,
|
|
1925
|
-
where: where,
|
|
1926
|
-
sub: activity.subworkflows
|
|
1927
2059
|
});
|
|
1928
2060
|
}
|
|
1929
2061
|
|
|
@@ -1936,6 +2068,11 @@ function validateActivityActions({v: v2, where: where, activity: activity}) {
|
|
|
1936
2068
|
label: label
|
|
1937
2069
|
});
|
|
1938
2070
|
for (const [key, groq] of effectBindings(a.effects)) v2.tryParse(groq, `${label} effect binding "${key}"`);
|
|
2071
|
+
a.spawn !== void 0 && validateSpawn({
|
|
2072
|
+
v: v2,
|
|
2073
|
+
where: label,
|
|
2074
|
+
sub: a.spawn
|
|
2075
|
+
});
|
|
1939
2076
|
}
|
|
1940
2077
|
}
|
|
1941
2078
|
|
|
@@ -1943,10 +2080,10 @@ function checkOpWheres({v: v2, ops: ops, label: label}) {
|
|
|
1943
2080
|
for (const op of ops ?? []) (op.type === "field.updateWhere" || op.type === "field.removeWhere") && v2.checkWhere(op.where, `${label} ${op.type} where`);
|
|
1944
2081
|
}
|
|
1945
2082
|
|
|
1946
|
-
function
|
|
1947
|
-
v2.tryParse(sub.forEach, `${where}.
|
|
1948
|
-
for (const [key, groq] of Object.entries(sub.with ?? {})) v2.tryParse(groq, `${where}.
|
|
1949
|
-
for (const [key, groq] of Object.entries(sub.context ?? {})) v2.tryParse(groq, `${where}.
|
|
2083
|
+
function validateSpawn({v: v2, where: where, sub: sub}) {
|
|
2084
|
+
v2.tryParse(sub.forEach, `${where}.spawn.forEach`);
|
|
2085
|
+
for (const [key, groq] of Object.entries(sub.with ?? {})) v2.tryParse(groq, `${where}.spawn.with "${key}"`);
|
|
2086
|
+
for (const [key, groq] of Object.entries(sub.context ?? {})) v2.tryParse(groq, `${where}.spawn.context "${key}"`);
|
|
1950
2087
|
}
|
|
1951
2088
|
|
|
1952
2089
|
function effectBindings(effects) {
|
|
@@ -1958,7 +2095,11 @@ function lintEffectOutputs(definition) {
|
|
|
1958
2095
|
for (const {effect: effect} of effectSites(definition)) declared.set(effect.name, effect.outputs?.map(shape => shape.name) ?? []);
|
|
1959
2096
|
const warnings = /* @__PURE__ */ new Set, check = ({effect: effect, key: key}) => {
|
|
1960
2097
|
const outputs = declared.get(effect);
|
|
1961
|
-
outputs === void 0
|
|
2098
|
+
if (outputs === void 0) {
|
|
2099
|
+
warnings.add(`\`$effects['${effect}'].${key}\` reads an effect this definition never declares — $effects carries completed effects' outputs only (a start seed or spawn handoff is read via $context)`);
|
|
2100
|
+
return;
|
|
2101
|
+
}
|
|
2102
|
+
outputs.includes(key) || warnings.add(`effect "${effect}" is read as \`$effects['${effect}'].${key}\` but declares outputs [${outputs.join(", ")}] — no declared producer for "${key}"`);
|
|
1962
2103
|
};
|
|
1963
2104
|
for (const groq of effectReadGroq(definition)) invariants.conditionEffectReads(groq).forEach(check);
|
|
1964
2105
|
return guardEffectReads(definition).forEach(check), [ ...warnings ];
|
|
@@ -1997,7 +2138,7 @@ function actionVerdict(activity, action) {
|
|
|
1997
2138
|
}
|
|
1998
2139
|
|
|
1999
2140
|
function availableActions(activities) {
|
|
2000
|
-
return activities.flatMap(t => t.actions.map(a => actionVerdict(t, a)));
|
|
2141
|
+
return activities.filter(t => !t.scopedOut).flatMap(t => t.actions.map(a => actionVerdict(t, a)));
|
|
2001
2142
|
}
|
|
2002
2143
|
|
|
2003
2144
|
function buildClientForGdr(args) {
|
|
@@ -2222,7 +2363,13 @@ const DATA_MODEL_VERSION = 1, DATA_MODEL_MIN_READER = 0, MODEL_STAMP = {
|
|
|
2222
2363
|
};
|
|
2223
2364
|
|
|
2224
2365
|
function fieldTreeShape(value) {
|
|
2225
|
-
|
|
2366
|
+
if (Array.isArray(value)) return value.map(fieldTreeShape);
|
|
2367
|
+
if (value === null) return "null";
|
|
2368
|
+
if (typeof value == "object") {
|
|
2369
|
+
const record = value;
|
|
2370
|
+
return Object.fromEntries(Object.keys(record).toSorted().map(key => [ key, fieldTreeShape(record[key]) ]));
|
|
2371
|
+
}
|
|
2372
|
+
return typeof value;
|
|
2226
2373
|
}
|
|
2227
2374
|
|
|
2228
2375
|
function modelVersionOf(doc) {
|
|
@@ -2309,7 +2456,7 @@ function isResolved(status) {
|
|
|
2309
2456
|
}
|
|
2310
2457
|
|
|
2311
2458
|
function failedEffectCause(input) {
|
|
2312
|
-
const
|
|
2459
|
+
const stalledEffectNames = new Set(input.activities.filter(t => !isResolved(t.status)).flatMap(t => (t.activity.actions ?? []).flatMap(action => action.effects ?? [])).map(effect2 => effect2.name)), effect = [ ...input.instance.effectHistory ].reverse().find(e => e.status === "failed" && stalledEffectNames.has(e.name));
|
|
2313
2460
|
return effect ? {
|
|
2314
2461
|
kind: "failed-effect",
|
|
2315
2462
|
effect: effect
|
|
@@ -2338,7 +2485,7 @@ function waitingState(input) {
|
|
|
2338
2485
|
state: "waiting",
|
|
2339
2486
|
activity: active.activity.name,
|
|
2340
2487
|
assignees: input.assignees[active.activity.name] ?? [],
|
|
2341
|
-
actions: (active.activity.actions ?? []).map(a => a.name)
|
|
2488
|
+
actions: (active.activity.actions ?? []).filter(a => !invariants.isCascadeFired(a)).map(a => a.name)
|
|
2342
2489
|
};
|
|
2343
2490
|
}
|
|
2344
2491
|
|
|
@@ -2353,7 +2500,7 @@ function blockedState(input) {
|
|
|
2353
2500
|
}
|
|
2354
2501
|
|
|
2355
2502
|
function noTransitionFiresCause(input) {
|
|
2356
|
-
if (input.transitions.length === 0 || input.transitions.some(t => t.
|
|
2503
|
+
if (input.transitions.length === 0 || input.transitions.some(t => t.whenSatisfied) || !input.activities.every(t => isResolved(t.status))) return;
|
|
2357
2504
|
const undecidable = input.transitions.filter(t => t.unevaluable);
|
|
2358
2505
|
return undecidable.length > 0 ? {
|
|
2359
2506
|
kind: "transition-unevaluable",
|
|
@@ -2390,7 +2537,7 @@ function diagnoseInstance(input) {
|
|
|
2390
2537
|
};
|
|
2391
2538
|
}
|
|
2392
2539
|
|
|
2393
|
-
const RUNNABLE_VERBS = /* @__PURE__ */ new Set([ "set-stage", "abort" ]);
|
|
2540
|
+
const RUNNABLE_VERBS = /* @__PURE__ */ new Set([ "set-stage", "abort", "drain-effects" ]);
|
|
2394
2541
|
|
|
2395
2542
|
function remediationsForCause(cause) {
|
|
2396
2543
|
switch (cause.kind) {
|
|
@@ -2406,7 +2553,7 @@ function remediationsForCause(cause) {
|
|
|
2406
2553
|
case "hung-effect":
|
|
2407
2554
|
return [ {
|
|
2408
2555
|
verb: "drain-effects",
|
|
2409
|
-
rationale: "Re-run the effect drainer to re-pick the claimed effect."
|
|
2556
|
+
rationale: "Re-run the effect drainer — the runtime that registered the effect handlers — to re-pick the claimed effect."
|
|
2410
2557
|
}, {
|
|
2411
2558
|
verb: "abort",
|
|
2412
2559
|
rationale: "Abort the instance if the effect never drains."
|
|
@@ -2499,7 +2646,7 @@ function resolveGuardRead(args) {
|
|
|
2499
2646
|
}
|
|
2500
2647
|
const effectsRead = invariants.EFFECTS_READ.exec(expr);
|
|
2501
2648
|
if (effectsRead !== null) {
|
|
2502
|
-
const outputs =
|
|
2649
|
+
const outputs = effectOutputsMap(ctx.instance)[effectsRead[1]];
|
|
2503
2650
|
return effectsRead[2] !== void 0 ? getPath(outputs, effectsRead[2]) : outputs;
|
|
2504
2651
|
}
|
|
2505
2652
|
throw new Error(`Guard read "${expr}" is not a supported deploy-time value — use "$self", "$now", "$fields.<name>[.path]", or "$effects['<name>'][.path]" (guards store resolved values; the lake cannot see $fields or $effects)`);
|
|
@@ -2751,6 +2898,40 @@ function contentReleaseName(args) {
|
|
|
2751
2898
|
if (!readsRaw(ref) && Array.isArray(perspective)) return perspective.find(entry => entry !== "drafts" && entry !== "published" && entry !== "raw");
|
|
2752
2899
|
}
|
|
2753
2900
|
|
|
2901
|
+
function contentDraftFallback(args) {
|
|
2902
|
+
if (readsRaw(args.ref)) return !0;
|
|
2903
|
+
const effective = args.perspective ?? DEFAULT_CONTENT_PERSPECTIVE;
|
|
2904
|
+
return Array.isArray(effective) ? effective.includes("drafts") : effective === "drafts";
|
|
2905
|
+
}
|
|
2906
|
+
|
|
2907
|
+
function contentDocQuery(documentId) {
|
|
2908
|
+
return {
|
|
2909
|
+
query: "*[_id == $id][0]",
|
|
2910
|
+
params: {
|
|
2911
|
+
id: documentId
|
|
2912
|
+
}
|
|
2913
|
+
};
|
|
2914
|
+
}
|
|
2915
|
+
|
|
2916
|
+
function watchRefRepresentations(args) {
|
|
2917
|
+
const {ref: ref} = args;
|
|
2918
|
+
if (readsRaw(ref)) return [ ref.documentId ];
|
|
2919
|
+
const release = contentReleaseName(args);
|
|
2920
|
+
return [ ref.documentId, ...contentDraftFallback(args) ? [ `drafts.${ref.documentId}` ] : [], ...release !== void 0 ? [ `versions.${release}.${ref.documentId}` ] : [] ];
|
|
2921
|
+
}
|
|
2922
|
+
|
|
2923
|
+
function projectToWatchRef(args) {
|
|
2924
|
+
const {doc: doc, ref: ref} = args;
|
|
2925
|
+
if (doc._id === ref.documentId) return doc;
|
|
2926
|
+
const accepted = watchRefRepresentations(args);
|
|
2927
|
+
if (!accepted.includes(doc._id)) throw new Error(`projectToWatchRef: the store resolved "${doc._id}" for watch ref "${ref.globalDocumentId}" — not a representation of "${ref.documentId}" under the instance's perspective (accepted: ${accepted.map(id => `"${id}"`).join(", ")}). The consumer is feeding the wrong document.`);
|
|
2928
|
+
return {
|
|
2929
|
+
...doc,
|
|
2930
|
+
_id: ref.documentId,
|
|
2931
|
+
_originalId: doc._id
|
|
2932
|
+
};
|
|
2933
|
+
}
|
|
2934
|
+
|
|
2754
2935
|
function subscriptionDocument(ref) {
|
|
2755
2936
|
return {
|
|
2756
2937
|
...invariants.parseGdr(ref.id),
|
|
@@ -2847,9 +3028,8 @@ async function readDoc({client: client, id: id, perspective: perspective}) {
|
|
|
2847
3028
|
const doc2 = await client.getDocument(id) ?? null;
|
|
2848
3029
|
return doc2 ? assertReadableModel(doc2) : null;
|
|
2849
3030
|
}
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
}, {
|
|
3031
|
+
const {query: query, params: params} = contentDocQuery(id);
|
|
3032
|
+
return await client.fetch(query, params, {
|
|
2853
3033
|
perspective: perspective
|
|
2854
3034
|
}) ?? null;
|
|
2855
3035
|
}
|
|
@@ -3002,35 +3182,35 @@ async function reload({client: client, instanceId: instanceId, tag: tag}) {
|
|
|
3002
3182
|
return doc;
|
|
3003
3183
|
}
|
|
3004
3184
|
|
|
3005
|
-
function
|
|
3185
|
+
function contextEntry(name, value) {
|
|
3006
3186
|
const _key = randomKey();
|
|
3007
3187
|
return typeof value == "string" ? {
|
|
3008
3188
|
_key: _key,
|
|
3009
|
-
_type: "
|
|
3189
|
+
_type: "context.string",
|
|
3010
3190
|
name: name,
|
|
3011
3191
|
value: value
|
|
3012
3192
|
} : typeof value == "number" ? {
|
|
3013
3193
|
_key: _key,
|
|
3014
|
-
_type: "
|
|
3194
|
+
_type: "context.number",
|
|
3015
3195
|
name: name,
|
|
3016
3196
|
value: value
|
|
3017
3197
|
} : typeof value == "boolean" ? {
|
|
3018
3198
|
_key: _key,
|
|
3019
|
-
_type: "
|
|
3199
|
+
_type: "context.boolean",
|
|
3020
3200
|
name: name,
|
|
3021
3201
|
value: value
|
|
3022
3202
|
} : {
|
|
3023
3203
|
_key: _key,
|
|
3024
|
-
_type: "
|
|
3204
|
+
_type: "context.ref",
|
|
3025
3205
|
name: name,
|
|
3026
3206
|
value: value
|
|
3027
3207
|
};
|
|
3028
3208
|
}
|
|
3029
3209
|
|
|
3030
|
-
function
|
|
3210
|
+
function contextJsonEntry(name, value) {
|
|
3031
3211
|
return {
|
|
3032
3212
|
_key: randomKey(),
|
|
3033
|
-
_type: "
|
|
3213
|
+
_type: "context.json",
|
|
3034
3214
|
name: name,
|
|
3035
3215
|
value: serializeContextValue(name, value)
|
|
3036
3216
|
};
|
|
@@ -3041,9 +3221,9 @@ function serializeContextValue(name, value) {
|
|
|
3041
3221
|
try {
|
|
3042
3222
|
json = JSON.stringify(value);
|
|
3043
3223
|
} catch (err) {
|
|
3044
|
-
invariants.rethrowWithContext(err, `
|
|
3224
|
+
invariants.rethrowWithContext(err, `context entry "${name}" holds unserializable JSON`);
|
|
3045
3225
|
}
|
|
3046
|
-
if (json === void 0) throw new Error(`
|
|
3226
|
+
if (json === void 0) throw new Error(`context entry "${name}" holds a non-JSON value (undefined, a function, or a symbol)`);
|
|
3047
3227
|
return json;
|
|
3048
3228
|
}
|
|
3049
3229
|
|
|
@@ -3073,7 +3253,7 @@ function buildInstanceBase(args) {
|
|
|
3073
3253
|
} : {},
|
|
3074
3254
|
definitionSnapshot: JSON.stringify(args.definition),
|
|
3075
3255
|
fields: args.fields,
|
|
3076
|
-
|
|
3256
|
+
context: args.context,
|
|
3077
3257
|
ancestors: args.ancestors,
|
|
3078
3258
|
...perspective !== void 0 ? {
|
|
3079
3259
|
perspective: perspective
|
|
@@ -3252,14 +3432,19 @@ async function deleteOrphanedDefinitionGuards(args) {
|
|
|
3252
3432
|
}
|
|
3253
3433
|
|
|
3254
3434
|
function derivePerspectiveFromFields(entries) {
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
}
|
|
3435
|
+
for (const entry of entries ?? []) {
|
|
3436
|
+
if (entry._type !== "release.ref") continue;
|
|
3437
|
+
const perspective = perspectiveFromReleaseValue(entry.value);
|
|
3438
|
+
if (perspective !== void 0) return perspective;
|
|
3260
3439
|
}
|
|
3261
3440
|
}
|
|
3262
3441
|
|
|
3442
|
+
function perspectiveFromReleaseValue(value) {
|
|
3443
|
+
if (typeof value != "object" || value === null) return;
|
|
3444
|
+
const releaseName = value.releaseName;
|
|
3445
|
+
return typeof releaseName == "string" && releaseName.length > 0 ? [ releaseName ] : void 0;
|
|
3446
|
+
}
|
|
3447
|
+
|
|
3263
3448
|
async function resolveDeclaredFields(args) {
|
|
3264
3449
|
const {entryDefs: entryDefs, initialFields: initialFields, ctx: ctx, randomKey: randomKey2} = args;
|
|
3265
3450
|
if (entryDefs === void 0 || entryDefs.length === 0) return [];
|
|
@@ -3285,8 +3470,8 @@ async function resolveDeclaredFields(args) {
|
|
|
3285
3470
|
}
|
|
3286
3471
|
|
|
3287
3472
|
function missingRequiredInputs(args) {
|
|
3288
|
-
return args.entryDefs.filter(entry => entry.required === !0 && entry
|
|
3289
|
-
const match =
|
|
3473
|
+
return args.entryDefs.filter(entry => entry.required === !0 && invariants.isInputSourced(entry)).filter(entry => {
|
|
3474
|
+
const match = suppliedFieldFor(entry, args.initialFields);
|
|
3290
3475
|
return match === void 0 || match.value === void 0 || match.value === null;
|
|
3291
3476
|
}).map(entry => ({
|
|
3292
3477
|
name: entry.name,
|
|
@@ -3294,6 +3479,10 @@ function missingRequiredInputs(args) {
|
|
|
3294
3479
|
}));
|
|
3295
3480
|
}
|
|
3296
3481
|
|
|
3482
|
+
function suppliedFieldFor(entry, initialFields) {
|
|
3483
|
+
return initialFields.find(field => field.name === entry.name && field.type === entry.type);
|
|
3484
|
+
}
|
|
3485
|
+
|
|
3297
3486
|
function assertRequiredInputProvided({entryDefs: entryDefs, initialFields: initialFields, definitionName: definitionName}) {
|
|
3298
3487
|
const missing = missingRequiredInputs({
|
|
3299
3488
|
entryDefs: entryDefs,
|
|
@@ -3314,18 +3503,19 @@ function defaultEntryValue(entryType) {
|
|
|
3314
3503
|
}
|
|
3315
3504
|
|
|
3316
3505
|
function resolveInputValue({entry: entry, initialFields: initialFields, defaultValue: defaultValue}) {
|
|
3317
|
-
const initMatch =
|
|
3506
|
+
const initMatch = suppliedFieldFor(entry, initialFields);
|
|
3318
3507
|
return initMatch === void 0 ? defaultValue : (assertInputValueShape(entry, initMatch.value),
|
|
3319
3508
|
initMatch.value);
|
|
3320
3509
|
}
|
|
3321
3510
|
|
|
3322
|
-
function resolveFieldReadValue({source: source, ctx: ctx, defaultValue: defaultValue}) {
|
|
3323
|
-
const
|
|
3324
|
-
return
|
|
3325
|
-
kind:
|
|
3326
|
-
value:
|
|
3511
|
+
function resolveFieldReadValue({entry: entry, source: source, ctx: ctx, defaultValue: defaultValue}) {
|
|
3512
|
+
const sourceEntry = (source.scope === "workflow" ? ctx.workflowFields ?? [] : ctx.resolvedFields ?? []).find(s => s.name === source.field);
|
|
3513
|
+
return sourceEntry === void 0 ? defaultValue : resolveFieldRead({
|
|
3514
|
+
kind: sourceEntry._type,
|
|
3515
|
+
value: sourceEntry.value,
|
|
3327
3516
|
path: source.path,
|
|
3328
|
-
snapshot: void 0
|
|
3517
|
+
snapshot: void 0,
|
|
3518
|
+
targetKind: entry.type
|
|
3329
3519
|
}) ?? defaultValue;
|
|
3330
3520
|
}
|
|
3331
3521
|
|
|
@@ -3364,6 +3554,7 @@ async function resolveEntryValue({entry: entry, initialFields: initialFields, ct
|
|
|
3364
3554
|
ctx: ctx,
|
|
3365
3555
|
defaultValue: defaultValue
|
|
3366
3556
|
}) : source.type === "fieldRead" ? resolveFieldReadValue({
|
|
3557
|
+
entry: entry,
|
|
3367
3558
|
source: source,
|
|
3368
3559
|
ctx: ctx,
|
|
3369
3560
|
defaultValue: defaultValue
|
|
@@ -3581,7 +3772,7 @@ function loadCallContext({client: client, instanceId: instanceId, options: optio
|
|
|
3581
3772
|
|
|
3582
3773
|
async function retryOnRevisionConflict(args) {
|
|
3583
3774
|
const {client: client, instanceId: instanceId, options: options, commit: commit, onExhausted: onExhausted} = args;
|
|
3584
|
-
for (let attempt = 1; attempt <=
|
|
3775
|
+
for (let attempt = 1; attempt <= CONCURRENT_COMMIT_MAX_ATTEMPTS; attempt++) {
|
|
3585
3776
|
const ctx = await loadCallContext({
|
|
3586
3777
|
client: client,
|
|
3587
3778
|
instanceId: instanceId,
|
|
@@ -3849,7 +4040,7 @@ function startMutation(instance) {
|
|
|
3849
4040
|
})),
|
|
3850
4041
|
pendingEffects: [ ...instance.pendingEffects ],
|
|
3851
4042
|
effectHistory: [ ...instance.effectHistory ],
|
|
3852
|
-
|
|
4043
|
+
context: [ ...instance.context ],
|
|
3853
4044
|
history: [ ...instance.history ],
|
|
3854
4045
|
processedRequests: [ ...instance.processedRequests ?? [] ],
|
|
3855
4046
|
lastChangedAt: instance.lastChangedAt,
|
|
@@ -3873,7 +4064,7 @@ function instanceStateFields(src) {
|
|
|
3873
4064
|
subworkflows: src.subworkflows ?? [],
|
|
3874
4065
|
pendingEffects: src.pendingEffects,
|
|
3875
4066
|
effectHistory: src.effectHistory,
|
|
3876
|
-
|
|
4067
|
+
context: src.context,
|
|
3877
4068
|
history: src.history,
|
|
3878
4069
|
processedRequests: src.processedRequests ?? []
|
|
3879
4070
|
};
|
|
@@ -3881,6 +4072,15 @@ function instanceStateFields(src) {
|
|
|
3881
4072
|
fields;
|
|
3882
4073
|
}
|
|
3883
4074
|
|
|
4075
|
+
function liveViewContext(ctx, mutation) {
|
|
4076
|
+
const materialized = materializeInstance(ctx.instance, mutation);
|
|
4077
|
+
return {
|
|
4078
|
+
...ctx,
|
|
4079
|
+
instance: materialized,
|
|
4080
|
+
snapshot: overlayInstanceInSnapshot(ctx.snapshot, materialized)
|
|
4081
|
+
};
|
|
4082
|
+
}
|
|
4083
|
+
|
|
3884
4084
|
function materializeInstance(base, mutation) {
|
|
3885
4085
|
return {
|
|
3886
4086
|
...base,
|
|
@@ -3888,7 +4088,9 @@ function materializeInstance(base, mutation) {
|
|
|
3888
4088
|
fields: mutation.fields,
|
|
3889
4089
|
stages: mutation.stages,
|
|
3890
4090
|
subworkflows: mutation.subworkflows,
|
|
3891
|
-
|
|
4091
|
+
context: mutation.context,
|
|
4092
|
+
pendingEffects: mutation.pendingEffects,
|
|
4093
|
+
effectHistory: mutation.effectHistory
|
|
3892
4094
|
};
|
|
3893
4095
|
}
|
|
3894
4096
|
|
|
@@ -4053,22 +4255,27 @@ async function refreshStageGuards({ctx: ctx, mutation: mutation, stageName: stag
|
|
|
4053
4255
|
async function completeEffect(args) {
|
|
4054
4256
|
const {client: client, instanceId: instanceId, effectKey: effectKey, status: status, outputs: outputs, ops: ops, detail: detail, error: error, durationMs: durationMs, requestRecord: requestRecord, options: options} = args;
|
|
4055
4257
|
if (status !== "done" && status !== "failed") throw new invariants.ContractViolationError(`completeEffect: status must be "done" or "failed", got ${JSON.stringify(status)} — "cancelled" is engine-stamped by abort, never reportable`);
|
|
4056
|
-
|
|
4258
|
+
return retryOnRevisionConflict({
|
|
4057
4259
|
client: client,
|
|
4058
4260
|
instanceId: instanceId,
|
|
4059
|
-
options: options
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
|
|
4067
|
-
|
|
4068
|
-
|
|
4069
|
-
|
|
4070
|
-
|
|
4071
|
-
|
|
4261
|
+
options: options,
|
|
4262
|
+
commit: ctx => commitCompleteEffect({
|
|
4263
|
+
ctx: ctx,
|
|
4264
|
+
effectKey: effectKey,
|
|
4265
|
+
status: status,
|
|
4266
|
+
outputs: outputs,
|
|
4267
|
+
ops: ops,
|
|
4268
|
+
detail: detail,
|
|
4269
|
+
error: error,
|
|
4270
|
+
durationMs: durationMs,
|
|
4271
|
+
requestRecord: requestRecord,
|
|
4272
|
+
actor: options.actor
|
|
4273
|
+
}),
|
|
4274
|
+
onExhausted: () => new ConcurrentCompleteEffectError({
|
|
4275
|
+
instanceId: instanceId,
|
|
4276
|
+
effectKey: effectKey,
|
|
4277
|
+
attempts: CONCURRENT_COMMIT_MAX_ATTEMPTS
|
|
4278
|
+
})
|
|
4072
4279
|
});
|
|
4073
4280
|
}
|
|
4074
4281
|
|
|
@@ -4158,11 +4365,6 @@ function validateEffectOutputs(args) {
|
|
|
4158
4365
|
});
|
|
4159
4366
|
}
|
|
4160
4367
|
|
|
4161
|
-
function upsertEffectsContext({mutation: mutation, effectName: effectName, outputs: outputs}) {
|
|
4162
|
-
const existingIndex = mutation.effectsContext.findIndex(e => e.name === effectName), entry = effectsContextJsonEntry(effectName, outputs);
|
|
4163
|
-
existingIndex >= 0 ? mutation.effectsContext[existingIndex] = entry : mutation.effectsContext.push(entry);
|
|
4164
|
-
}
|
|
4165
|
-
|
|
4166
4368
|
function requirePendingEffect(instance, effectKey) {
|
|
4167
4369
|
const pending = instance.pendingEffects.find(e => e._key === effectKey);
|
|
4168
4370
|
if (pending !== void 0) return pending;
|
|
@@ -4230,12 +4432,8 @@ async function commitCompleteEffect({ctx: ctx, effectKey: effectKey, status: sta
|
|
|
4230
4432
|
outputs: outputs
|
|
4231
4433
|
});
|
|
4232
4434
|
mutation.effectHistory.push(settlement.run);
|
|
4233
|
-
const
|
|
4234
|
-
|
|
4235
|
-
mutation: mutation,
|
|
4236
|
-
effectName: pending.name,
|
|
4237
|
-
outputs: outputs
|
|
4238
|
-
}), mutation.history.push(settlement.event);
|
|
4435
|
+
const wroteEffectOutputs = status === "done" && outputs !== void 0;
|
|
4436
|
+
mutation.history.push(settlement.event);
|
|
4239
4437
|
const ranOps = await runOps({
|
|
4240
4438
|
ops: validatedOps,
|
|
4241
4439
|
mutation: mutation,
|
|
@@ -4248,7 +4446,7 @@ async function commitCompleteEffect({ctx: ctx, effectKey: effectKey, status: sta
|
|
|
4248
4446
|
self: invariants.selfGdr(ctx.instance),
|
|
4249
4447
|
now: ranAt,
|
|
4250
4448
|
snapshot: ctx.snapshot
|
|
4251
|
-
}), needsGuardRefresh =
|
|
4449
|
+
}), needsGuardRefresh = wroteEffectOutputs || ranOps.some(isFieldOp);
|
|
4252
4450
|
return await persistThenMaybeRefresh({
|
|
4253
4451
|
ctx: ctx,
|
|
4254
4452
|
mutation: mutation,
|
|
@@ -4418,6 +4616,60 @@ async function commitAbort({ctx: ctx, reason: reason, requestRecord: requestReco
|
|
|
4418
4616
|
};
|
|
4419
4617
|
}
|
|
4420
4618
|
|
|
4619
|
+
async function buildStageActivities({ctx: ctx, stage: stage, recordDiscard: recordDiscard}) {
|
|
4620
|
+
const entries = [];
|
|
4621
|
+
for (const activity of stage.activities ?? []) entries.push(await buildActivityEntry({
|
|
4622
|
+
ctx: ctx,
|
|
4623
|
+
stage: stage,
|
|
4624
|
+
activity: activity,
|
|
4625
|
+
recordDiscard: recordDiscard
|
|
4626
|
+
}));
|
|
4627
|
+
return entries;
|
|
4628
|
+
}
|
|
4629
|
+
|
|
4630
|
+
async function buildActivityEntry({ctx: ctx, stage: stage, activity: activity, recordDiscard: recordDiscard}) {
|
|
4631
|
+
const outcome = await ctxEvaluateConditionOutcome({
|
|
4632
|
+
ctx: ctx,
|
|
4633
|
+
condition: activity.filter,
|
|
4634
|
+
opts: {
|
|
4635
|
+
activityName: activity.name
|
|
4636
|
+
}
|
|
4637
|
+
}), inScope = outcome !== "unsatisfied", entry = {
|
|
4638
|
+
_key: randomKey(),
|
|
4639
|
+
name: activity.name,
|
|
4640
|
+
status: inScope ? "active" : "skipped",
|
|
4641
|
+
...activity.filter !== void 0 ? {
|
|
4642
|
+
filterEvaluation: buildFilterEvaluation({
|
|
4643
|
+
at: ctx.now,
|
|
4644
|
+
filter: activity.filter,
|
|
4645
|
+
outcome: outcome
|
|
4646
|
+
})
|
|
4647
|
+
} : {}
|
|
4648
|
+
};
|
|
4649
|
+
return inScope && (entry.startedAt = ctx.now, activity.fields !== void 0 && activity.fields.length > 0 && (entry.fields = await resolveActivityFieldEntries({
|
|
4650
|
+
client: ctx.client,
|
|
4651
|
+
instance: ctx.instance,
|
|
4652
|
+
stage: stage,
|
|
4653
|
+
activity: activity,
|
|
4654
|
+
now: ctx.now,
|
|
4655
|
+
...recordDiscard !== void 0 ? {
|
|
4656
|
+
recordDiscard: recordDiscard
|
|
4657
|
+
} : {}
|
|
4658
|
+
}))), entry;
|
|
4659
|
+
}
|
|
4660
|
+
|
|
4661
|
+
function buildFilterEvaluation({at: at, filter: filter, outcome: outcome}) {
|
|
4662
|
+
return outcome === "unevaluable" ? {
|
|
4663
|
+
at: at,
|
|
4664
|
+
truthy: !1,
|
|
4665
|
+
unevaluable: !0,
|
|
4666
|
+
detail: `Filter "${filter}" could not be evaluated (GROQ null — a referenced operand was missing or incomparable). Activity kept in scope so the gate is not silently skipped.`
|
|
4667
|
+
} : {
|
|
4668
|
+
at: at,
|
|
4669
|
+
truthy: outcome === "satisfied"
|
|
4670
|
+
};
|
|
4671
|
+
}
|
|
4672
|
+
|
|
4421
4673
|
async function discoverItems(args) {
|
|
4422
4674
|
const {client: client, groq: groq, params: params, perspective: perspective} = args;
|
|
4423
4675
|
return client.fetch(groq, paramsForLake(params), {
|
|
@@ -4436,65 +4688,28 @@ function clientForDiscovery({defaultClient: defaultClient, clientForGdr: clientF
|
|
|
4436
4688
|
|
|
4437
4689
|
const MAX_SPAWN_DEPTH = 6;
|
|
4438
4690
|
|
|
4439
|
-
function subworkflowsSettledGate(activityName) {
|
|
4440
|
-
return `count($subworkflows[activity == ${JSON.stringify(activityName)} && current && status == 'active']) == 0`;
|
|
4441
|
-
}
|
|
4442
|
-
|
|
4443
4691
|
function subworkflowsOnExit(sub) {
|
|
4444
4692
|
return sub.onExit ?? "detach";
|
|
4445
4693
|
}
|
|
4446
4694
|
|
|
4447
|
-
function
|
|
4448
|
-
|
|
4449
|
-
|
|
4450
|
-
|
|
4451
|
-
|
|
4452
|
-
|
|
4453
|
-
|
|
4454
|
-
|
|
4455
|
-
|
|
4456
|
-
|
|
4457
|
-
|
|
4458
|
-
|
|
4459
|
-
|
|
4460
|
-
function effectiveCompleteWhen(activity) {
|
|
4461
|
-
return activity.completeWhen ?? (activity.subworkflows !== void 0 ? subworkflowsSettledGate(activity.name) : void 0);
|
|
4462
|
-
}
|
|
4463
|
-
|
|
4464
|
-
async function evaluateResolutionGate({ctx: ctx, activity: activity}) {
|
|
4465
|
-
const scope = {
|
|
4466
|
-
activityName: activity.name
|
|
4467
|
-
};
|
|
4468
|
-
if (activity.failWhen !== void 0 && await ctxEvaluateCondition({
|
|
4469
|
-
ctx: ctx,
|
|
4470
|
-
condition: activity.failWhen,
|
|
4471
|
-
opts: scope
|
|
4472
|
-
})) return "failed";
|
|
4473
|
-
const completeWhen = effectiveCompleteWhen(activity);
|
|
4474
|
-
if (completeWhen !== void 0 && await ctxEvaluateCondition({
|
|
4475
|
-
ctx: ctx,
|
|
4476
|
-
condition: completeWhen,
|
|
4477
|
-
opts: scope
|
|
4478
|
-
})) return "done";
|
|
4479
|
-
}
|
|
4480
|
-
|
|
4481
|
-
async function spawnSubworkflows({ctx: ctx, mutation: mutation, activity: activity, sub: sub, actor: actor}) {
|
|
4482
|
-
assertSpawnDepth(ctx, activity);
|
|
4483
|
-
const openEntry = findOpenStageEntry(mutation);
|
|
4484
|
-
if (openEntry === void 0) throw new Error(`Mutation invariant broken: no open StageEntry while spawning on activity "${activity.name}" of ${ctx.instance._id}`);
|
|
4485
|
-
const definition = await requireChildDefinition({
|
|
4486
|
-
ctx: ctx,
|
|
4487
|
-
activity: activity,
|
|
4488
|
-
sub: sub
|
|
4489
|
-
}), parentScope = await ctxConditionParams(ctx, {
|
|
4490
|
-
activityName: activity.name,
|
|
4491
|
-
...actor ? {
|
|
4492
|
-
actor: actor
|
|
4695
|
+
async function spawnSubworkflows({ctx: ctx, mutation: mutation, activity: activity, action: action, sub: sub, actor: actor}) {
|
|
4696
|
+
assertSpawnDepth(ctx, activity);
|
|
4697
|
+
const openEntry = findOpenStageEntry(mutation);
|
|
4698
|
+
if (openEntry === void 0) throw new Error(`Mutation invariant broken: no open StageEntry while spawning on activity "${activity.name}" of ${ctx.instance._id}`);
|
|
4699
|
+
const definition = await requireChildDefinition({
|
|
4700
|
+
ctx: ctx,
|
|
4701
|
+
activity: activity,
|
|
4702
|
+
sub: sub
|
|
4703
|
+
}), parentScope = await ctxConditionParams(ctx, {
|
|
4704
|
+
activityName: activity.name,
|
|
4705
|
+
...actor ? {
|
|
4706
|
+
actor: actor
|
|
4493
4707
|
} : {}
|
|
4494
4708
|
}), {rows: rows, discoveryResource: discoveryResource} = await resolveSpawnRows(ctx, sub), keyedRows = keyRows(rows, {
|
|
4495
4709
|
activity: activity.name,
|
|
4710
|
+
action: action.name,
|
|
4496
4711
|
instanceId: ctx.instance._id
|
|
4497
|
-
}), live = liveSubworkflows(mutation).filter(r => r.activity === activity.name && r.definition === sub.definition.name && r.abortPending === void 0);
|
|
4712
|
+
}), live = liveSubworkflows(mutation).filter(r => r.activity === activity.name && r.action === action.name && r.definition === sub.definition.name && r.abortPending === void 0);
|
|
4498
4713
|
adoptRediscoveredRows({
|
|
4499
4714
|
mutation: mutation,
|
|
4500
4715
|
live: live,
|
|
@@ -4503,7 +4718,7 @@ async function spawnSubworkflows({ctx: ctx, mutation: mutation, activity: activi
|
|
|
4503
4718
|
activity: activity,
|
|
4504
4719
|
now: ctx.now
|
|
4505
4720
|
});
|
|
4506
|
-
const
|
|
4721
|
+
const context = await resolveContextHandoff({
|
|
4507
4722
|
ctx: ctx,
|
|
4508
4723
|
sub: sub,
|
|
4509
4724
|
parentScope: parentScope
|
|
@@ -4512,10 +4727,11 @@ async function spawnSubworkflows({ctx: ctx, mutation: mutation, activity: activi
|
|
|
4512
4727
|
ctx: ctx,
|
|
4513
4728
|
mutation: mutation,
|
|
4514
4729
|
activity: activity,
|
|
4730
|
+
action: action,
|
|
4515
4731
|
sub: sub,
|
|
4516
4732
|
definition: definition,
|
|
4517
4733
|
parentScope: parentScope,
|
|
4518
|
-
|
|
4734
|
+
context: context,
|
|
4519
4735
|
discoveryResource: discoveryResource,
|
|
4520
4736
|
openEntry: openEntry,
|
|
4521
4737
|
keyed: keyed,
|
|
@@ -4553,7 +4769,7 @@ function adoptRediscoveredRows({mutation: mutation, live: live, keyedRows: keyed
|
|
|
4553
4769
|
}));
|
|
4554
4770
|
}
|
|
4555
4771
|
|
|
4556
|
-
async function spawnRow({ctx: ctx, mutation: mutation, activity: activity, sub: sub, definition: definition, parentScope: parentScope,
|
|
4772
|
+
async function spawnRow({ctx: ctx, mutation: mutation, activity: activity, action: action, sub: sub, definition: definition, parentScope: parentScope, context: context, discoveryResource: discoveryResource, openEntry: openEntry, keyed: keyed, actor: actor}) {
|
|
4557
4773
|
const now = ctx.now, initialFields = await projectRowFields({
|
|
4558
4774
|
ctx: ctx,
|
|
4559
4775
|
sub: sub,
|
|
@@ -4566,7 +4782,7 @@ async function spawnRow({ctx: ctx, mutation: mutation, activity: activity, sub:
|
|
|
4566
4782
|
parent: ctx.instance,
|
|
4567
4783
|
definition: definition,
|
|
4568
4784
|
initialFields: initialFields,
|
|
4569
|
-
|
|
4785
|
+
context: context,
|
|
4570
4786
|
actor: actor,
|
|
4571
4787
|
now: now
|
|
4572
4788
|
});
|
|
@@ -4581,6 +4797,7 @@ async function spawnRow({ctx: ctx, mutation: mutation, activity: activity, sub:
|
|
|
4581
4797
|
}), mutation.subworkflows.push({
|
|
4582
4798
|
_key: randomKey(),
|
|
4583
4799
|
activity: activity.name,
|
|
4800
|
+
action: action.name,
|
|
4584
4801
|
definition: sub.definition.name,
|
|
4585
4802
|
stageEntry: openEntry._key,
|
|
4586
4803
|
cohortStage: openEntry.name,
|
|
@@ -4606,7 +4823,7 @@ function keyRows(rows, cx) {
|
|
|
4606
4823
|
rowKey: spawnRowKey(row, cx)
|
|
4607
4824
|
})), seen = /* @__PURE__ */ new Set;
|
|
4608
4825
|
for (const {rowKey: rowKey} of keyed) {
|
|
4609
|
-
if (seen.has(rowKey)) throw new Error(`
|
|
4826
|
+
if (seen.has(rowKey)) throw new Error(`spawn.forEach on action "${cx.action}" (activity "${cx.activity}") of ${cx.instanceId} produced two rows with the same identity "${rowKey}" — adopt-matching cannot tell them apart. Make each row's identity unique: distinct \`_key\`/\`_id\`/GDR \`id\` values for object rows, distinct values for scalar rows.`);
|
|
4610
4827
|
seen.add(rowKey);
|
|
4611
4828
|
}
|
|
4612
4829
|
return keyed;
|
|
@@ -4620,15 +4837,19 @@ function spawnRowKey(row, cx) {
|
|
|
4620
4837
|
if (typeof _id == "string" && _id.length > 0) return _id;
|
|
4621
4838
|
if (invariants.isGdr(row) && invariants.isGdrUri(row.id)) return row.id;
|
|
4622
4839
|
}
|
|
4623
|
-
throw new Error(`
|
|
4840
|
+
throw new Error(`spawn.forEach on action "${cx.action}" (activity "${cx.activity}") of ${cx.instanceId} produced a row without an identity, so re-entering the stage could not tell "same row" from "new row". Project \`_id\` (document rows) or \`_key\` (array items), keep GDR rows whole (a \`{id, type}\` row with a GDR-URI \`id\` — the shape a \`doc.refs\` field stores — keys on its \`id\`), or mint a \`_key\` for synthetic rows (e.g. \`{"_key": lang + "-" + market, ...}\`); scalar rows key on their value. Row: ${JSON.stringify(row)}`);
|
|
4624
4841
|
}
|
|
4625
4842
|
|
|
4626
4843
|
function condemnOnExitCohorts({mutation: mutation, stage: stage, exitedEntry: exitedEntry, at: at}) {
|
|
4627
|
-
|
|
4628
|
-
|
|
4629
|
-
|
|
4844
|
+
const abortCohorts = (stage.activities ?? []).flatMap(activity => (activity.actions ?? []).filter(action => action.spawn !== void 0 && subworkflowsOnExit(action.spawn) === "abort").map(action => ({
|
|
4845
|
+
activity: activity.name,
|
|
4846
|
+
action: action.name
|
|
4847
|
+
})));
|
|
4848
|
+
for (const cohort of abortCohorts) {
|
|
4849
|
+
const owed = liveSubworkflows(mutation).filter(row => row.activity === cohort.activity && row.action === cohort.action && row.stageEntry === exitedEntry._key);
|
|
4850
|
+
for (const row of owed) condemnSubworkflow(row, {
|
|
4630
4851
|
at: at,
|
|
4631
|
-
reason: `stage "${stage.name}" exited with the child still live (onExit: "abort" on activity "${activity
|
|
4852
|
+
reason: `stage "${stage.name}" exited with the child still live (onExit: "abort" on action "${cohort.action}" of activity "${cohort.activity}")`
|
|
4632
4853
|
});
|
|
4633
4854
|
}
|
|
4634
4855
|
}
|
|
@@ -4679,7 +4900,7 @@ async function projectRowFields({ctx: ctx, sub: sub, childDefinition: childDefin
|
|
|
4679
4900
|
const out = [];
|
|
4680
4901
|
for (const [name, groq] of Object.entries(sub.with ?? {})) {
|
|
4681
4902
|
const declared = (childDefinition.fields ?? []).find(e => e.name === name);
|
|
4682
|
-
if (declared === void 0) throw new Error(`
|
|
4903
|
+
if (declared === void 0) throw new Error(`spawn.with["${name}"]: subworkflow "${childDefinition.name}" declares no field entry "${name}"`);
|
|
4683
4904
|
const raw = await invariants.runGroq({
|
|
4684
4905
|
groq: groq,
|
|
4685
4906
|
params: {
|
|
@@ -4709,7 +4930,7 @@ async function projectRowFields({ctx: ctx, sub: sub, childDefinition: childDefin
|
|
|
4709
4930
|
types: declared.types,
|
|
4710
4931
|
value: value
|
|
4711
4932
|
}).includes("document") ? ' A projected bare id or `_ref` canonicalises with the generic type "document" — project the target\'s schema type alongside its id (e.g. {"id": _id, "type": _type}).' : "";
|
|
4712
|
-
throw new Error(`
|
|
4933
|
+
throw new Error(`spawn.with["${name}"] on subworkflow "${childDefinition.name}": ${typeIssues.join("; ")}.${hint}`);
|
|
4713
4934
|
}
|
|
4714
4935
|
out.push({
|
|
4715
4936
|
type: declared.type,
|
|
@@ -4740,7 +4961,7 @@ async function resolveContextHandoff({ctx: ctx, sub: sub, parentScope: parentSco
|
|
|
4740
4961
|
};
|
|
4741
4962
|
continue;
|
|
4742
4963
|
}
|
|
4743
|
-
throw new Error(`
|
|
4964
|
+
throw new Error(`spawn.context["${name}"] evaluated to ${JSON.stringify(v2)}, which the handoff cannot carry. A context entry seeds the child's $context bag with a string, number, boolean, or a document reference — a full GDR \`{id, type}\` whose \`id\` is a "<scheme>:..." URI (parent-scope ids are already canonical).`);
|
|
4744
4965
|
}
|
|
4745
4966
|
}
|
|
4746
4967
|
return out;
|
|
@@ -4751,7 +4972,7 @@ function canonicaliseSpawnValue({kind: kind, value: value, cx: cx}) {
|
|
|
4751
4972
|
}
|
|
4752
4973
|
|
|
4753
4974
|
function assertBareIdRootsCorrectly(id, cx) {
|
|
4754
|
-
if (!(cx.discoveryResource === void 0 || invariants.sameResource(cx.discoveryResource, cx.parentResource))) throw new Error(`
|
|
4975
|
+
if (!(cx.discoveryResource === void 0 || invariants.sameResource(cx.discoveryResource, cx.parentResource))) throw new Error(`spawn.with["${cx.entryName}"]: subworkflow "${cx.childName}" projected the bare id "${id}", which roots at the parent resource "${cx.parentResource.id}", but spawn discovery ran against "${cx.discoveryResource.id}". The child would reference a document that doesn't exist in the parent resource. Project an explicit GDR URI in the with-GROQ (e.g. "dataset:<projectId>:<dataset>:" + _id) so the child's subject points at the resource the discovered rows came from.`);
|
|
4755
4976
|
}
|
|
4756
4977
|
|
|
4757
4978
|
function coerceSpawnGdr(raw, cx) {
|
|
@@ -4786,7 +5007,7 @@ async function resolveDefinitionRef({client: client, ref: ref, tag: tag}) {
|
|
|
4786
5007
|
}
|
|
4787
5008
|
|
|
4788
5009
|
async function prepareChildInstance(args) {
|
|
4789
|
-
const {client: client, parent: parent, definition: definition, initialFields: initialFields,
|
|
5010
|
+
const {client: client, parent: parent, definition: definition, initialFields: initialFields, context: context, actor: actor, now: now} = args, childTag = parent.tag, workflowResource = parent.workflowResource, childDocId = instanceDocId(childTag), childRef = {
|
|
4790
5011
|
id: invariants.gdrFromResource(workflowResource, childDocId),
|
|
4791
5012
|
type: WORKFLOW_INSTANCE_TYPE
|
|
4792
5013
|
}, ancestors = [ ...parent.ancestors, {
|
|
@@ -4812,7 +5033,7 @@ async function prepareChildInstance(args) {
|
|
|
4812
5033
|
})
|
|
4813
5034
|
},
|
|
4814
5035
|
randomKey: randomKey
|
|
4815
|
-
}), childPerspective = derivePerspectiveFromFields(childFields) ?? inheritedPerspective,
|
|
5036
|
+
}), childPerspective = derivePerspectiveFromFields(childFields) ?? inheritedPerspective, contextEntries = Object.entries(context).map(([key, value]) => contextEntry(key, value)), base = buildInstanceBase({
|
|
4816
5037
|
id: childDocId,
|
|
4817
5038
|
now: now,
|
|
4818
5039
|
tag: childTag,
|
|
@@ -4822,7 +5043,7 @@ async function prepareChildInstance(args) {
|
|
|
4822
5043
|
pinnedContentHash: definition.contentHash,
|
|
4823
5044
|
definition: definition,
|
|
4824
5045
|
fields: childFields,
|
|
4825
|
-
|
|
5046
|
+
context: contextEntries,
|
|
4826
5047
|
ancestors: ancestors,
|
|
4827
5048
|
perspective: childPerspective,
|
|
4828
5049
|
initialStage: definition.initialStage,
|
|
@@ -4837,182 +5058,6 @@ async function prepareChildInstance(args) {
|
|
|
4837
5058
|
};
|
|
4838
5059
|
}
|
|
4839
5060
|
|
|
4840
|
-
async function invokeActivity(args) {
|
|
4841
|
-
const {client: client, instanceId: instanceId, activity: activityName, options: options} = args, ctx = await loadCallContext({
|
|
4842
|
-
client: client,
|
|
4843
|
-
instanceId: instanceId,
|
|
4844
|
-
options: options
|
|
4845
|
-
});
|
|
4846
|
-
return commitInvoke({
|
|
4847
|
-
ctx: ctx,
|
|
4848
|
-
activityName: activityName,
|
|
4849
|
-
actor: options?.actor
|
|
4850
|
-
});
|
|
4851
|
-
}
|
|
4852
|
-
|
|
4853
|
-
async function commitInvoke({ctx: ctx, activityName: activityName, actor: actor}) {
|
|
4854
|
-
const {activity: activity} = findActivityInCurrentStage(ctx, activityName), entry = findCurrentActivities(ctx.instance).find(t => t.name === activityName);
|
|
4855
|
-
if (entry === void 0) throw new Error(`Activity "${activityName}" has no status entry on instance ${ctx.instance._id}`);
|
|
4856
|
-
if (entry.status !== "pending") return {
|
|
4857
|
-
invoked: !1,
|
|
4858
|
-
activity: activityName
|
|
4859
|
-
};
|
|
4860
|
-
const mutation = startMutation(ctx.instance), mutEntry = requireMutationActivityEntry(mutation, activityName);
|
|
4861
|
-
return await activateActivity({
|
|
4862
|
-
ctx: ctx,
|
|
4863
|
-
mutation: mutation,
|
|
4864
|
-
activity: activity,
|
|
4865
|
-
entry: mutEntry,
|
|
4866
|
-
actor: actor
|
|
4867
|
-
}), await persist(ctx, mutation), {
|
|
4868
|
-
invoked: !0,
|
|
4869
|
-
activity: activityName
|
|
4870
|
-
};
|
|
4871
|
-
}
|
|
4872
|
-
|
|
4873
|
-
async function activateActivity({ctx: ctx, mutation: mutation, activity: activity, entry: entry, actor: actor}) {
|
|
4874
|
-
const now = ctx.now;
|
|
4875
|
-
if (entry.status = "active", entry.startedAt = now, activity.fields !== void 0 && activity.fields.length > 0) {
|
|
4876
|
-
const stage = findStage(ctx.definition, mutation.currentStage);
|
|
4877
|
-
entry.fields = await resolveActivityFieldEntries({
|
|
4878
|
-
client: ctx.client,
|
|
4879
|
-
instance: ctx.instance,
|
|
4880
|
-
stage: stage,
|
|
4881
|
-
activity: activity,
|
|
4882
|
-
now: now,
|
|
4883
|
-
recordDiscard: recordFieldDiscards({
|
|
4884
|
-
target: mutation.history,
|
|
4885
|
-
scope: "activity",
|
|
4886
|
-
at: now
|
|
4887
|
-
})
|
|
4888
|
-
});
|
|
4889
|
-
}
|
|
4890
|
-
await runOps({
|
|
4891
|
-
ops: activity.ops,
|
|
4892
|
-
mutation: mutation,
|
|
4893
|
-
stage: mutation.currentStage,
|
|
4894
|
-
origin: {
|
|
4895
|
-
activity: activity.name
|
|
4896
|
-
},
|
|
4897
|
-
params: {},
|
|
4898
|
-
actor: actor,
|
|
4899
|
-
self: invariants.selfGdr(ctx.instance),
|
|
4900
|
-
now: now,
|
|
4901
|
-
snapshot: ctx.snapshot
|
|
4902
|
-
}), mutation.history.push({
|
|
4903
|
-
_key: randomKey(),
|
|
4904
|
-
_type: "activityActivated",
|
|
4905
|
-
at: now,
|
|
4906
|
-
stage: mutation.currentStage,
|
|
4907
|
-
activity: activity.name,
|
|
4908
|
-
...actor !== void 0 ? {
|
|
4909
|
-
actor: actor
|
|
4910
|
-
} : {}
|
|
4911
|
-
}), await queueEffects({
|
|
4912
|
-
ctx: ctx,
|
|
4913
|
-
mutation: mutation,
|
|
4914
|
-
effects: activity.effects,
|
|
4915
|
-
origin: {
|
|
4916
|
-
kind: "activity",
|
|
4917
|
-
name: activity.name
|
|
4918
|
-
},
|
|
4919
|
-
actor: actor,
|
|
4920
|
-
opts: {
|
|
4921
|
-
activityName: activity.name
|
|
4922
|
-
}
|
|
4923
|
-
}), activity.subworkflows !== void 0 && await spawnSubworkflows({
|
|
4924
|
-
ctx: ctx,
|
|
4925
|
-
mutation: mutation,
|
|
4926
|
-
activity: activity,
|
|
4927
|
-
sub: activity.subworkflows,
|
|
4928
|
-
actor: actor
|
|
4929
|
-
}), !await autoResolveOnActivate({
|
|
4930
|
-
ctx: ctx,
|
|
4931
|
-
mutation: mutation,
|
|
4932
|
-
activity: activity,
|
|
4933
|
-
entry: entry,
|
|
4934
|
-
now: now,
|
|
4935
|
-
actor: actor
|
|
4936
|
-
}) && resolveMachineStep({
|
|
4937
|
-
mutation: mutation,
|
|
4938
|
-
activity: activity,
|
|
4939
|
-
entry: entry,
|
|
4940
|
-
now: now,
|
|
4941
|
-
actor: actor
|
|
4942
|
-
});
|
|
4943
|
-
}
|
|
4944
|
-
|
|
4945
|
-
async function autoResolveOnActivate({ctx: ctx, mutation: mutation, activity: activity, entry: entry, now: now, actor: actor}) {
|
|
4946
|
-
if (activity.failWhen === void 0 && effectiveCompleteWhen(activity) === void 0) return !1;
|
|
4947
|
-
const gateCtx = {
|
|
4948
|
-
...ctx,
|
|
4949
|
-
instance: materializeInstance(ctx.instance, mutation)
|
|
4950
|
-
}, outcome = await evaluateResolutionGate({
|
|
4951
|
-
ctx: gateCtx,
|
|
4952
|
-
activity: activity
|
|
4953
|
-
});
|
|
4954
|
-
return outcome === void 0 ? !1 : (applyGateOutcome({
|
|
4955
|
-
entry: entry,
|
|
4956
|
-
history: mutation.history,
|
|
4957
|
-
stage: mutation.currentStage,
|
|
4958
|
-
at: now,
|
|
4959
|
-
outcome: outcome,
|
|
4960
|
-
actor: actor
|
|
4961
|
-
}), !0);
|
|
4962
|
-
}
|
|
4963
|
-
|
|
4964
|
-
function resolveMachineStep({mutation: mutation, activity: activity, entry: entry, now: now, actor: actor}) {
|
|
4965
|
-
const waitsForActor = activity.actions !== void 0 && activity.actions.length > 0, waitsForCondition = activity.completeWhen !== void 0 || activity.subworkflows !== void 0;
|
|
4966
|
-
waitsForActor || waitsForCondition || applyActivityStatusChange({
|
|
4967
|
-
entry: entry,
|
|
4968
|
-
history: mutation.history,
|
|
4969
|
-
stage: mutation.currentStage,
|
|
4970
|
-
to: "done",
|
|
4971
|
-
at: now,
|
|
4972
|
-
...actor !== void 0 ? {
|
|
4973
|
-
actor: actor
|
|
4974
|
-
} : {}
|
|
4975
|
-
});
|
|
4976
|
-
}
|
|
4977
|
-
|
|
4978
|
-
async function buildStageActivities(ctx, stage) {
|
|
4979
|
-
const entries = [];
|
|
4980
|
-
for (const activity of stage.activities ?? []) {
|
|
4981
|
-
const outcome = await ctxEvaluateConditionOutcome({
|
|
4982
|
-
ctx: ctx,
|
|
4983
|
-
condition: activity.filter,
|
|
4984
|
-
opts: {
|
|
4985
|
-
activityName: activity.name
|
|
4986
|
-
}
|
|
4987
|
-
}), inScope = outcome !== "unsatisfied";
|
|
4988
|
-
entries.push({
|
|
4989
|
-
_key: randomKey(),
|
|
4990
|
-
name: activity.name,
|
|
4991
|
-
status: inScope ? "pending" : "skipped",
|
|
4992
|
-
...activity.filter !== void 0 ? {
|
|
4993
|
-
filterEvaluation: buildFilterEvaluation({
|
|
4994
|
-
at: ctx.now,
|
|
4995
|
-
filter: activity.filter,
|
|
4996
|
-
outcome: outcome
|
|
4997
|
-
})
|
|
4998
|
-
} : {}
|
|
4999
|
-
});
|
|
5000
|
-
}
|
|
5001
|
-
return entries;
|
|
5002
|
-
}
|
|
5003
|
-
|
|
5004
|
-
function buildFilterEvaluation({at: at, filter: filter, outcome: outcome}) {
|
|
5005
|
-
return outcome === "unevaluable" ? {
|
|
5006
|
-
at: at,
|
|
5007
|
-
truthy: !1,
|
|
5008
|
-
unevaluable: !0,
|
|
5009
|
-
detail: `Filter "${filter}" could not be evaluated (GROQ null — a referenced operand was missing or incomparable). Activity kept in scope so the gate is not silently skipped.`
|
|
5010
|
-
} : {
|
|
5011
|
-
at: at,
|
|
5012
|
-
truthy: outcome === "satisfied"
|
|
5013
|
-
};
|
|
5014
|
-
}
|
|
5015
|
-
|
|
5016
5061
|
function isTerminalStage(stage) {
|
|
5017
5062
|
return (stage.transitions ?? []).length === 0;
|
|
5018
5063
|
}
|
|
@@ -5032,13 +5077,13 @@ async function setStage(args) {
|
|
|
5032
5077
|
});
|
|
5033
5078
|
}
|
|
5034
5079
|
|
|
5035
|
-
async function enterStage({ctx: ctx, mutation: mutation, nextStage: nextStage,
|
|
5080
|
+
async function enterStage({ctx: ctx, mutation: mutation, nextStage: nextStage, at: at}) {
|
|
5036
5081
|
mutation.currentStage = nextStage.name;
|
|
5037
5082
|
const entryCtx = {
|
|
5038
5083
|
...ctx,
|
|
5039
5084
|
instance: materializeInstance(ctx.instance, mutation)
|
|
5040
5085
|
}, nextStageEntry = {
|
|
5041
|
-
_key:
|
|
5086
|
+
_key: randomKey(),
|
|
5042
5087
|
name: nextStage.name,
|
|
5043
5088
|
enteredAt: at,
|
|
5044
5089
|
fields: await resolveStageFieldEntries({
|
|
@@ -5054,19 +5099,15 @@ async function enterStage({ctx: ctx, mutation: mutation, nextStage: nextStage, a
|
|
|
5054
5099
|
}),
|
|
5055
5100
|
activities: []
|
|
5056
5101
|
};
|
|
5057
|
-
mutation.stages.push(nextStageEntry), nextStageEntry.activities = await buildStageActivities(
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
actor: actor
|
|
5067
|
-
});
|
|
5068
|
-
}
|
|
5069
|
-
isTerminalStage(nextStage) && (mutation.completedAt = at);
|
|
5102
|
+
mutation.stages.push(nextStageEntry), nextStageEntry.activities = await buildStageActivities({
|
|
5103
|
+
ctx: entryCtx,
|
|
5104
|
+
stage: nextStage,
|
|
5105
|
+
recordDiscard: recordFieldDiscards({
|
|
5106
|
+
target: mutation.history,
|
|
5107
|
+
scope: "activity",
|
|
5108
|
+
at: at
|
|
5109
|
+
})
|
|
5110
|
+
}), isTerminalStage(nextStage) && (mutation.completedAt = at);
|
|
5070
5111
|
}
|
|
5071
5112
|
|
|
5072
5113
|
function isTerminal(ctx) {
|
|
@@ -5091,13 +5132,34 @@ async function commitSetStage({ctx: ctx, targetStage: targetStage, reason: reaso
|
|
|
5091
5132
|
record: requestRecord,
|
|
5092
5133
|
now: ctx.now
|
|
5093
5134
|
});
|
|
5094
|
-
const
|
|
5095
|
-
return
|
|
5135
|
+
const transitionName = `setStage:${currentStage.name}->${nextStage.name}`;
|
|
5136
|
+
return await commitStageMove({
|
|
5137
|
+
ctx: ctx,
|
|
5138
|
+
mutation: mutation,
|
|
5139
|
+
fromStage: currentStage,
|
|
5140
|
+
toStage: nextStage,
|
|
5141
|
+
transition: transitionName,
|
|
5142
|
+
via: "setStage",
|
|
5143
|
+
actor: actor,
|
|
5144
|
+
reason: reason
|
|
5145
|
+
}), {
|
|
5146
|
+
fired: !0,
|
|
5096
5147
|
fromStage: currentStage.name,
|
|
5097
5148
|
toStage: nextStage.name,
|
|
5149
|
+
transition: transitionName
|
|
5150
|
+
};
|
|
5151
|
+
}
|
|
5152
|
+
|
|
5153
|
+
async function commitStageMove({ctx: ctx, mutation: mutation, fromStage: fromStage, toStage: toStage, transition: transition, via: via, actor: actor, reason: reason}) {
|
|
5154
|
+
const at = ctx.now;
|
|
5155
|
+
mutation.history.push(...stageTransitionHistory({
|
|
5156
|
+
fromStage: fromStage.name,
|
|
5157
|
+
toStage: toStage.name,
|
|
5098
5158
|
at: at,
|
|
5099
|
-
transition:
|
|
5100
|
-
via
|
|
5159
|
+
transition: transition,
|
|
5160
|
+
...via === "setStage" ? {
|
|
5161
|
+
via: via
|
|
5162
|
+
} : {},
|
|
5101
5163
|
...actor !== void 0 ? {
|
|
5102
5164
|
actor: actor
|
|
5103
5165
|
} : {},
|
|
@@ -5106,30 +5168,24 @@ async function commitSetStage({ctx: ctx, targetStage: targetStage, reason: reaso
|
|
|
5106
5168
|
} : {}
|
|
5107
5169
|
})), exitOpenStage({
|
|
5108
5170
|
mutation: mutation,
|
|
5109
|
-
stage:
|
|
5171
|
+
stage: fromStage,
|
|
5110
5172
|
at: at
|
|
5111
5173
|
}), await enterStage({
|
|
5112
5174
|
ctx: ctx,
|
|
5113
5175
|
mutation: mutation,
|
|
5114
|
-
nextStage:
|
|
5115
|
-
actor: actor,
|
|
5176
|
+
nextStage: toStage,
|
|
5116
5177
|
at: at
|
|
5117
5178
|
}), await persistStageMove({
|
|
5118
5179
|
ctx: ctx,
|
|
5119
5180
|
mutation: mutation,
|
|
5120
|
-
exitedStage:
|
|
5121
|
-
enteredStage:
|
|
5181
|
+
exitedStage: fromStage.name,
|
|
5182
|
+
enteredStage: toStage.name
|
|
5122
5183
|
}), emitStageTransitioned({
|
|
5123
5184
|
ctx: ctx,
|
|
5124
|
-
nextStage:
|
|
5185
|
+
nextStage: toStage,
|
|
5125
5186
|
at: at,
|
|
5126
|
-
via:
|
|
5127
|
-
})
|
|
5128
|
-
fired: !0,
|
|
5129
|
-
fromStage: currentStage.name,
|
|
5130
|
-
toStage: nextStage.name,
|
|
5131
|
-
transition: transitionName
|
|
5132
|
-
};
|
|
5187
|
+
via: via
|
|
5188
|
+
});
|
|
5133
5189
|
}
|
|
5134
5190
|
|
|
5135
5191
|
function emitStageTransitioned({ctx: ctx, nextStage: nextStage, at: at, via: via}) {
|
|
@@ -5177,893 +5233,321 @@ async function persistStageMove({ctx: ctx, mutation: mutation, exitedStage: exit
|
|
|
5177
5233
|
});
|
|
5178
5234
|
}
|
|
5179
5235
|
|
|
5180
|
-
async function commitTransition(ctx, actor) {
|
|
5181
|
-
if (isTerminal(ctx)) return {
|
|
5182
|
-
fired: !1
|
|
5183
|
-
};
|
|
5184
|
-
const currentStage = findStage(ctx.definition, ctx.instance.currentStage), transition = await pickTransition(ctx, currentStage);
|
|
5185
|
-
if (transition === void 0) return {
|
|
5186
|
-
fired: !1
|
|
5187
|
-
};
|
|
5188
|
-
const mutation = startMutation(ctx.instance), at = ctx.now;
|
|
5189
|
-
await runOps({
|
|
5190
|
-
ops: transition.ops,
|
|
5191
|
-
mutation: mutation,
|
|
5192
|
-
stage: currentStage.name,
|
|
5193
|
-
origin: {
|
|
5194
|
-
transition: transition.name
|
|
5195
|
-
},
|
|
5196
|
-
params: {},
|
|
5197
|
-
actor: actor,
|
|
5198
|
-
self: invariants.selfGdr(ctx.instance),
|
|
5199
|
-
now: at,
|
|
5200
|
-
snapshot: ctx.snapshot
|
|
5201
|
-
});
|
|
5202
|
-
const nextEntryKey = randomKey();
|
|
5203
|
-
await queueEffects({
|
|
5204
|
-
ctx: ctx,
|
|
5205
|
-
mutation: mutation,
|
|
5206
|
-
effects: transition.effects,
|
|
5207
|
-
origin: {
|
|
5208
|
-
kind: "transition",
|
|
5209
|
-
name: transition.name
|
|
5210
|
-
},
|
|
5211
|
-
actor: actor,
|
|
5212
|
-
opts: {
|
|
5213
|
-
stageEntryKey: nextEntryKey
|
|
5214
|
-
}
|
|
5215
|
-
}), mutation.history.push(...stageTransitionHistory({
|
|
5216
|
-
fromStage: currentStage.name,
|
|
5217
|
-
toStage: transition.to,
|
|
5218
|
-
at: at,
|
|
5219
|
-
transition: transition.name,
|
|
5220
|
-
...actor !== void 0 ? {
|
|
5221
|
-
actor: actor
|
|
5222
|
-
} : {}
|
|
5223
|
-
})), exitOpenStage({
|
|
5224
|
-
mutation: mutation,
|
|
5225
|
-
stage: currentStage,
|
|
5226
|
-
at: at
|
|
5227
|
-
});
|
|
5228
|
-
const nextStage = findStage(ctx.definition, transition.to);
|
|
5229
|
-
return await enterStage({
|
|
5230
|
-
ctx: ctx,
|
|
5231
|
-
mutation: mutation,
|
|
5232
|
-
nextStage: nextStage,
|
|
5233
|
-
actor: actor,
|
|
5234
|
-
at: at,
|
|
5235
|
-
entryKey: nextEntryKey
|
|
5236
|
-
}), await persistStageMove({
|
|
5237
|
-
ctx: ctx,
|
|
5238
|
-
mutation: mutation,
|
|
5239
|
-
exitedStage: currentStage.name,
|
|
5240
|
-
enteredStage: nextStage.name
|
|
5241
|
-
}), emitStageTransitioned({
|
|
5242
|
-
ctx: ctx,
|
|
5243
|
-
nextStage: nextStage,
|
|
5244
|
-
at: at,
|
|
5245
|
-
via: "transition"
|
|
5246
|
-
}), {
|
|
5247
|
-
fired: !0,
|
|
5248
|
-
fromStage: currentStage.name,
|
|
5249
|
-
toStage: transition.to,
|
|
5250
|
-
transition: transition.name
|
|
5251
|
-
};
|
|
5252
|
-
}
|
|
5253
|
-
|
|
5254
5236
|
async function pickTransition(ctx, stage) {
|
|
5255
5237
|
for (const candidate of stage.transitions ?? []) {
|
|
5256
5238
|
const outcome = await ctxEvaluateConditionOutcome({
|
|
5257
5239
|
ctx: ctx,
|
|
5258
|
-
condition: candidate.
|
|
5240
|
+
condition: candidate.when
|
|
5259
5241
|
});
|
|
5260
5242
|
if (outcome === "satisfied") return candidate;
|
|
5261
5243
|
if (outcome === "unevaluable") return;
|
|
5262
5244
|
}
|
|
5263
5245
|
}
|
|
5264
5246
|
|
|
5265
|
-
|
|
5266
|
-
const {client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, clock: clock, executionContext: executionContext, telemetry: telemetry, overlay: overlay} = args, ctx = await loadContext({
|
|
5267
|
-
client: client,
|
|
5268
|
-
instanceId: instanceId,
|
|
5269
|
-
options: {
|
|
5270
|
-
clientForGdr: clientForGdr,
|
|
5271
|
-
...actor ? {
|
|
5272
|
-
actor: actor
|
|
5273
|
-
} : {},
|
|
5274
|
-
...clock ? {
|
|
5275
|
-
clock: clock
|
|
5276
|
-
} : {},
|
|
5277
|
-
...executionContext ? {
|
|
5278
|
-
executionContext: executionContext
|
|
5279
|
-
} : {},
|
|
5280
|
-
...telemetry ? {
|
|
5281
|
-
telemetry: telemetry
|
|
5282
|
-
} : {},
|
|
5283
|
-
...overlay ? {
|
|
5284
|
-
overlay: overlay
|
|
5285
|
-
} : {}
|
|
5286
|
-
}
|
|
5287
|
-
});
|
|
5288
|
-
return commitTransition(ctx, actor);
|
|
5289
|
-
}
|
|
5247
|
+
const parsedFilters = /* @__PURE__ */ new Map;
|
|
5290
5248
|
|
|
5291
|
-
async function
|
|
5292
|
-
const
|
|
5293
|
-
|
|
5294
|
-
const
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
clientForGdr: clientForGdr,
|
|
5299
|
-
instance: instance,
|
|
5300
|
-
definition: definition,
|
|
5301
|
-
...clock ? {
|
|
5302
|
-
clock: clock
|
|
5303
|
-
} : {},
|
|
5304
|
-
...actor ? {
|
|
5305
|
-
actor: actor
|
|
5306
|
-
} : {},
|
|
5307
|
-
...executionContext ? {
|
|
5308
|
-
executionContext: executionContext
|
|
5309
|
-
} : {},
|
|
5310
|
-
...telemetry ? {
|
|
5311
|
-
telemetry: telemetry
|
|
5249
|
+
async function matchesFilter(args) {
|
|
5250
|
+
const {document: document, filter: filter, userId: userId} = args;
|
|
5251
|
+
parsedFilters.has(filter) || parsedFilters.set(filter, groqJs.parse(`*[${filter}]`));
|
|
5252
|
+
const ast = parsedFilters.get(filter), data = await (await groqJs.evaluate(ast, {
|
|
5253
|
+
dataset: [ document ],
|
|
5254
|
+
...userId !== void 0 ? {
|
|
5255
|
+
identity: userId
|
|
5312
5256
|
} : {}
|
|
5313
|
-
})
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
}
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5257
|
+
})).get();
|
|
5258
|
+
return Array.isArray(data) && data.length === 1;
|
|
5259
|
+
}
|
|
5260
|
+
|
|
5261
|
+
async function grantsPermissionOn(args) {
|
|
5262
|
+
const {document: document, grants: grants, permission: permission, userId: userId} = args;
|
|
5263
|
+
if (!document || grants.length === 0) return !1;
|
|
5264
|
+
for (const grant of grants) if (grant.permissions.includes(permission) && await matchesFilter({
|
|
5265
|
+
document: document,
|
|
5266
|
+
filter: grant.filter,
|
|
5267
|
+
...userId !== void 0 ? {
|
|
5268
|
+
userId: userId
|
|
5269
|
+
} : {}
|
|
5270
|
+
})) return !0;
|
|
5271
|
+
return !1;
|
|
5272
|
+
}
|
|
5273
|
+
|
|
5274
|
+
function aclPathForResource(res) {
|
|
5275
|
+
if (res.type === "dataset") {
|
|
5276
|
+
const {projectId: projectId, dataset: dataset} = invariants.datasetResourceParts(res.id);
|
|
5277
|
+
return `/projects/${projectId}/datasets/${dataset}/acl`;
|
|
5278
|
+
}
|
|
5279
|
+
if (res.type === "canvas") return `/canvases/${res.id}/acl`;
|
|
5280
|
+
}
|
|
5281
|
+
|
|
5282
|
+
async function fetchGrants(args) {
|
|
5283
|
+
const {client: client, resourcePath: resourcePath, signal: signal} = args;
|
|
5284
|
+
return client.request({
|
|
5285
|
+
url: resourcePath,
|
|
5286
|
+
tag: REQUEST_TAG.accessGrants,
|
|
5287
|
+
...signal ? {
|
|
5288
|
+
signal: signal
|
|
5342
5289
|
} : {}
|
|
5343
|
-
}).ifRevisionId(instance._rev).commit(SYNC_COMMIT);
|
|
5344
|
-
await deployOrRollback({
|
|
5345
|
-
client: client,
|
|
5346
|
-
instanceId: instance._id,
|
|
5347
|
-
committedRev: committed._rev,
|
|
5348
|
-
restore: {
|
|
5349
|
-
stages: instance.stages,
|
|
5350
|
-
history: instance.history
|
|
5351
|
-
},
|
|
5352
|
-
reversible: !0,
|
|
5353
|
-
deploy: () => deployStageGuards({
|
|
5354
|
-
client: client,
|
|
5355
|
-
clientForGdr: clientForGdr,
|
|
5356
|
-
instance: instance,
|
|
5357
|
-
definition: definition,
|
|
5358
|
-
stageName: stage.name,
|
|
5359
|
-
now: now,
|
|
5360
|
-
snapshot: ctx.snapshot
|
|
5361
|
-
})
|
|
5362
|
-
}), await autoActivatePrimedActivities({
|
|
5363
|
-
client: client,
|
|
5364
|
-
instanceId: instanceId,
|
|
5365
|
-
definition: definition,
|
|
5366
|
-
stage: stage,
|
|
5367
|
-
actor: actor,
|
|
5368
|
-
clientForGdr: clientForGdr,
|
|
5369
|
-
clock: clock,
|
|
5370
|
-
executionContext: executionContext,
|
|
5371
|
-
telemetry: telemetry
|
|
5372
5290
|
});
|
|
5373
5291
|
}
|
|
5374
5292
|
|
|
5375
|
-
|
|
5376
|
-
|
|
5293
|
+
const actorCache = /* @__PURE__ */ new WeakMap, grantsCache = /* @__PURE__ */ new WeakMap;
|
|
5294
|
+
|
|
5295
|
+
async function resolveAccess(taggedClient, args = {}) {
|
|
5296
|
+
const client = unwrapRequestTag(taggedClient), requestFn = lazyRequest(client);
|
|
5297
|
+
if (requestFn === void 0) throw new invariants.ContractViolationError("workflow: no actor available. The engine resolves the actor from the client's token via `client.request({ uri: '/users/me' })`. Supply a real `@sanity/client` configured with a token (the test bench serves these endpoints per registered token).");
|
|
5298
|
+
const grantsPromise = args.grantsFromPath !== void 0 ? cachedGrants({
|
|
5377
5299
|
client: client,
|
|
5378
|
-
|
|
5379
|
-
|
|
5380
|
-
|
|
5300
|
+
requestFn: requestFn,
|
|
5301
|
+
resourcePath: args.grantsFromPath
|
|
5302
|
+
}) : Promise.resolve(void 0), [actor, grants] = await Promise.all([ cachedActor(client, requestFn), grantsPromise ]);
|
|
5303
|
+
if (actor === void 0) throw new invariants.ContractViolationError("workflow: failed to resolve actor from `/users/me`. The client is configured but the endpoint returned no usable identity — check the token.");
|
|
5304
|
+
return {
|
|
5381
5305
|
actor: actor,
|
|
5382
|
-
|
|
5383
|
-
|
|
5384
|
-
|
|
5385
|
-
|
|
5306
|
+
...grants !== void 0 ? {
|
|
5307
|
+
grants: grants
|
|
5308
|
+
} : {}
|
|
5309
|
+
};
|
|
5310
|
+
}
|
|
5311
|
+
|
|
5312
|
+
function cachedActor(client, requestFn) {
|
|
5313
|
+
const cached = actorCache.get(client);
|
|
5314
|
+
if (cached !== void 0) return cached;
|
|
5315
|
+
const pending = fetchActor(requestFn).catch(err => {
|
|
5316
|
+
throw actorCache.get(client) === pending && actorCache.delete(client), err;
|
|
5386
5317
|
});
|
|
5318
|
+
return actorCache.set(client, pending), pending;
|
|
5387
5319
|
}
|
|
5388
5320
|
|
|
5389
|
-
|
|
5390
|
-
const
|
|
5391
|
-
|
|
5392
|
-
const ctx = await buildEngineContext({
|
|
5321
|
+
function grantsForClientPath(taggedClient, resourcePath) {
|
|
5322
|
+
const client = unwrapRequestTag(taggedClient), requestFn = lazyRequest(client);
|
|
5323
|
+
return requestFn === void 0 ? Promise.resolve(void 0) : cachedGrants({
|
|
5393
5324
|
client: client,
|
|
5394
|
-
|
|
5395
|
-
|
|
5396
|
-
|
|
5397
|
-
...clock ? {
|
|
5398
|
-
clock: clock
|
|
5399
|
-
} : {},
|
|
5400
|
-
...actor ? {
|
|
5401
|
-
actor: actor
|
|
5402
|
-
} : {},
|
|
5403
|
-
...executionContext ? {
|
|
5404
|
-
executionContext: executionContext
|
|
5405
|
-
} : {},
|
|
5406
|
-
...telemetry ? {
|
|
5407
|
-
telemetry: telemetry
|
|
5408
|
-
} : {}
|
|
5409
|
-
}), mutation = startMutation(updated), mutEntry = currentActivities(mutation).find(t => t.name === activity.name);
|
|
5410
|
-
mutEntry && mutEntry.status === "pending" && (await activateActivity({
|
|
5411
|
-
ctx: ctx,
|
|
5412
|
-
mutation: mutation,
|
|
5413
|
-
activity: activity,
|
|
5414
|
-
entry: mutEntry,
|
|
5415
|
-
actor: actor
|
|
5416
|
-
}), await persist(ctx, mutation));
|
|
5325
|
+
requestFn: requestFn,
|
|
5326
|
+
resourcePath: resourcePath
|
|
5327
|
+
});
|
|
5417
5328
|
}
|
|
5418
5329
|
|
|
5419
|
-
|
|
5330
|
+
function lazyRequest(client) {
|
|
5331
|
+
return client.request === void 0 ? void 0 : opts => client.request(opts);
|
|
5332
|
+
}
|
|
5420
5333
|
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5334
|
+
function cachedGrants({client: client, requestFn: requestFn, resourcePath: resourcePath}) {
|
|
5335
|
+
let byPath = grantsCache.get(client);
|
|
5336
|
+
byPath === void 0 && (byPath = /* @__PURE__ */ new Map, grantsCache.set(client, byPath));
|
|
5337
|
+
let cached = byPath.get(resourcePath);
|
|
5338
|
+
return cached === void 0 && (cached = fetchGrantsCached(requestFn, resourcePath),
|
|
5339
|
+
byPath.set(resourcePath, cached)), cached;
|
|
5340
|
+
}
|
|
5341
|
+
|
|
5342
|
+
async function fetchActor(requestFn) {
|
|
5343
|
+
let user;
|
|
5344
|
+
try {
|
|
5345
|
+
user = await requestFn({
|
|
5346
|
+
uri: "/users/me",
|
|
5347
|
+
tag: REQUEST_TAG.accessResolveActor
|
|
5428
5348
|
});
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5349
|
+
} catch (err) {
|
|
5350
|
+
throw new Error('workflow: /users/me request failed. The engine resolves the actor from the client\'s token via `client.request({ uri: "/users/me" })`. Check the token/connectivity.', {
|
|
5351
|
+
cause: err
|
|
5432
5352
|
});
|
|
5433
5353
|
}
|
|
5434
|
-
return
|
|
5354
|
+
if (!user || typeof user.id != "string" || user.id.length === 0) return;
|
|
5355
|
+
const roleNames = user.roles?.map(r => r.name).filter(n => !!n) ?? [];
|
|
5356
|
+
return {
|
|
5357
|
+
kind: "person",
|
|
5358
|
+
id: user.id,
|
|
5359
|
+
...roleNames.length > 0 ? {
|
|
5360
|
+
roles: roleNames
|
|
5361
|
+
} : {}
|
|
5362
|
+
};
|
|
5435
5363
|
}
|
|
5436
5364
|
|
|
5437
|
-
async function
|
|
5438
|
-
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
} : {},
|
|
5449
|
-
...executionContext ? {
|
|
5450
|
-
executionContext: executionContext
|
|
5451
|
-
} : {},
|
|
5452
|
-
...telemetry ? {
|
|
5453
|
-
telemetry: telemetry
|
|
5454
|
-
} : {},
|
|
5455
|
-
...overlay ? {
|
|
5456
|
-
overlay: overlay
|
|
5457
|
-
} : {}
|
|
5458
|
-
}
|
|
5459
|
-
}), stage = findStage(ctx.definition, ctx.instance.currentStage), gatedActivities = (stage.activities ?? []).filter(t => effectiveCompleteWhen(t) !== void 0 || t.failWhen !== void 0);
|
|
5460
|
-
if (gatedActivities.length === 0) return 0;
|
|
5461
|
-
const candidates = await gatherAutoResolveCandidates(ctx, gatedActivities);
|
|
5462
|
-
if (candidates.length === 0) return 0;
|
|
5463
|
-
const mutation = startMutation(ctx.instance);
|
|
5464
|
-
let count = 0;
|
|
5465
|
-
for (const {activity: activity, outcome: outcome} of candidates) {
|
|
5466
|
-
const mutEntry = currentActivities(mutation).find(t => t.name === activity.name);
|
|
5467
|
-
mutEntry === void 0 || mutEntry.status !== "active" || (applyGateOutcome({
|
|
5468
|
-
entry: mutEntry,
|
|
5469
|
-
history: mutation.history,
|
|
5470
|
-
stage: stage.name,
|
|
5471
|
-
at: ctx.now,
|
|
5472
|
-
outcome: outcome,
|
|
5473
|
-
actor: actor
|
|
5474
|
-
}), count++);
|
|
5365
|
+
async function fetchGrantsCached(requestFn, resourcePath) {
|
|
5366
|
+
try {
|
|
5367
|
+
return await fetchGrants({
|
|
5368
|
+
client: {
|
|
5369
|
+
request: requestFn
|
|
5370
|
+
},
|
|
5371
|
+
resourcePath: resourcePath
|
|
5372
|
+
});
|
|
5373
|
+
} catch (err) {
|
|
5374
|
+
console.warn(`workflow: failed to fetch grants from "${resourcePath}"; advisory permission reads that depend on them are skipped — a rendered $can stays undefined (conditions referencing it fail closed) and the subject-write forecast omits this resource. The lake still enforces writes. Original error: ${invariants.errorMessage(err)}`);
|
|
5375
|
+
return;
|
|
5475
5376
|
}
|
|
5476
|
-
return count > 0 && await persist(ctx, mutation), count;
|
|
5477
5377
|
}
|
|
5478
5378
|
|
|
5479
|
-
async function
|
|
5480
|
-
|
|
5481
|
-
for (
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
|
|
5488
|
-
|
|
5489
|
-
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
instanceId: instanceId,
|
|
5493
|
-
actor: actor,
|
|
5494
|
-
clientForGdr: clientForGdr,
|
|
5495
|
-
clock: clock,
|
|
5496
|
-
executionContext: executionContext,
|
|
5497
|
-
telemetry: telemetry,
|
|
5498
|
-
overlay: overlay
|
|
5499
|
-
}), !(await evaluateAutoTransitions({
|
|
5500
|
-
client: client,
|
|
5501
|
-
instanceId: instanceId,
|
|
5502
|
-
actor: actor,
|
|
5503
|
-
clientForGdr: clientForGdr,
|
|
5504
|
-
...clock ? {
|
|
5505
|
-
clock: clock
|
|
5506
|
-
} : {},
|
|
5507
|
-
...executionContext ? {
|
|
5508
|
-
executionContext: executionContext
|
|
5509
|
-
} : {},
|
|
5510
|
-
...telemetry ? {
|
|
5511
|
-
telemetry: telemetry
|
|
5512
|
-
} : {},
|
|
5513
|
-
...overlay ? {
|
|
5514
|
-
overlay: overlay
|
|
5515
|
-
} : {}
|
|
5516
|
-
})).fired) return count;
|
|
5517
|
-
if (count++, count >= CASCADE_LIMIT) throw new CascadeLimitError({
|
|
5518
|
-
instanceId: instanceId,
|
|
5519
|
-
limit: CASCADE_LIMIT
|
|
5379
|
+
async function buildFieldInsights({sites: sites, snapshot: snapshot}) {
|
|
5380
|
+
const insights = [];
|
|
5381
|
+
for (const field of fieldsReadAcross(sites)) {
|
|
5382
|
+
const involved = sites.filter(entry => readsField(entry.insight, field));
|
|
5383
|
+
insights.push({
|
|
5384
|
+
field: field,
|
|
5385
|
+
reads: groqConditionDescribe.dedupeReads(involved.flatMap(entry => fieldReads(entry.insight, field))),
|
|
5386
|
+
involvedIn: involved.map(entry => entry.site),
|
|
5387
|
+
proposals: await verifyProposals({
|
|
5388
|
+
field: field,
|
|
5389
|
+
involved: involved,
|
|
5390
|
+
snapshot: snapshot
|
|
5391
|
+
})
|
|
5520
5392
|
});
|
|
5521
5393
|
}
|
|
5394
|
+
return insights;
|
|
5522
5395
|
}
|
|
5523
5396
|
|
|
5524
|
-
|
|
5525
|
-
|
|
5526
|
-
|
|
5527
|
-
const instance = await getInstanceDocument(client, instanceId);
|
|
5528
|
-
if (!instance) return;
|
|
5529
|
-
const condemned = condemnedSubworkflows(instance);
|
|
5530
|
-
if (condemned.length !== 0) {
|
|
5531
|
-
for (const row of condemned) await settleCondemnedRow({
|
|
5532
|
-
client: client,
|
|
5533
|
-
ownerId: instanceId,
|
|
5534
|
-
row: row,
|
|
5535
|
-
actor: actor,
|
|
5536
|
-
clientForGdr: clientForGdr,
|
|
5537
|
-
clock: clock,
|
|
5538
|
-
executionContext: executionContext,
|
|
5539
|
-
telemetry: telemetry,
|
|
5540
|
-
draining: draining
|
|
5541
|
-
});
|
|
5542
|
-
await stampDrainedRows({
|
|
5543
|
-
client: client,
|
|
5544
|
-
instance: instance,
|
|
5545
|
-
condemned: condemned,
|
|
5546
|
-
clock: clock,
|
|
5547
|
-
executionContext: executionContext
|
|
5548
|
-
});
|
|
5549
|
-
}
|
|
5397
|
+
function fieldsReadAcross(sites) {
|
|
5398
|
+
const names = sites.flatMap(entry => entry.insight.analysis.reads).filter(read => read.variable === "fields").map(read => read.path[0]).filter(head => typeof head == "string");
|
|
5399
|
+
return [ ...new Set(names) ];
|
|
5550
5400
|
}
|
|
5551
5401
|
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
|
|
5567
|
-
|
|
5568
|
-
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
executionContext: executionContext,
|
|
5581
|
-
telemetry: telemetry,
|
|
5582
|
-
draining: draining
|
|
5583
|
-
});
|
|
5584
|
-
} catch (cause) {
|
|
5585
|
-
if (cause instanceof invariants.InstanceNotFoundError) return;
|
|
5586
|
-
throw cause instanceof WorkflowStateDivergedError ? cause : new WorkflowStateDivergedError({
|
|
5587
|
-
instanceId: ownerId,
|
|
5588
|
-
guardError: cause,
|
|
5589
|
-
reason: `owed child abort of "${childId}" failed while draining condemned rows`
|
|
5402
|
+
function fieldReads(insight, field) {
|
|
5403
|
+
return insight.analysis.reads.filter(read => read.variable === "fields" && read.path[0] === field);
|
|
5404
|
+
}
|
|
5405
|
+
|
|
5406
|
+
function readsField(insight, field) {
|
|
5407
|
+
return fieldReads(insight, field).length > 0;
|
|
5408
|
+
}
|
|
5409
|
+
|
|
5410
|
+
async function verifyProposals({field: field, involved: involved, snapshot: snapshot}) {
|
|
5411
|
+
const proposals = [];
|
|
5412
|
+
for (const assign of candidateAssignments(involved, field)) {
|
|
5413
|
+
const consequences = [];
|
|
5414
|
+
for (const entry of involved) {
|
|
5415
|
+
const result = await groqConditionDescribe.whatIfCondition({
|
|
5416
|
+
condition: entry.condition,
|
|
5417
|
+
dataset: snapshot.docs,
|
|
5418
|
+
params: entry.params,
|
|
5419
|
+
assign: assign
|
|
5420
|
+
});
|
|
5421
|
+
result.changed && consequences.push({
|
|
5422
|
+
site: entry.site,
|
|
5423
|
+
before: result.before,
|
|
5424
|
+
after: result.after
|
|
5425
|
+
});
|
|
5426
|
+
}
|
|
5427
|
+
consequences.length > 0 && proposals.push({
|
|
5428
|
+
assign: assign,
|
|
5429
|
+
consequences: consequences
|
|
5590
5430
|
});
|
|
5591
5431
|
}
|
|
5432
|
+
return proposals;
|
|
5592
5433
|
}
|
|
5593
5434
|
|
|
5594
|
-
|
|
5595
|
-
const
|
|
5596
|
-
|
|
5597
|
-
}), byId = new Map(children.map(c => [ assertReadableModel(c)._id, c ])), condemnedKeys = new Set(condemned.map(row => row._key)), history = [ ...instance.history ], subworkflows = (instance.subworkflows ?? []).map(row => {
|
|
5598
|
-
if (row.resolved !== void 0 || !condemnedKeys.has(row._key)) return row;
|
|
5599
|
-
const child = byId.get(invariants.toBareId(row.ref.id)), resolved = child !== void 0 ? terminalResolution(child) : {
|
|
5600
|
-
at: now,
|
|
5601
|
-
aborted: !0
|
|
5602
|
-
};
|
|
5603
|
-
return resolved === void 0 ? row : (history.push(...stampHistoryEntries([ subworkflowResolvedEntry({
|
|
5604
|
-
row: row,
|
|
5605
|
-
at: now,
|
|
5606
|
-
status: resolved.aborted === !0 ? "aborted" : "done"
|
|
5607
|
-
}) ], stamp)), {
|
|
5608
|
-
...row,
|
|
5609
|
-
resolved: resolved
|
|
5610
|
-
});
|
|
5611
|
-
});
|
|
5612
|
-
await client.patch(instance._id).set({
|
|
5613
|
-
subworkflows: subworkflows,
|
|
5614
|
-
history: history,
|
|
5615
|
-
lastChangedAt: now
|
|
5616
|
-
}).ifRevisionId(instance._rev).commit(SYNC_COMMIT);
|
|
5435
|
+
function candidateAssignments(involved, field) {
|
|
5436
|
+
const candidates = involved.flatMap(entry => entry.insight.blockedBy).flatMap(atom => atom.requirement !== void 0 ? [ atom.requirement ] : []).filter(requirement => requirement.target.variable === "fields").filter(requirement => requirement.target.path[0] === field).flatMap(assignmentFor).filter(fabricatable);
|
|
5437
|
+
return groqConditionDescribe.dedupeBy(candidates, assignment => JSON.stringify([ assignment.target.path, assignment.value ]));
|
|
5617
5438
|
}
|
|
5618
5439
|
|
|
5619
|
-
function
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5440
|
+
function assignmentFor(requirement) {
|
|
5441
|
+
switch (requirement.kind) {
|
|
5442
|
+
case "equals":
|
|
5443
|
+
return [ {
|
|
5444
|
+
target: requirement.target,
|
|
5445
|
+
value: requirement.value
|
|
5446
|
+
} ];
|
|
5447
|
+
|
|
5448
|
+
case "truthy":
|
|
5449
|
+
return [ {
|
|
5450
|
+
target: requirement.target,
|
|
5451
|
+
value: !0
|
|
5452
|
+
} ];
|
|
5453
|
+
|
|
5454
|
+
case "falsy":
|
|
5455
|
+
return [ {
|
|
5456
|
+
target: requirement.target,
|
|
5457
|
+
value: !1
|
|
5458
|
+
} ];
|
|
5459
|
+
|
|
5460
|
+
case "differs":
|
|
5461
|
+
case "defined":
|
|
5462
|
+
case "undefined":
|
|
5463
|
+
case "compares":
|
|
5464
|
+
return [];
|
|
5465
|
+
}
|
|
5628
5466
|
}
|
|
5629
5467
|
|
|
5630
|
-
function
|
|
5631
|
-
return
|
|
5632
|
-
_key: randomKey(),
|
|
5633
|
-
_type: "subworkflowResolved",
|
|
5634
|
-
at: at,
|
|
5635
|
-
activity: row.activity,
|
|
5636
|
-
instanceRef: row.ref,
|
|
5637
|
-
status: status
|
|
5638
|
-
};
|
|
5468
|
+
function fabricatable(assignment) {
|
|
5469
|
+
return assignment.target.path.every(segment => typeof segment != "number" || segment <= groqConditionDescribe.MAX_COUNTERFACTUAL_INDEX);
|
|
5639
5470
|
}
|
|
5640
5471
|
|
|
5641
|
-
async function
|
|
5642
|
-
const
|
|
5643
|
-
|
|
5644
|
-
|
|
5472
|
+
async function subjectResourceGrants(args) {
|
|
5473
|
+
const {clientForGdr: clientForGdr, instance: instance} = args, clients = /* @__PURE__ */ new Map;
|
|
5474
|
+
for (const {parsed: parsed, resource: resource} of foreignSubjectRefs(instance)) {
|
|
5475
|
+
const key = invariants.resourceGdr(resource);
|
|
5476
|
+
clients.has(key) || clients.set(key, {
|
|
5477
|
+
resource: resource,
|
|
5478
|
+
client: clientForGdr(parsed)
|
|
5479
|
+
});
|
|
5480
|
+
}
|
|
5481
|
+
const resolved = await Promise.all([ ...clients.entries() ].map(async ([key, entry]) => {
|
|
5482
|
+
const path = aclPathForResource(entry.resource);
|
|
5483
|
+
if (path === void 0) return;
|
|
5484
|
+
const grants = await grantsForClientPath(entry.client, path);
|
|
5485
|
+
return grants === void 0 ? void 0 : [ key, grants ];
|
|
5486
|
+
}));
|
|
5487
|
+
return new Map(resolved.filter(entry => entry !== void 0));
|
|
5488
|
+
}
|
|
5489
|
+
|
|
5490
|
+
const UNBOUND_CALLER_VARS = Object.fromEntries(invariants.CALLER_BOUND_VARS.map(name => [ name, void 0 ]));
|
|
5491
|
+
|
|
5492
|
+
async function evaluateInstance(args) {
|
|
5493
|
+
const {client: client, tag: tag, workflowResource: workflowResource, instanceId: instanceId, resourceClients: resourceClients} = args, now = (args.clock ?? wallClock)();
|
|
5494
|
+
invariants.validateTag(tag);
|
|
5495
|
+
const {actor: actor, grants: grants} = await resolveAccess(client, {
|
|
5496
|
+
...args.grantsFromPath !== void 0 ? {
|
|
5497
|
+
grantsFromPath: args.grantsFromPath
|
|
5498
|
+
} : {}
|
|
5499
|
+
}), instance = await reload({
|
|
5500
|
+
client: client,
|
|
5501
|
+
instanceId: instanceId,
|
|
5502
|
+
tag: tag
|
|
5503
|
+
}), definition = parseDefinitionSnapshot(instance), clientForGdr = buildClientForGdr({
|
|
5504
|
+
client: client,
|
|
5505
|
+
workflowResource: workflowResource,
|
|
5506
|
+
resourceClients: resourceClients
|
|
5507
|
+
}), snapshot = await hydrateSnapshot({
|
|
5645
5508
|
client: client,
|
|
5646
5509
|
clientForGdr: clientForGdr,
|
|
5647
|
-
instance:
|
|
5510
|
+
instance: instance
|
|
5511
|
+
}), guards = await verdictGuardsForInstance(client, instance._id), resourceGrants = await subjectResourceGrants({
|
|
5512
|
+
clientForGdr: clientForGdr,
|
|
5513
|
+
instance: instance
|
|
5514
|
+
});
|
|
5515
|
+
return evaluateFromSnapshot({
|
|
5516
|
+
instance: instance,
|
|
5648
5517
|
definition: definition,
|
|
5649
|
-
...clock ? {
|
|
5650
|
-
clock: clock
|
|
5651
|
-
} : {},
|
|
5652
|
-
...actor ? {
|
|
5653
|
-
actor: actor
|
|
5654
|
-
} : {},
|
|
5655
|
-
...executionContext ? {
|
|
5656
|
-
executionContext: executionContext
|
|
5657
|
-
} : {},
|
|
5658
|
-
...telemetry ? {
|
|
5659
|
-
telemetry: telemetry
|
|
5660
|
-
} : {}
|
|
5661
|
-
}), mutation = startMutation(parent), row = mutation.subworkflows.find(r => invariants.toBareId(r.ref.id) === child._id);
|
|
5662
|
-
if (row === void 0) {
|
|
5663
|
-
await recordOrphanedPropagation({
|
|
5664
|
-
ctx: ctx,
|
|
5665
|
-
mutation: mutation,
|
|
5666
|
-
child: child
|
|
5667
|
-
});
|
|
5668
|
-
return;
|
|
5669
|
-
}
|
|
5670
|
-
let changed = stampResolvedChild({
|
|
5671
|
-
mutation: mutation,
|
|
5672
|
-
row: row,
|
|
5673
|
-
child: child,
|
|
5674
|
-
now: ctx.now
|
|
5675
|
-
});
|
|
5676
|
-
const parentTerminal = parent.completedAt !== void 0;
|
|
5677
|
-
!parentTerminal && await flipOpenCohortGate({
|
|
5678
|
-
ctx: ctx,
|
|
5679
|
-
mutation: mutation,
|
|
5680
|
-
definition: definition,
|
|
5681
|
-
row: row
|
|
5682
|
-
}) && (changed = !0), changed && await persist(ctx, mutation), !parentTerminal && (await cascadeAutoTransitions({
|
|
5683
|
-
client: client,
|
|
5684
|
-
instanceId: parent._id,
|
|
5685
|
-
actor: actor,
|
|
5686
|
-
clientForGdr: clientForGdr,
|
|
5687
|
-
clock: clock,
|
|
5688
|
-
executionContext: executionContext,
|
|
5689
|
-
telemetry: telemetry
|
|
5690
|
-
}), await propagateToAncestors({
|
|
5691
|
-
client: client,
|
|
5692
|
-
instanceId: parent._id,
|
|
5693
|
-
actor: actor,
|
|
5694
|
-
clientForGdr: clientForGdr,
|
|
5695
|
-
clock: clock,
|
|
5696
|
-
executionContext: executionContext,
|
|
5697
|
-
telemetry: telemetry
|
|
5698
|
-
}));
|
|
5699
|
-
}
|
|
5700
|
-
|
|
5701
|
-
async function loadPropagationPair(client, instanceId) {
|
|
5702
|
-
const child = await getInstanceDocument(client, instanceId);
|
|
5703
|
-
if (!child) return;
|
|
5704
|
-
const parentGdr = parentRef(child);
|
|
5705
|
-
if (parentGdr === void 0) return;
|
|
5706
|
-
const parent = await getInstanceDocument(client, invariants.toBareId(parentGdr.id));
|
|
5707
|
-
if (!(!parent || parent._type !== WORKFLOW_INSTANCE_TYPE) && typeof parent.definitionSnapshot == "string") return {
|
|
5708
|
-
child: child,
|
|
5709
|
-
parent: parent
|
|
5710
|
-
};
|
|
5711
|
-
}
|
|
5712
|
-
|
|
5713
|
-
function stampResolvedChild({mutation: mutation, row: row, child: child, now: now}) {
|
|
5714
|
-
if (row.resolved !== void 0) return !1;
|
|
5715
|
-
const resolved = terminalResolution(child);
|
|
5716
|
-
return resolved === void 0 ? !1 : (row.resolved = resolved, mutation.history.push(subworkflowResolvedEntry({
|
|
5717
|
-
row: row,
|
|
5718
|
-
at: now,
|
|
5719
|
-
status: resolved.aborted === !0 ? "aborted" : "done"
|
|
5720
|
-
})), !0);
|
|
5721
|
-
}
|
|
5722
|
-
|
|
5723
|
-
async function flipOpenCohortGate({ctx: ctx, mutation: mutation, definition: definition, row: row}) {
|
|
5724
|
-
const openEntry = findOpenStageEntry(mutation);
|
|
5725
|
-
if (openEntry === void 0 || row.stageEntry !== openEntry._key) return !1;
|
|
5726
|
-
const entry = openEntry.activities.find(e => e.name === row.activity);
|
|
5727
|
-
if (entry === void 0 || entry.status !== "active") return !1;
|
|
5728
|
-
const activityDef = (definition.stages.find(s => s.name === openEntry.name)?.activities ?? []).find(a => a.name === row.activity);
|
|
5729
|
-
if (activityDef?.subworkflows === void 0) return !1;
|
|
5730
|
-
const gateCtx = {
|
|
5731
|
-
...ctx,
|
|
5732
|
-
instance: materializeInstance(ctx.instance, mutation)
|
|
5733
|
-
}, outcome = await evaluateResolutionGate({
|
|
5734
|
-
ctx: gateCtx,
|
|
5735
|
-
activity: activityDef
|
|
5736
|
-
});
|
|
5737
|
-
return outcome === void 0 ? !1 : (applyGateOutcome({
|
|
5738
|
-
entry: entry,
|
|
5739
|
-
history: mutation.history,
|
|
5740
|
-
stage: openEntry.name,
|
|
5741
|
-
at: ctx.now,
|
|
5742
|
-
outcome: outcome,
|
|
5743
|
-
...ctx.actor !== void 0 ? {
|
|
5744
|
-
actor: ctx.actor
|
|
5745
|
-
} : {}
|
|
5746
|
-
}), !0);
|
|
5747
|
-
}
|
|
5748
|
-
|
|
5749
|
-
async function recordOrphanedPropagation({ctx: ctx, mutation: mutation, child: child}) {
|
|
5750
|
-
const childUri = invariants.selfGdr(child);
|
|
5751
|
-
mutation.history.some(h => h._type === "subworkflowOrphaned" && h.instanceRef.id === childUri) || (mutation.history.push({
|
|
5752
|
-
_key: randomKey(),
|
|
5753
|
-
_type: "subworkflowOrphaned",
|
|
5754
|
-
at: ctx.now,
|
|
5755
|
-
instanceRef: {
|
|
5756
|
-
id: childUri,
|
|
5757
|
-
type: WORKFLOW_INSTANCE_TYPE
|
|
5758
|
-
},
|
|
5759
|
-
detail: `Instance "${child._id}" names this instance as its parent but no subworkflow-registry row matches it, so its state cannot drive any gate here.`
|
|
5760
|
-
}), await persist(ctx, mutation));
|
|
5761
|
-
}
|
|
5762
|
-
|
|
5763
|
-
const parsedFilters = /* @__PURE__ */ new Map;
|
|
5764
|
-
|
|
5765
|
-
async function matchesFilter(args) {
|
|
5766
|
-
const {document: document, filter: filter, userId: userId} = args;
|
|
5767
|
-
parsedFilters.has(filter) || parsedFilters.set(filter, groqJs.parse(`*[${filter}]`));
|
|
5768
|
-
const ast = parsedFilters.get(filter), data = await (await groqJs.evaluate(ast, {
|
|
5769
|
-
dataset: [ document ],
|
|
5770
|
-
...userId !== void 0 ? {
|
|
5771
|
-
identity: userId
|
|
5772
|
-
} : {}
|
|
5773
|
-
})).get();
|
|
5774
|
-
return Array.isArray(data) && data.length === 1;
|
|
5775
|
-
}
|
|
5776
|
-
|
|
5777
|
-
async function grantsPermissionOn(args) {
|
|
5778
|
-
const {document: document, grants: grants, permission: permission, userId: userId} = args;
|
|
5779
|
-
if (!document || grants.length === 0) return !1;
|
|
5780
|
-
for (const grant of grants) if (grant.permissions.includes(permission) && await matchesFilter({
|
|
5781
|
-
document: document,
|
|
5782
|
-
filter: grant.filter,
|
|
5783
|
-
...userId !== void 0 ? {
|
|
5784
|
-
userId: userId
|
|
5785
|
-
} : {}
|
|
5786
|
-
})) return !0;
|
|
5787
|
-
return !1;
|
|
5788
|
-
}
|
|
5789
|
-
|
|
5790
|
-
function aclPathForResource(res) {
|
|
5791
|
-
if (res.type === "dataset") {
|
|
5792
|
-
const {projectId: projectId, dataset: dataset} = invariants.datasetResourceParts(res.id);
|
|
5793
|
-
return `/projects/${projectId}/datasets/${dataset}/acl`;
|
|
5794
|
-
}
|
|
5795
|
-
if (res.type === "canvas") return `/canvases/${res.id}/acl`;
|
|
5796
|
-
}
|
|
5797
|
-
|
|
5798
|
-
async function fetchGrants(args) {
|
|
5799
|
-
const {client: client, resourcePath: resourcePath, signal: signal} = args;
|
|
5800
|
-
return client.request({
|
|
5801
|
-
url: resourcePath,
|
|
5802
|
-
tag: REQUEST_TAG.accessGrants,
|
|
5803
|
-
...signal ? {
|
|
5804
|
-
signal: signal
|
|
5805
|
-
} : {}
|
|
5806
|
-
});
|
|
5807
|
-
}
|
|
5808
|
-
|
|
5809
|
-
const actorCache = /* @__PURE__ */ new WeakMap, grantsCache = /* @__PURE__ */ new WeakMap;
|
|
5810
|
-
|
|
5811
|
-
async function resolveAccess(taggedClient, args = {}) {
|
|
5812
|
-
const client = unwrapRequestTag(taggedClient), requestFn = lazyRequest(client);
|
|
5813
|
-
if (requestFn === void 0) throw new invariants.ContractViolationError("workflow: no actor available. The engine resolves the actor from the client's token via `client.request({ uri: '/users/me' })`. Supply a real `@sanity/client` configured with a token (the test bench serves these endpoints per registered token).");
|
|
5814
|
-
const grantsPromise = args.grantsFromPath !== void 0 ? cachedGrants({
|
|
5815
|
-
client: client,
|
|
5816
|
-
requestFn: requestFn,
|
|
5817
|
-
resourcePath: args.grantsFromPath
|
|
5818
|
-
}) : Promise.resolve(void 0), [actor, grants] = await Promise.all([ cachedActor(client, requestFn), grantsPromise ]);
|
|
5819
|
-
if (actor === void 0) throw new invariants.ContractViolationError("workflow: failed to resolve actor from `/users/me`. The client is configured but the endpoint returned no usable identity — check the token.");
|
|
5820
|
-
return {
|
|
5821
5518
|
actor: actor,
|
|
5519
|
+
snapshot: snapshot,
|
|
5520
|
+
guards: guards,
|
|
5521
|
+
now: now,
|
|
5522
|
+
resourceGrants: resourceGrants,
|
|
5822
5523
|
...grants !== void 0 ? {
|
|
5823
5524
|
grants: grants
|
|
5824
5525
|
} : {}
|
|
5825
|
-
};
|
|
5826
|
-
}
|
|
5827
|
-
|
|
5828
|
-
function cachedActor(client, requestFn) {
|
|
5829
|
-
const cached = actorCache.get(client);
|
|
5830
|
-
if (cached !== void 0) return cached;
|
|
5831
|
-
const pending = fetchActor(requestFn).catch(err => {
|
|
5832
|
-
throw actorCache.get(client) === pending && actorCache.delete(client), err;
|
|
5833
|
-
});
|
|
5834
|
-
return actorCache.set(client, pending), pending;
|
|
5835
|
-
}
|
|
5836
|
-
|
|
5837
|
-
function grantsForClientPath(taggedClient, resourcePath) {
|
|
5838
|
-
const client = unwrapRequestTag(taggedClient), requestFn = lazyRequest(client);
|
|
5839
|
-
return requestFn === void 0 ? Promise.resolve(void 0) : cachedGrants({
|
|
5840
|
-
client: client,
|
|
5841
|
-
requestFn: requestFn,
|
|
5842
|
-
resourcePath: resourcePath
|
|
5843
5526
|
});
|
|
5844
5527
|
}
|
|
5845
5528
|
|
|
5846
|
-
function
|
|
5847
|
-
return client.request === void 0 ? void 0 : opts => client.request(opts);
|
|
5848
|
-
}
|
|
5849
|
-
|
|
5850
|
-
function cachedGrants({client: client, requestFn: requestFn, resourcePath: resourcePath}) {
|
|
5851
|
-
let byPath = grantsCache.get(client);
|
|
5852
|
-
byPath === void 0 && (byPath = /* @__PURE__ */ new Map, grantsCache.set(client, byPath));
|
|
5853
|
-
let cached = byPath.get(resourcePath);
|
|
5854
|
-
return cached === void 0 && (cached = fetchGrantsCached(requestFn, resourcePath),
|
|
5855
|
-
byPath.set(resourcePath, cached)), cached;
|
|
5856
|
-
}
|
|
5857
|
-
|
|
5858
|
-
async function fetchActor(requestFn) {
|
|
5859
|
-
let user;
|
|
5529
|
+
function currentStageOf(instance, definition) {
|
|
5860
5530
|
try {
|
|
5861
|
-
|
|
5862
|
-
uri: "/users/me",
|
|
5863
|
-
tag: REQUEST_TAG.accessResolveActor
|
|
5864
|
-
});
|
|
5531
|
+
return findStage(definition, instance.currentStage);
|
|
5865
5532
|
} catch (err) {
|
|
5866
|
-
throw new Error(
|
|
5533
|
+
throw new Error(`Instance "${instance._id}" currentStage "${instance.currentStage}" not in definition`, {
|
|
5867
5534
|
cause: err
|
|
5868
5535
|
});
|
|
5869
5536
|
}
|
|
5870
|
-
if (!user || typeof user.id != "string" || user.id.length === 0) return;
|
|
5871
|
-
const roleNames = user.roles?.map(r => r.name).filter(n => !!n) ?? [];
|
|
5872
|
-
return {
|
|
5873
|
-
kind: "person",
|
|
5874
|
-
id: user.id,
|
|
5875
|
-
...roleNames.length > 0 ? {
|
|
5876
|
-
roles: roleNames
|
|
5877
|
-
} : {}
|
|
5878
|
-
};
|
|
5879
|
-
}
|
|
5880
|
-
|
|
5881
|
-
async function fetchGrantsCached(requestFn, resourcePath) {
|
|
5882
|
-
try {
|
|
5883
|
-
return await fetchGrants({
|
|
5884
|
-
client: {
|
|
5885
|
-
request: requestFn
|
|
5886
|
-
},
|
|
5887
|
-
resourcePath: resourcePath
|
|
5888
|
-
});
|
|
5889
|
-
} catch (err) {
|
|
5890
|
-
console.warn(`workflow: failed to fetch grants from "${resourcePath}"; advisory permission reads that depend on them are skipped — a rendered $can stays undefined (conditions referencing it fail closed) and the subject-write forecast omits this resource. The lake still enforces writes. Original error: ${invariants.errorMessage(err)}`);
|
|
5891
|
-
return;
|
|
5892
|
-
}
|
|
5893
|
-
}
|
|
5894
|
-
|
|
5895
|
-
async function buildFieldInsights({sites: sites, snapshot: snapshot}) {
|
|
5896
|
-
const insights = [];
|
|
5897
|
-
for (const field of fieldsReadAcross(sites)) {
|
|
5898
|
-
const involved = sites.filter(entry => readsField(entry.insight, field));
|
|
5899
|
-
insights.push({
|
|
5900
|
-
field: field,
|
|
5901
|
-
reads: groqConditionDescribe.dedupeReads(involved.flatMap(entry => fieldReads(entry.insight, field))),
|
|
5902
|
-
involvedIn: involved.map(entry => entry.site),
|
|
5903
|
-
proposals: await verifyProposals({
|
|
5904
|
-
field: field,
|
|
5905
|
-
involved: involved,
|
|
5906
|
-
snapshot: snapshot
|
|
5907
|
-
})
|
|
5908
|
-
});
|
|
5909
|
-
}
|
|
5910
|
-
return insights;
|
|
5911
|
-
}
|
|
5912
|
-
|
|
5913
|
-
function fieldsReadAcross(sites) {
|
|
5914
|
-
const names = sites.flatMap(entry => entry.insight.analysis.reads).filter(read => read.variable === "fields").map(read => read.path[0]).filter(head => typeof head == "string");
|
|
5915
|
-
return [ ...new Set(names) ];
|
|
5916
|
-
}
|
|
5917
|
-
|
|
5918
|
-
function fieldReads(insight, field) {
|
|
5919
|
-
return insight.analysis.reads.filter(read => read.variable === "fields" && read.path[0] === field);
|
|
5920
5537
|
}
|
|
5921
5538
|
|
|
5922
|
-
function
|
|
5923
|
-
|
|
5924
|
-
|
|
5925
|
-
|
|
5926
|
-
|
|
5927
|
-
|
|
5928
|
-
|
|
5929
|
-
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
|
|
5934
|
-
params: entry.params,
|
|
5935
|
-
assign: assign
|
|
5936
|
-
});
|
|
5937
|
-
result.changed && consequences.push({
|
|
5938
|
-
site: entry.site,
|
|
5939
|
-
before: result.before,
|
|
5940
|
-
after: result.after
|
|
5941
|
-
});
|
|
5942
|
-
}
|
|
5943
|
-
consequences.length > 0 && proposals.push({
|
|
5944
|
-
assign: assign,
|
|
5945
|
-
consequences: consequences
|
|
5946
|
-
});
|
|
5947
|
-
}
|
|
5948
|
-
return proposals;
|
|
5949
|
-
}
|
|
5950
|
-
|
|
5951
|
-
function candidateAssignments(involved, field) {
|
|
5952
|
-
const candidates = involved.flatMap(entry => entry.insight.blockedBy).flatMap(atom => atom.requirement !== void 0 ? [ atom.requirement ] : []).filter(requirement => requirement.target.variable === "fields").filter(requirement => requirement.target.path[0] === field).flatMap(assignmentFor).filter(fabricatable);
|
|
5953
|
-
return groqConditionDescribe.dedupeBy(candidates, assignment => JSON.stringify([ assignment.target.path, assignment.value ]));
|
|
5954
|
-
}
|
|
5955
|
-
|
|
5956
|
-
function assignmentFor(requirement) {
|
|
5957
|
-
switch (requirement.kind) {
|
|
5958
|
-
case "equals":
|
|
5959
|
-
return [ {
|
|
5960
|
-
target: requirement.target,
|
|
5961
|
-
value: requirement.value
|
|
5962
|
-
} ];
|
|
5963
|
-
|
|
5964
|
-
case "truthy":
|
|
5965
|
-
return [ {
|
|
5966
|
-
target: requirement.target,
|
|
5967
|
-
value: !0
|
|
5968
|
-
} ];
|
|
5969
|
-
|
|
5970
|
-
case "falsy":
|
|
5971
|
-
return [ {
|
|
5972
|
-
target: requirement.target,
|
|
5973
|
-
value: !1
|
|
5974
|
-
} ];
|
|
5975
|
-
|
|
5976
|
-
case "differs":
|
|
5977
|
-
case "defined":
|
|
5978
|
-
case "undefined":
|
|
5979
|
-
case "compares":
|
|
5980
|
-
return [];
|
|
5981
|
-
}
|
|
5982
|
-
}
|
|
5983
|
-
|
|
5984
|
-
function fabricatable(assignment) {
|
|
5985
|
-
return assignment.target.path.every(segment => typeof segment != "number" || segment <= groqConditionDescribe.MAX_COUNTERFACTUAL_INDEX);
|
|
5986
|
-
}
|
|
5987
|
-
|
|
5988
|
-
async function subjectResourceGrants(args) {
|
|
5989
|
-
const {clientForGdr: clientForGdr, instance: instance} = args, clients = /* @__PURE__ */ new Map;
|
|
5990
|
-
for (const {parsed: parsed, resource: resource} of foreignSubjectRefs(instance)) {
|
|
5991
|
-
const key = invariants.resourceGdr(resource);
|
|
5992
|
-
clients.has(key) || clients.set(key, {
|
|
5993
|
-
resource: resource,
|
|
5994
|
-
client: clientForGdr(parsed)
|
|
5995
|
-
});
|
|
5996
|
-
}
|
|
5997
|
-
const resolved = await Promise.all([ ...clients.entries() ].map(async ([key, entry]) => {
|
|
5998
|
-
const path = aclPathForResource(entry.resource);
|
|
5999
|
-
if (path === void 0) return;
|
|
6000
|
-
const grants = await grantsForClientPath(entry.client, path);
|
|
6001
|
-
return grants === void 0 ? void 0 : [ key, grants ];
|
|
6002
|
-
}));
|
|
6003
|
-
return new Map(resolved.filter(entry => entry !== void 0));
|
|
6004
|
-
}
|
|
6005
|
-
|
|
6006
|
-
const UNBOUND_CALLER_VARS = Object.fromEntries(invariants.CALLER_BOUND_VARS.map(name => [ name, void 0 ]));
|
|
6007
|
-
|
|
6008
|
-
async function evaluateInstance(args) {
|
|
6009
|
-
const {client: client, tag: tag, workflowResource: workflowResource, instanceId: instanceId, resourceClients: resourceClients} = args, now = (args.clock ?? wallClock)();
|
|
6010
|
-
invariants.validateTag(tag);
|
|
6011
|
-
const {actor: actor, grants: grants} = await resolveAccess(client, {
|
|
6012
|
-
...args.grantsFromPath !== void 0 ? {
|
|
6013
|
-
grantsFromPath: args.grantsFromPath
|
|
6014
|
-
} : {}
|
|
6015
|
-
}), instance = await reload({
|
|
6016
|
-
client: client,
|
|
6017
|
-
instanceId: instanceId,
|
|
6018
|
-
tag: tag
|
|
6019
|
-
}), definition = parseDefinitionSnapshot(instance), clientForGdr = buildClientForGdr({
|
|
6020
|
-
client: client,
|
|
6021
|
-
workflowResource: workflowResource,
|
|
6022
|
-
resourceClients: resourceClients
|
|
6023
|
-
}), snapshot = await hydrateSnapshot({
|
|
6024
|
-
client: client,
|
|
6025
|
-
clientForGdr: clientForGdr,
|
|
6026
|
-
instance: instance
|
|
6027
|
-
}), guards = await verdictGuardsForInstance(client, instance._id), resourceGrants = await subjectResourceGrants({
|
|
6028
|
-
clientForGdr: clientForGdr,
|
|
6029
|
-
instance: instance
|
|
6030
|
-
});
|
|
6031
|
-
return evaluateFromSnapshot({
|
|
6032
|
-
instance: instance,
|
|
6033
|
-
definition: definition,
|
|
6034
|
-
actor: actor,
|
|
6035
|
-
snapshot: snapshot,
|
|
6036
|
-
guards: guards,
|
|
6037
|
-
now: now,
|
|
6038
|
-
resourceGrants: resourceGrants,
|
|
6039
|
-
...grants !== void 0 ? {
|
|
6040
|
-
grants: grants
|
|
6041
|
-
} : {}
|
|
6042
|
-
});
|
|
6043
|
-
}
|
|
6044
|
-
|
|
6045
|
-
function currentStageOf(instance, definition) {
|
|
6046
|
-
try {
|
|
6047
|
-
return findStage(definition, instance.currentStage);
|
|
6048
|
-
} catch (err) {
|
|
6049
|
-
throw new Error(`Instance "${instance._id}" currentStage "${instance.currentStage}" not in definition`, {
|
|
6050
|
-
cause: err
|
|
6051
|
-
});
|
|
6052
|
-
}
|
|
6053
|
-
}
|
|
6054
|
-
|
|
6055
|
-
async function explainSite(args) {
|
|
6056
|
-
const {site: site, condition: condition, params: params, snapshot: snapshot, sites: sites} = args, insight = await groqConditionDescribe.explainCondition({
|
|
6057
|
-
condition: condition,
|
|
6058
|
-
dataset: snapshot.docs,
|
|
6059
|
-
params: params
|
|
6060
|
-
});
|
|
6061
|
-
return sites.push({
|
|
6062
|
-
site: site,
|
|
6063
|
-
condition: condition,
|
|
6064
|
-
params: params,
|
|
6065
|
-
insight: insight
|
|
6066
|
-
}), insight;
|
|
5539
|
+
async function explainSite(args) {
|
|
5540
|
+
const {site: site, condition: condition, params: params, snapshot: snapshot, sites: sites} = args, insight = await groqConditionDescribe.explainCondition({
|
|
5541
|
+
condition: condition,
|
|
5542
|
+
dataset: snapshot.docs,
|
|
5543
|
+
params: params
|
|
5544
|
+
});
|
|
5545
|
+
return sites.push({
|
|
5546
|
+
site: site,
|
|
5547
|
+
condition: condition,
|
|
5548
|
+
params: params,
|
|
5549
|
+
insight: insight
|
|
5550
|
+
}), insight;
|
|
6067
5551
|
}
|
|
6068
5552
|
|
|
6069
5553
|
async function evaluateFromSnapshot(args) {
|
|
@@ -6111,14 +5595,14 @@ async function evaluateFromSnapshot(args) {
|
|
|
6111
5595
|
kind: "transition",
|
|
6112
5596
|
transition: transition.name
|
|
6113
5597
|
},
|
|
6114
|
-
condition: transition.
|
|
5598
|
+
condition: transition.when,
|
|
6115
5599
|
params: cascadeParams,
|
|
6116
5600
|
snapshot: snapshot,
|
|
6117
5601
|
sites: sites
|
|
6118
5602
|
});
|
|
6119
5603
|
transitionEvaluations.push({
|
|
6120
5604
|
transition: transition,
|
|
6121
|
-
|
|
5605
|
+
whenSatisfied: insight.outcome === "satisfied",
|
|
6122
5606
|
unevaluable: insight.outcome === "unevaluable",
|
|
6123
5607
|
insight: insight
|
|
6124
5608
|
});
|
|
@@ -6322,7 +5806,7 @@ function activityScopeFor(args) {
|
|
|
6322
5806
|
}
|
|
6323
5807
|
|
|
6324
5808
|
async function evaluateActivity(args) {
|
|
6325
|
-
const {activity: activity, statusEntry: statusEntry, instance: instance, actor: actor, snapshot: snapshot, scope: scope, roleAliases: roleAliases, stageHasExits: stageHasExits, guardDenial: guardDenial, subjectPermissionReason: subjectPermissionReason, sites: sites} = args, status = statusEntry?.status ?? "
|
|
5809
|
+
const {activity: activity, statusEntry: statusEntry, instance: instance, actor: actor, snapshot: snapshot, scope: scope, roleAliases: roleAliases, stageHasExits: stageHasExits, guardDenial: guardDenial, subjectPermissionReason: subjectPermissionReason, sites: sites} = args, status = statusEntry?.status ?? "skipped", activityScope = activityScopeFor({
|
|
6326
5810
|
scope: scope,
|
|
6327
5811
|
instance: instance,
|
|
6328
5812
|
snapshot: snapshot,
|
|
@@ -6354,8 +5838,13 @@ async function evaluateActivity(args) {
|
|
|
6354
5838
|
return {
|
|
6355
5839
|
activity: activity,
|
|
6356
5840
|
status: status,
|
|
6357
|
-
kind:
|
|
6358
|
-
|
|
5841
|
+
kind: invariants.deriveActivityKind(activity),
|
|
5842
|
+
classification: invariants.deriveExecutorClassification(activity),
|
|
5843
|
+
pendingOnActor: status === "active" && assigned,
|
|
5844
|
+
scopedOut: isFilterScopedOut({
|
|
5845
|
+
status: status,
|
|
5846
|
+
startedAt: statusEntry?.startedAt
|
|
5847
|
+
}),
|
|
6359
5848
|
...unmetRequirements.length > 0 ? {
|
|
6360
5849
|
unmetRequirements: unmetRequirements
|
|
6361
5850
|
} : {},
|
|
@@ -6380,13 +5869,7 @@ async function explainActivityConditions({activity: activity, activityScope: act
|
|
|
6380
5869
|
const requirementInsights = Object.fromEntries(requirementEntries), unmetRequirements = requirementEntries.filter(([, insight]) => insight.outcome !== "satisfied").map(([name]) => name), filterInsight = activity.filter !== void 0 ? await explainAt({
|
|
6381
5870
|
kind: "activity-filter",
|
|
6382
5871
|
activity: activity.name
|
|
6383
|
-
}, activity.filter) : void 0
|
|
6384
|
-
kind: "complete-when",
|
|
6385
|
-
activity: activity.name
|
|
6386
|
-
}, activity.completeWhen) : void 0, failWhenInsight = activity.failWhen !== void 0 ? await explainAt({
|
|
6387
|
-
kind: "fail-when",
|
|
6388
|
-
activity: activity.name
|
|
6389
|
-
}, activity.failWhen) : void 0;
|
|
5872
|
+
}, activity.filter) : void 0;
|
|
6390
5873
|
return {
|
|
6391
5874
|
unmetRequirements: unmetRequirements,
|
|
6392
5875
|
...activity.requirements !== void 0 ? {
|
|
@@ -6394,116 +5877,915 @@ async function explainActivityConditions({activity: activity, activityScope: act
|
|
|
6394
5877
|
} : {},
|
|
6395
5878
|
...filterInsight !== void 0 ? {
|
|
6396
5879
|
filterInsight: filterInsight
|
|
6397
|
-
} : {},
|
|
6398
|
-
...completeWhenInsight !== void 0 ? {
|
|
6399
|
-
completeWhenInsight: completeWhenInsight
|
|
6400
|
-
} : {},
|
|
6401
|
-
...failWhenInsight !== void 0 ? {
|
|
6402
|
-
failWhenInsight: failWhenInsight
|
|
6403
5880
|
} : {}
|
|
6404
5881
|
};
|
|
6405
5882
|
}
|
|
6406
5883
|
|
|
6407
5884
|
async function evaluateAction(args) {
|
|
6408
|
-
const {action: action, activityName: activityName,
|
|
5885
|
+
const {action: action, activityName: activityName, snapshot: snapshot, activityScope: activityScope, sites: sites} = args, conditionScope = invariants.isCascadeFired(action) ? {
|
|
5886
|
+
...activityScope,
|
|
5887
|
+
...UNBOUND_CALLER_VARS,
|
|
5888
|
+
assigned: !1
|
|
5889
|
+
} : activityScope, insights = await explainActionGates({
|
|
5890
|
+
action: action,
|
|
5891
|
+
activityName: activityName,
|
|
5892
|
+
conditionScope: conditionScope,
|
|
5893
|
+
snapshot: snapshot,
|
|
5894
|
+
sites: sites
|
|
5895
|
+
});
|
|
5896
|
+
return action.when !== void 0 ? triggeredActionVerdict({
|
|
5897
|
+
action: action,
|
|
5898
|
+
when: action.when,
|
|
5899
|
+
insights: insights
|
|
5900
|
+
}) : fireableActionVerdict({
|
|
5901
|
+
args: args,
|
|
5902
|
+
insights: insights
|
|
5903
|
+
});
|
|
5904
|
+
}
|
|
5905
|
+
|
|
5906
|
+
async function explainActionGates({action: action, activityName: activityName, conditionScope: conditionScope, snapshot: snapshot, sites: sites}) {
|
|
5907
|
+
const insight = action.filter !== void 0 ? await explainSite({
|
|
6409
5908
|
site: {
|
|
6410
5909
|
kind: "action",
|
|
6411
5910
|
activity: activityName,
|
|
6412
5911
|
action: action.name
|
|
6413
5912
|
},
|
|
6414
5913
|
condition: action.filter,
|
|
6415
|
-
params:
|
|
5914
|
+
params: conditionScope,
|
|
5915
|
+
snapshot: snapshot,
|
|
5916
|
+
sites: sites
|
|
5917
|
+
}) : void 0, whenInsight = action.when !== void 0 ? await explainSite({
|
|
5918
|
+
site: {
|
|
5919
|
+
kind: "action-when",
|
|
5920
|
+
activity: activityName,
|
|
5921
|
+
action: action.name
|
|
5922
|
+
},
|
|
5923
|
+
condition: action.when,
|
|
5924
|
+
params: conditionScope,
|
|
6416
5925
|
snapshot: snapshot,
|
|
6417
5926
|
sites: sites
|
|
6418
|
-
}) : void 0
|
|
5927
|
+
}) : void 0;
|
|
5928
|
+
return {
|
|
5929
|
+
...insight !== void 0 ? {
|
|
5930
|
+
insight: insight
|
|
5931
|
+
} : {},
|
|
5932
|
+
...whenInsight !== void 0 ? {
|
|
5933
|
+
whenInsight: whenInsight
|
|
5934
|
+
} : {}
|
|
5935
|
+
};
|
|
5936
|
+
}
|
|
5937
|
+
|
|
5938
|
+
function triggeredActionVerdict({action: action, when: when, insights: insights}) {
|
|
5939
|
+
return insights.insight !== void 0 && insights.insight.outcome === "unsatisfied" ? disabled({
|
|
5940
|
+
action: action,
|
|
5941
|
+
reason: {
|
|
5942
|
+
kind: "filter-failed",
|
|
5943
|
+
filter: action.filter ?? ""
|
|
5944
|
+
},
|
|
5945
|
+
...insights
|
|
5946
|
+
}) : {
|
|
5947
|
+
action: action,
|
|
5948
|
+
allowed: !1,
|
|
5949
|
+
triggered: !0,
|
|
5950
|
+
disabledReason: {
|
|
5951
|
+
kind: "cascade-fired",
|
|
5952
|
+
when: when
|
|
5953
|
+
},
|
|
5954
|
+
...insights
|
|
5955
|
+
};
|
|
5956
|
+
}
|
|
5957
|
+
|
|
5958
|
+
function fireableActionVerdict({args: args, insights: insights}) {
|
|
5959
|
+
const {action: action, status: status, instance: instance, stageHasExits: stageHasExits, guardDenial: guardDenial} = args, lifecycle = lifecycleReason({
|
|
6419
5960
|
instance: instance,
|
|
6420
5961
|
status: status,
|
|
6421
5962
|
stageHasExits: stageHasExits
|
|
6422
5963
|
});
|
|
6423
|
-
|
|
5964
|
+
if (lifecycle !== void 0) return disabled({
|
|
6424
5965
|
action: action,
|
|
6425
5966
|
reason: lifecycle,
|
|
6426
|
-
|
|
6427
|
-
})
|
|
5967
|
+
...insights
|
|
5968
|
+
});
|
|
5969
|
+
if (guardDenial !== void 0) return disabled({
|
|
6428
5970
|
action: action,
|
|
6429
5971
|
reason: guardDenial,
|
|
6430
|
-
|
|
6431
|
-
})
|
|
5972
|
+
...insights
|
|
5973
|
+
});
|
|
5974
|
+
if (args.subjectPermissionReason !== void 0 && (action.effects?.length ?? 0) > 0) return disabled({
|
|
6432
5975
|
action: action,
|
|
6433
|
-
reason: subjectPermissionReason,
|
|
6434
|
-
|
|
6435
|
-
})
|
|
5976
|
+
reason: args.subjectPermissionReason,
|
|
5977
|
+
...insights
|
|
5978
|
+
});
|
|
5979
|
+
if (args.requirementsReason !== void 0) return disabled({
|
|
6436
5980
|
action: action,
|
|
6437
|
-
reason: requirementsReason,
|
|
6438
|
-
|
|
6439
|
-
})
|
|
5981
|
+
reason: args.requirementsReason,
|
|
5982
|
+
...insights
|
|
5983
|
+
});
|
|
5984
|
+
const {insight: insight} = insights;
|
|
5985
|
+
return action.filter !== void 0 && insight !== void 0 && insight.outcome !== "satisfied" ? disabled({
|
|
6440
5986
|
action: action,
|
|
6441
5987
|
reason: {
|
|
6442
5988
|
kind: "filter-failed",
|
|
6443
5989
|
filter: action.filter
|
|
6444
5990
|
},
|
|
6445
|
-
|
|
5991
|
+
...insights
|
|
6446
5992
|
}) : {
|
|
6447
5993
|
action: action,
|
|
6448
5994
|
allowed: !0,
|
|
6449
|
-
...
|
|
6450
|
-
|
|
5995
|
+
...insights
|
|
5996
|
+
};
|
|
5997
|
+
}
|
|
5998
|
+
|
|
5999
|
+
async function instanceGuardReason({instance: instance, actor: actor, guards: guards}) {
|
|
6000
|
+
if (guards === void 0 || guards.length === 0) return;
|
|
6001
|
+
const denied = await instanceWriteDenials({
|
|
6002
|
+
instance: instance,
|
|
6003
|
+
guards: guards,
|
|
6004
|
+
identity: actor.id
|
|
6005
|
+
});
|
|
6006
|
+
if (denied.length !== 0) return {
|
|
6007
|
+
kind: "mutation-guard-denied",
|
|
6008
|
+
denied: deniedGuardRefs(denied)
|
|
6009
|
+
};
|
|
6010
|
+
}
|
|
6011
|
+
|
|
6012
|
+
function lifecycleReason({instance: instance, status: status, stageHasExits: stageHasExits}) {
|
|
6013
|
+
const terminal = terminalState(instance);
|
|
6014
|
+
if (terminal === "aborted" && instance.abortedAt !== void 0) return {
|
|
6015
|
+
kind: "instance-aborted",
|
|
6016
|
+
abortedAt: instance.abortedAt
|
|
6017
|
+
};
|
|
6018
|
+
if (terminal === "completed" && instance.completedAt !== void 0) return {
|
|
6019
|
+
kind: "instance-completed",
|
|
6020
|
+
completedAt: instance.completedAt
|
|
6021
|
+
};
|
|
6022
|
+
if (!stageHasExits) return {
|
|
6023
|
+
kind: "stage-terminal",
|
|
6024
|
+
stage: instance.currentStage
|
|
6025
|
+
};
|
|
6026
|
+
if (invariants.isTerminalActivityStatus(status)) return {
|
|
6027
|
+
kind: "activity-not-active",
|
|
6028
|
+
status: status
|
|
6029
|
+
};
|
|
6030
|
+
}
|
|
6031
|
+
|
|
6032
|
+
function disabled(args) {
|
|
6033
|
+
return {
|
|
6034
|
+
action: args.action,
|
|
6035
|
+
allowed: !1,
|
|
6036
|
+
disabledReason: args.reason,
|
|
6037
|
+
...args.insight !== void 0 ? {
|
|
6038
|
+
insight: args.insight
|
|
6039
|
+
} : {},
|
|
6040
|
+
...args.whenInsight !== void 0 ? {
|
|
6041
|
+
whenInsight: args.whenInsight
|
|
6042
|
+
} : {}
|
|
6043
|
+
};
|
|
6044
|
+
}
|
|
6045
|
+
|
|
6046
|
+
function subjectDenialLabels(denied) {
|
|
6047
|
+
return denied.map(d => `${d.permission} on ${d.subject} (${d.resource})`);
|
|
6048
|
+
}
|
|
6049
|
+
|
|
6050
|
+
class ActionDisabledError extends invariants.WorkflowError {
|
|
6051
|
+
reason;
|
|
6052
|
+
activity;
|
|
6053
|
+
action;
|
|
6054
|
+
constructor(args) {
|
|
6055
|
+
super("action-disabled", formatDisabledReason({
|
|
6056
|
+
activity: args.activity,
|
|
6057
|
+
action: args.action,
|
|
6058
|
+
reason: args.reason
|
|
6059
|
+
})), this.name = "ActionDisabledError", this.reason = args.reason, this.activity = args.activity,
|
|
6060
|
+
this.action = args.action;
|
|
6061
|
+
}
|
|
6062
|
+
}
|
|
6063
|
+
|
|
6064
|
+
function actionRendering(action) {
|
|
6065
|
+
const kind = action.disabledReason?.kind;
|
|
6066
|
+
return kind === "filter-failed" ? "absent" : action.triggered === !0 || kind === "cascade-fired" ? "automation" : "button";
|
|
6067
|
+
}
|
|
6068
|
+
|
|
6069
|
+
const disabledReasonDetail = {
|
|
6070
|
+
"filter-failed": r => `action filter returned false${r.detail ? ` (${r.detail})` : ""}`,
|
|
6071
|
+
"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`,
|
|
6072
|
+
"activity-not-active": r => `activity status is "${r.status}"`,
|
|
6073
|
+
"stage-terminal": r => `stage "${r.stage}" is terminal`,
|
|
6074
|
+
"instance-completed": r => `instance completed at ${r.completedAt}`,
|
|
6075
|
+
"instance-aborted": r => `instance aborted at ${r.abortedAt}`,
|
|
6076
|
+
"requirements-unmet": r => `unmet requirement(s): ${r.unmetRequirements.join(", ")}`,
|
|
6077
|
+
"subject-permission-denied": r => `missing subject permission(s): ${subjectDenialLabels(r.denied).join(", ")}`
|
|
6078
|
+
};
|
|
6079
|
+
|
|
6080
|
+
function actionDisabledDetail(reason) {
|
|
6081
|
+
return disabledReasonDetail[reason.kind](reason);
|
|
6082
|
+
}
|
|
6083
|
+
|
|
6084
|
+
function formatDisabledReason({activity: activity, action: action, reason: reason}) {
|
|
6085
|
+
return `Action "${activity}:${action}" is not allowed: ${actionDisabledDetail(reason)}`;
|
|
6086
|
+
}
|
|
6087
|
+
|
|
6088
|
+
class EditFieldDeniedError extends invariants.WorkflowError {
|
|
6089
|
+
reason;
|
|
6090
|
+
target;
|
|
6091
|
+
constructor(args) {
|
|
6092
|
+
super("edit-field-denied", formatEditDisabledReason(args.target, args.reason)),
|
|
6093
|
+
this.name = "EditFieldDeniedError", this.reason = args.reason, this.target = args.target;
|
|
6094
|
+
}
|
|
6095
|
+
}
|
|
6096
|
+
|
|
6097
|
+
const editDisabledReasonDetail = {
|
|
6098
|
+
"not-editable": () => "field is not declared editable",
|
|
6099
|
+
"instance-completed": r => `instance completed at ${r.completedAt}`,
|
|
6100
|
+
"instance-aborted": r => `instance aborted at ${r.abortedAt}`,
|
|
6101
|
+
"edit-window-closed": r => `edit window closed (${r.detail})`,
|
|
6102
|
+
"editor-not-permitted": r => `editor not permitted (${r.predicate})`
|
|
6103
|
+
};
|
|
6104
|
+
|
|
6105
|
+
function formatEditDisabledReason(target, reason) {
|
|
6106
|
+
const detail = editDisabledReasonDetail[reason.kind](reason), where = target.activity !== void 0 ? `${target.activity}.${target.field}` : target.field;
|
|
6107
|
+
return `Field "${target.scope}:${where}" is not editable: ${detail}`;
|
|
6108
|
+
}
|
|
6109
|
+
|
|
6110
|
+
async function fireAction(args) {
|
|
6111
|
+
const {client: client, instanceId: instanceId, activity: activity, action: action, params: params, requestRecord: requestRecord, options: options} = args;
|
|
6112
|
+
return retryOnRevisionConflict({
|
|
6113
|
+
client: client,
|
|
6114
|
+
instanceId: instanceId,
|
|
6115
|
+
options: options,
|
|
6116
|
+
commit: ctx => commitAction({
|
|
6117
|
+
ctx: ctx,
|
|
6118
|
+
activityName: activity,
|
|
6119
|
+
actionName: action,
|
|
6120
|
+
callerParams: params,
|
|
6121
|
+
requestRecord: requestRecord,
|
|
6122
|
+
options: options
|
|
6123
|
+
}),
|
|
6124
|
+
onExhausted: () => new ConcurrentFireActionError({
|
|
6125
|
+
instanceId: instanceId,
|
|
6126
|
+
activity: activity,
|
|
6127
|
+
action: action,
|
|
6128
|
+
attempts: CONCURRENT_COMMIT_MAX_ATTEMPTS
|
|
6129
|
+
})
|
|
6130
|
+
});
|
|
6131
|
+
}
|
|
6132
|
+
|
|
6133
|
+
async function resolveActionCommit({ctx: ctx, activityName: activityName, actionName: actionName, callerParams: callerParams, options: options}) {
|
|
6134
|
+
const actor = options?.actor, {stage: stage, activity: activity} = findActivityInCurrentStage(ctx, activityName), action = (activity.actions ?? []).find(a => a.name === actionName);
|
|
6135
|
+
if (action === void 0) throw new invariants.ContractViolationError(`Action "${actionName}" not declared on activity "${activityName}"`);
|
|
6136
|
+
if (action.when !== void 0) throw new ActionDisabledError({
|
|
6137
|
+
activity: activityName,
|
|
6138
|
+
action: actionName,
|
|
6139
|
+
reason: {
|
|
6140
|
+
kind: "cascade-fired",
|
|
6141
|
+
when: action.when
|
|
6142
|
+
}
|
|
6143
|
+
});
|
|
6144
|
+
if (action.filter !== void 0) {
|
|
6145
|
+
const can = options?.grants !== void 0 && actor !== void 0 ? await advisoryCan({
|
|
6146
|
+
instance: ctx.instance,
|
|
6147
|
+
actor: actor,
|
|
6148
|
+
grants: options.grants
|
|
6149
|
+
}) : void 0;
|
|
6150
|
+
if (!await ctxEvaluateCondition({
|
|
6151
|
+
ctx: ctx,
|
|
6152
|
+
condition: action.filter,
|
|
6153
|
+
opts: {
|
|
6154
|
+
activityName: activityName,
|
|
6155
|
+
...actor !== void 0 ? {
|
|
6156
|
+
actor: actor
|
|
6157
|
+
} : {},
|
|
6158
|
+
...can !== void 0 ? {
|
|
6159
|
+
vars: {
|
|
6160
|
+
can: can
|
|
6161
|
+
}
|
|
6162
|
+
} : {}
|
|
6163
|
+
}
|
|
6164
|
+
})) throw new ActionDisabledError({
|
|
6165
|
+
activity: activityName,
|
|
6166
|
+
action: actionName,
|
|
6167
|
+
reason: {
|
|
6168
|
+
kind: "filter-failed",
|
|
6169
|
+
filter: action.filter,
|
|
6170
|
+
detail: "commit re-check"
|
|
6171
|
+
}
|
|
6172
|
+
});
|
|
6173
|
+
}
|
|
6174
|
+
const entry = findCurrentActivities(ctx.instance).find(t => t.name === activityName);
|
|
6175
|
+
if (entry === void 0 || entry.status !== "active") throw notActiveAtCommit({
|
|
6176
|
+
activityName: activityName,
|
|
6177
|
+
actionName: actionName,
|
|
6178
|
+
status: entry?.status
|
|
6179
|
+
});
|
|
6180
|
+
const params = validateActionParams({
|
|
6181
|
+
action: action,
|
|
6182
|
+
activityName: activityName,
|
|
6183
|
+
callerParams: callerParams
|
|
6184
|
+
});
|
|
6185
|
+
return {
|
|
6186
|
+
stage: stage,
|
|
6187
|
+
activity: activity,
|
|
6188
|
+
action: action,
|
|
6189
|
+
params: params
|
|
6190
|
+
};
|
|
6191
|
+
}
|
|
6192
|
+
|
|
6193
|
+
function notActiveAtCommit(args) {
|
|
6194
|
+
const {activityName: activityName, actionName: actionName, status: status} = args;
|
|
6195
|
+
return status !== void 0 && invariants.isTerminalActivityStatus(status) ? new ActionDisabledError({
|
|
6196
|
+
activity: activityName,
|
|
6197
|
+
action: actionName,
|
|
6198
|
+
reason: {
|
|
6199
|
+
kind: "activity-not-active",
|
|
6200
|
+
status: status
|
|
6201
|
+
}
|
|
6202
|
+
}) : new Error(`Activity "${activityName}" must be active to fire action "${actionName}"; status is ${status ?? "missing"}`);
|
|
6203
|
+
}
|
|
6204
|
+
|
|
6205
|
+
async function applyActionFire({ctx: ctx, mutation: mutation, activity: activity, action: action, params: params, actor: actor, triggered: triggered}) {
|
|
6206
|
+
mutation.history.push({
|
|
6207
|
+
_key: randomKey(),
|
|
6208
|
+
_type: "actionFired",
|
|
6209
|
+
at: ctx.now,
|
|
6210
|
+
stage: mutation.currentStage,
|
|
6211
|
+
activity: activity.name,
|
|
6212
|
+
action: action.name,
|
|
6213
|
+
...actor !== void 0 ? {
|
|
6214
|
+
actor: actor,
|
|
6215
|
+
driverKind: invariants.driverKind(actor)
|
|
6216
|
+
} : {},
|
|
6217
|
+
...triggered ? {
|
|
6218
|
+
triggered: !0
|
|
6219
|
+
} : {}
|
|
6220
|
+
});
|
|
6221
|
+
const ranOps = await runOps({
|
|
6222
|
+
ops: action.ops,
|
|
6223
|
+
mutation: mutation,
|
|
6224
|
+
stage: mutation.currentStage,
|
|
6225
|
+
origin: {
|
|
6226
|
+
activity: activity.name,
|
|
6227
|
+
action: action.name
|
|
6228
|
+
},
|
|
6229
|
+
params: params,
|
|
6230
|
+
actor: actor,
|
|
6231
|
+
self: invariants.selfGdr(ctx.instance),
|
|
6232
|
+
now: ctx.now,
|
|
6233
|
+
snapshot: ctx.snapshot
|
|
6234
|
+
});
|
|
6235
|
+
return await queueEffects({
|
|
6236
|
+
ctx: ctx,
|
|
6237
|
+
mutation: mutation,
|
|
6238
|
+
effects: action.effects,
|
|
6239
|
+
origin: {
|
|
6240
|
+
kind: "action",
|
|
6241
|
+
name: action.name
|
|
6242
|
+
},
|
|
6243
|
+
actor: actor,
|
|
6244
|
+
opts: {
|
|
6245
|
+
callerParams: params,
|
|
6246
|
+
activityName: activity.name
|
|
6247
|
+
}
|
|
6248
|
+
}), action.spawn !== void 0 && await spawnSubworkflows({
|
|
6249
|
+
ctx: ctx,
|
|
6250
|
+
mutation: mutation,
|
|
6251
|
+
activity: activity,
|
|
6252
|
+
action: action,
|
|
6253
|
+
sub: action.spawn,
|
|
6254
|
+
actor: actor
|
|
6255
|
+
}), {
|
|
6256
|
+
ranOps: ranOps
|
|
6257
|
+
};
|
|
6258
|
+
}
|
|
6259
|
+
|
|
6260
|
+
async function commitAction({ctx: ctx, activityName: activityName, actionName: actionName, callerParams: callerParams, requestRecord: requestRecord, options: options}) {
|
|
6261
|
+
assertRequestUnprocessed({
|
|
6262
|
+
instance: ctx.instance,
|
|
6263
|
+
record: requestRecord,
|
|
6264
|
+
now: ctx.now
|
|
6265
|
+
});
|
|
6266
|
+
const actor = options?.actor, {stage: stage, activity: activity, action: action, params: params} = await resolveActionCommit({
|
|
6267
|
+
ctx: ctx,
|
|
6268
|
+
activityName: activityName,
|
|
6269
|
+
actionName: actionName,
|
|
6270
|
+
callerParams: callerParams,
|
|
6271
|
+
options: options
|
|
6272
|
+
}), mutation = startMutation(ctx.instance);
|
|
6273
|
+
recordProcessedRequest({
|
|
6274
|
+
mutation: mutation,
|
|
6275
|
+
record: requestRecord,
|
|
6276
|
+
now: ctx.now
|
|
6277
|
+
});
|
|
6278
|
+
const mutEntry = requireMutationActivityEntry(mutation, activityName), statusBefore = mutEntry.status, {ranOps: ranOps} = await applyActionFire({
|
|
6279
|
+
ctx: ctx,
|
|
6280
|
+
mutation: mutation,
|
|
6281
|
+
activity: activity,
|
|
6282
|
+
action: action,
|
|
6283
|
+
params: params,
|
|
6284
|
+
actor: actor,
|
|
6285
|
+
triggered: !1
|
|
6286
|
+
}), newStatus = mutEntry.status !== statusBefore ? mutEntry.status : void 0;
|
|
6287
|
+
return await persistThenMaybeRefresh({
|
|
6288
|
+
ctx: ctx,
|
|
6289
|
+
mutation: mutation,
|
|
6290
|
+
stageName: stage.name,
|
|
6291
|
+
didChangeState: ranOps.some(isFieldOp)
|
|
6292
|
+
}), {
|
|
6293
|
+
fired: !0,
|
|
6294
|
+
activity: activityName,
|
|
6295
|
+
action: actionName,
|
|
6296
|
+
...newStatus !== void 0 ? {
|
|
6297
|
+
newStatus: newStatus
|
|
6298
|
+
} : {},
|
|
6299
|
+
...ranOps.length > 0 ? {
|
|
6300
|
+
ranOps: ranOps
|
|
6301
|
+
} : {}
|
|
6302
|
+
};
|
|
6303
|
+
}
|
|
6304
|
+
|
|
6305
|
+
async function runTriggeredActions({ctx: ctx, mutation: mutation, stage: stage}) {
|
|
6306
|
+
let fires = 0, ranFieldOps = !1;
|
|
6307
|
+
const liveCtx = liveViewContext(ctx, mutation);
|
|
6308
|
+
let firedThisPass = !0;
|
|
6309
|
+
for (;firedThisPass; ) {
|
|
6310
|
+
firedThisPass = !1;
|
|
6311
|
+
for (const activity of stage.activities ?? []) {
|
|
6312
|
+
const fired = await runActivityTriggers({
|
|
6313
|
+
ctx: liveCtx,
|
|
6314
|
+
mutation: mutation,
|
|
6315
|
+
activity: activity
|
|
6316
|
+
});
|
|
6317
|
+
fires += fired.fires, ranFieldOps = ranFieldOps || fired.ranFieldOps, fired.fires > 0 && (firedThisPass = !0);
|
|
6318
|
+
}
|
|
6319
|
+
}
|
|
6320
|
+
return {
|
|
6321
|
+
fires: fires,
|
|
6322
|
+
ranFieldOps: ranFieldOps
|
|
6323
|
+
};
|
|
6324
|
+
}
|
|
6325
|
+
|
|
6326
|
+
async function runActivityTriggers({ctx: ctx, mutation: mutation, activity: activity}) {
|
|
6327
|
+
let fires = 0, ranFieldOps = !1;
|
|
6328
|
+
for (const action of activity.actions ?? []) {
|
|
6329
|
+
if (!invariants.isCascadeFired(action)) continue;
|
|
6330
|
+
const entry = currentActivities(mutation).find(e => e.name === activity.name);
|
|
6331
|
+
if (entry === void 0 || entry.status !== "active") break;
|
|
6332
|
+
if ((entry.firedActions ?? []).includes(action.name) || !await triggerIsLive({
|
|
6333
|
+
ctx: ctx,
|
|
6334
|
+
activity: activity,
|
|
6335
|
+
action: action
|
|
6336
|
+
})) continue;
|
|
6337
|
+
const result = await fireTriggeredAction({
|
|
6338
|
+
ctx: ctx,
|
|
6339
|
+
mutation: mutation,
|
|
6340
|
+
activity: activity,
|
|
6341
|
+
action: action,
|
|
6342
|
+
entry: entry
|
|
6343
|
+
});
|
|
6344
|
+
if (fires++, ranFieldOps = ranFieldOps || result.ranFieldOps, invariants.isTerminalActivityStatus(entry.status)) break;
|
|
6345
|
+
}
|
|
6346
|
+
return {
|
|
6347
|
+
fires: fires,
|
|
6348
|
+
ranFieldOps: ranFieldOps
|
|
6349
|
+
};
|
|
6350
|
+
}
|
|
6351
|
+
|
|
6352
|
+
async function triggerIsLive({ctx: ctx, activity: activity, action: action}) {
|
|
6353
|
+
if (!tokenMayExecute({
|
|
6354
|
+
actor: ctx.actor,
|
|
6355
|
+
action: action,
|
|
6356
|
+
ctx: ctx
|
|
6357
|
+
})) return !1;
|
|
6358
|
+
const scope = {
|
|
6359
|
+
activityName: activity.name
|
|
6360
|
+
};
|
|
6361
|
+
return action.filter !== void 0 && await ctxEvaluateConditionOutcome({
|
|
6362
|
+
ctx: ctx,
|
|
6363
|
+
condition: action.filter,
|
|
6364
|
+
opts: scope
|
|
6365
|
+
}) !== "satisfied" ? !1 : await ctxEvaluateConditionOutcome({
|
|
6366
|
+
ctx: ctx,
|
|
6367
|
+
condition: action.when,
|
|
6368
|
+
opts: scope
|
|
6369
|
+
}) === "satisfied";
|
|
6370
|
+
}
|
|
6371
|
+
|
|
6372
|
+
function tokenMayExecute({actor: actor, action: action, ctx: ctx}) {
|
|
6373
|
+
const roles = action.roles;
|
|
6374
|
+
return roles === void 0 || roles.length === 0 ? !0 : actor === void 0 ? !1 : roles.some(required => invariants.actorFulfillsRole({
|
|
6375
|
+
actorRoles: actor.roles,
|
|
6376
|
+
required: required,
|
|
6377
|
+
aliases: ctx.definition.roleAliases
|
|
6378
|
+
}));
|
|
6379
|
+
}
|
|
6380
|
+
|
|
6381
|
+
async function fireTriggeredAction({ctx: ctx, mutation: mutation, activity: activity, action: action, entry: entry}) {
|
|
6382
|
+
entry.firedActions = [ ...entry.firedActions ?? [], action.name ];
|
|
6383
|
+
const {ranOps: ranOps} = await applyActionFire({
|
|
6384
|
+
ctx: ctx,
|
|
6385
|
+
mutation: mutation,
|
|
6386
|
+
activity: activity,
|
|
6387
|
+
action: action,
|
|
6388
|
+
params: {},
|
|
6389
|
+
actor: ctx.actor,
|
|
6390
|
+
triggered: !0
|
|
6391
|
+
});
|
|
6392
|
+
return {
|
|
6393
|
+
ranFieldOps: ranOps.some(isFieldOp)
|
|
6394
|
+
};
|
|
6395
|
+
}
|
|
6396
|
+
|
|
6397
|
+
async function primeInitialStage({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, clock: clock, executionContext: executionContext, telemetry: telemetry}) {
|
|
6398
|
+
const instance = await getInstanceDocument(client, instanceId);
|
|
6399
|
+
if (!instance || instance.stages.length > 0) return;
|
|
6400
|
+
const definition = parseDefinitionSnapshot(instance), stage = definition.stages.find(s => s.name === instance.currentStage);
|
|
6401
|
+
if (stage === void 0) return;
|
|
6402
|
+
const ctx = await buildEngineContext({
|
|
6403
|
+
client: client,
|
|
6404
|
+
clientForGdr: clientForGdr,
|
|
6405
|
+
instance: instance,
|
|
6406
|
+
definition: definition,
|
|
6407
|
+
...clock ? {
|
|
6408
|
+
clock: clock
|
|
6409
|
+
} : {},
|
|
6410
|
+
...actor ? {
|
|
6411
|
+
actor: actor
|
|
6412
|
+
} : {},
|
|
6413
|
+
...executionContext ? {
|
|
6414
|
+
executionContext: executionContext
|
|
6415
|
+
} : {},
|
|
6416
|
+
...telemetry ? {
|
|
6417
|
+
telemetry: telemetry
|
|
6418
|
+
} : {}
|
|
6419
|
+
}), now = ctx.now, discards = [], initialStageEntry = {
|
|
6420
|
+
_key: randomKey(),
|
|
6421
|
+
name: stage.name,
|
|
6422
|
+
enteredAt: now,
|
|
6423
|
+
fields: await resolveStageFieldEntries({
|
|
6424
|
+
client: client,
|
|
6425
|
+
instance: instance,
|
|
6426
|
+
stage: stage,
|
|
6427
|
+
now: now,
|
|
6428
|
+
recordDiscard: recordFieldDiscards({
|
|
6429
|
+
target: discards,
|
|
6430
|
+
scope: "stage",
|
|
6431
|
+
at: now
|
|
6432
|
+
})
|
|
6433
|
+
}),
|
|
6434
|
+
activities: []
|
|
6435
|
+
}, primedCtx = {
|
|
6436
|
+
...ctx,
|
|
6437
|
+
instance: {
|
|
6438
|
+
...instance,
|
|
6439
|
+
stages: [ initialStageEntry ]
|
|
6440
|
+
}
|
|
6441
|
+
};
|
|
6442
|
+
initialStageEntry.activities = await buildStageActivities({
|
|
6443
|
+
ctx: primedCtx,
|
|
6444
|
+
stage: stage,
|
|
6445
|
+
recordDiscard: recordFieldDiscards({
|
|
6446
|
+
target: discards,
|
|
6447
|
+
scope: "activity",
|
|
6448
|
+
at: now
|
|
6449
|
+
})
|
|
6450
|
+
});
|
|
6451
|
+
const terminal = isTerminalStage(stage), committed = await client.patch(instance._id).set({
|
|
6452
|
+
stages: [ initialStageEntry ],
|
|
6453
|
+
lastChangedAt: now,
|
|
6454
|
+
...terminal ? {
|
|
6455
|
+
completedAt: now
|
|
6456
|
+
} : {},
|
|
6457
|
+
...discards.length > 0 ? {
|
|
6458
|
+
history: [ ...instance.history, ...stampHistoryEntries(discards, ctx.executionContext) ]
|
|
6459
|
+
} : {}
|
|
6460
|
+
}).ifRevisionId(instance._rev).commit(SYNC_COMMIT);
|
|
6461
|
+
await deployOrRollback({
|
|
6462
|
+
client: client,
|
|
6463
|
+
instanceId: instance._id,
|
|
6464
|
+
committedRev: committed._rev,
|
|
6465
|
+
restore: {
|
|
6466
|
+
stages: instance.stages,
|
|
6467
|
+
history: instance.history
|
|
6468
|
+
},
|
|
6469
|
+
...terminal ? {
|
|
6470
|
+
unset: [ "completedAt" ]
|
|
6471
|
+
} : {},
|
|
6472
|
+
reversible: !0,
|
|
6473
|
+
deploy: () => deployStageGuards({
|
|
6474
|
+
client: client,
|
|
6475
|
+
clientForGdr: clientForGdr,
|
|
6476
|
+
instance: instance,
|
|
6477
|
+
definition: definition,
|
|
6478
|
+
stageName: stage.name,
|
|
6479
|
+
now: now,
|
|
6480
|
+
snapshot: ctx.snapshot
|
|
6481
|
+
})
|
|
6482
|
+
});
|
|
6483
|
+
}
|
|
6484
|
+
|
|
6485
|
+
const CASCADE_LIMIT = 100;
|
|
6486
|
+
|
|
6487
|
+
async function runCascadeHop({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, clock: clock, executionContext: executionContext, telemetry: telemetry, overlay: overlay}) {
|
|
6488
|
+
const ctx = await loadContext({
|
|
6489
|
+
client: client,
|
|
6490
|
+
instanceId: instanceId,
|
|
6491
|
+
options: {
|
|
6492
|
+
clientForGdr: clientForGdr,
|
|
6493
|
+
...actor ? {
|
|
6494
|
+
actor: actor
|
|
6495
|
+
} : {},
|
|
6496
|
+
...clock ? {
|
|
6497
|
+
clock: clock
|
|
6498
|
+
} : {},
|
|
6499
|
+
...executionContext ? {
|
|
6500
|
+
executionContext: executionContext
|
|
6501
|
+
} : {},
|
|
6502
|
+
...telemetry ? {
|
|
6503
|
+
telemetry: telemetry
|
|
6504
|
+
} : {},
|
|
6505
|
+
...overlay ? {
|
|
6506
|
+
overlay: overlay
|
|
6507
|
+
} : {}
|
|
6508
|
+
}
|
|
6509
|
+
});
|
|
6510
|
+
if (isTerminal(ctx)) return {
|
|
6511
|
+
moved: !1
|
|
6512
|
+
};
|
|
6513
|
+
const stage = findStage(ctx.definition, ctx.instance.currentStage), mutation = startMutation(ctx.instance), fired = await runTriggeredActions({
|
|
6514
|
+
ctx: ctx,
|
|
6515
|
+
mutation: mutation,
|
|
6516
|
+
stage: stage
|
|
6517
|
+
}), hopCtx = liveViewContext(ctx, mutation), transition = await pickTransition(hopCtx, stage);
|
|
6518
|
+
return transition === void 0 ? (fired.fires > 0 && await persistThenMaybeRefresh({
|
|
6519
|
+
ctx: ctx,
|
|
6520
|
+
mutation: mutation,
|
|
6521
|
+
stageName: stage.name,
|
|
6522
|
+
didChangeState: fired.ranFieldOps
|
|
6523
|
+
}), {
|
|
6524
|
+
moved: !1
|
|
6525
|
+
}) : (await commitStageMove({
|
|
6526
|
+
ctx: ctx,
|
|
6527
|
+
mutation: mutation,
|
|
6528
|
+
fromStage: stage,
|
|
6529
|
+
toStage: findStage(ctx.definition, transition.to),
|
|
6530
|
+
transition: transition.name,
|
|
6531
|
+
via: "transition",
|
|
6532
|
+
actor: actor
|
|
6533
|
+
}), {
|
|
6534
|
+
moved: !0
|
|
6535
|
+
});
|
|
6536
|
+
}
|
|
6537
|
+
|
|
6538
|
+
async function cascadeAutoTransitions({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, clock: clock, executionContext: executionContext, telemetry: telemetry, overlay: overlay}) {
|
|
6539
|
+
let count = 0;
|
|
6540
|
+
for (;;) {
|
|
6541
|
+
if (await drainCondemnedChildren({
|
|
6542
|
+
client: client,
|
|
6543
|
+
instanceId: instanceId,
|
|
6544
|
+
actor: actor,
|
|
6545
|
+
clientForGdr: clientForGdr,
|
|
6546
|
+
clock: clock,
|
|
6547
|
+
executionContext: executionContext,
|
|
6548
|
+
telemetry: telemetry
|
|
6549
|
+
}), !(await runCascadeHop({
|
|
6550
|
+
client: client,
|
|
6551
|
+
instanceId: instanceId,
|
|
6552
|
+
actor: actor,
|
|
6553
|
+
clientForGdr: clientForGdr,
|
|
6554
|
+
clock: clock,
|
|
6555
|
+
executionContext: executionContext,
|
|
6556
|
+
telemetry: telemetry,
|
|
6557
|
+
overlay: overlay
|
|
6558
|
+
})).moved) return count;
|
|
6559
|
+
if (count++, count >= CASCADE_LIMIT) throw new CascadeLimitError({
|
|
6560
|
+
instanceId: instanceId,
|
|
6561
|
+
limit: CASCADE_LIMIT
|
|
6562
|
+
});
|
|
6563
|
+
}
|
|
6564
|
+
}
|
|
6565
|
+
|
|
6566
|
+
async function drainCondemnedChildren({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, clock: clock, executionContext: executionContext, telemetry: telemetry, draining: draining = /* @__PURE__ */ new Set}) {
|
|
6567
|
+
if (draining.has(instanceId)) return;
|
|
6568
|
+
draining.add(instanceId);
|
|
6569
|
+
const instance = await getInstanceDocument(client, instanceId);
|
|
6570
|
+
if (!instance) return;
|
|
6571
|
+
const condemned = condemnedSubworkflows(instance);
|
|
6572
|
+
if (condemned.length !== 0) {
|
|
6573
|
+
for (const row of condemned) await settleCondemnedRow({
|
|
6574
|
+
client: client,
|
|
6575
|
+
ownerId: instanceId,
|
|
6576
|
+
row: row,
|
|
6577
|
+
actor: actor,
|
|
6578
|
+
clientForGdr: clientForGdr,
|
|
6579
|
+
clock: clock,
|
|
6580
|
+
executionContext: executionContext,
|
|
6581
|
+
telemetry: telemetry,
|
|
6582
|
+
draining: draining
|
|
6583
|
+
});
|
|
6584
|
+
await stampDrainedRows({
|
|
6585
|
+
client: client,
|
|
6586
|
+
instance: instance,
|
|
6587
|
+
condemned: condemned,
|
|
6588
|
+
clock: clock,
|
|
6589
|
+
executionContext: executionContext
|
|
6590
|
+
});
|
|
6591
|
+
}
|
|
6592
|
+
}
|
|
6593
|
+
|
|
6594
|
+
async function settleCondemnedRow({client: client, ownerId: ownerId, row: row, actor: actor, clientForGdr: clientForGdr, clock: clock, executionContext: executionContext, telemetry: telemetry, draining: draining}) {
|
|
6595
|
+
const childId = invariants.toBareId(row.ref.id);
|
|
6596
|
+
try {
|
|
6597
|
+
await abortInstance({
|
|
6598
|
+
client: client,
|
|
6599
|
+
instanceId: childId,
|
|
6600
|
+
reason: row.abortPending?.reason ?? "condemned by parent",
|
|
6601
|
+
options: {
|
|
6602
|
+
...actor ? {
|
|
6603
|
+
actor: actor
|
|
6604
|
+
} : {},
|
|
6605
|
+
clientForGdr: clientForGdr,
|
|
6606
|
+
...clock ? {
|
|
6607
|
+
clock: clock
|
|
6608
|
+
} : {},
|
|
6609
|
+
...executionContext ? {
|
|
6610
|
+
executionContext: executionContext
|
|
6611
|
+
} : {},
|
|
6612
|
+
...telemetry ? {
|
|
6613
|
+
telemetry: telemetry
|
|
6614
|
+
} : {}
|
|
6615
|
+
}
|
|
6616
|
+
}), await drainCondemnedChildren({
|
|
6617
|
+
client: client,
|
|
6618
|
+
instanceId: childId,
|
|
6619
|
+
actor: actor,
|
|
6620
|
+
clientForGdr: clientForGdr,
|
|
6621
|
+
clock: clock,
|
|
6622
|
+
executionContext: executionContext,
|
|
6623
|
+
telemetry: telemetry,
|
|
6624
|
+
draining: draining
|
|
6625
|
+
});
|
|
6626
|
+
} catch (cause) {
|
|
6627
|
+
if (cause instanceof invariants.InstanceNotFoundError) return;
|
|
6628
|
+
throw cause instanceof WorkflowStateDivergedError ? cause : new WorkflowStateDivergedError({
|
|
6629
|
+
instanceId: ownerId,
|
|
6630
|
+
guardError: cause,
|
|
6631
|
+
reason: `owed child abort of "${childId}" failed while draining condemned rows`
|
|
6632
|
+
});
|
|
6633
|
+
}
|
|
6634
|
+
}
|
|
6635
|
+
|
|
6636
|
+
async function stampDrainedRows({client: client, instance: instance, condemned: condemned, clock: clock, executionContext: executionContext}) {
|
|
6637
|
+
const now = (clock ?? wallClock)(), stamp = resolveExecutionContext(executionContext), ids = condemned.map(row => invariants.toBareId(row.ref.id)), children = await client.fetch("*[_id in $ids]{_id, currentStage, completedAt, abortedAt, modelVersion, minReaderModel}", {
|
|
6638
|
+
ids: ids
|
|
6639
|
+
}), byId = new Map(children.map(c => [ assertReadableModel(c)._id, c ])), condemnedKeys = new Set(condemned.map(row => row._key)), history = [ ...instance.history ], subworkflows = (instance.subworkflows ?? []).map(row => {
|
|
6640
|
+
if (row.resolved !== void 0 || !condemnedKeys.has(row._key)) return row;
|
|
6641
|
+
const child = byId.get(invariants.toBareId(row.ref.id)), resolved = child !== void 0 ? terminalResolution(child) : {
|
|
6642
|
+
at: now,
|
|
6643
|
+
aborted: !0
|
|
6644
|
+
};
|
|
6645
|
+
return resolved === void 0 ? row : (history.push(...stampHistoryEntries([ subworkflowResolvedEntry({
|
|
6646
|
+
row: row,
|
|
6647
|
+
at: now,
|
|
6648
|
+
status: resolved.aborted === !0 ? "aborted" : "done"
|
|
6649
|
+
}) ], stamp)), {
|
|
6650
|
+
...row,
|
|
6651
|
+
resolved: resolved
|
|
6652
|
+
});
|
|
6653
|
+
});
|
|
6654
|
+
await client.patch(instance._id).set({
|
|
6655
|
+
subworkflows: subworkflows,
|
|
6656
|
+
history: history,
|
|
6657
|
+
lastChangedAt: now
|
|
6658
|
+
}).ifRevisionId(instance._rev).commit(SYNC_COMMIT);
|
|
6659
|
+
}
|
|
6660
|
+
|
|
6661
|
+
function terminalResolution(child) {
|
|
6662
|
+
const status = resolvedChildStatus(child);
|
|
6663
|
+
if (!(status === void 0 || child.completedAt === void 0 || child.completedAt === null)) return {
|
|
6664
|
+
at: child.completedAt,
|
|
6665
|
+
stage: child.currentStage,
|
|
6666
|
+
...status === "aborted" ? {
|
|
6667
|
+
aborted: !0
|
|
6451
6668
|
} : {}
|
|
6452
6669
|
};
|
|
6453
6670
|
}
|
|
6454
6671
|
|
|
6455
|
-
|
|
6456
|
-
|
|
6457
|
-
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6461
|
-
|
|
6462
|
-
if (denied.length !== 0) return {
|
|
6463
|
-
kind: "mutation-guard-denied",
|
|
6464
|
-
denied: deniedGuardRefs(denied)
|
|
6465
|
-
};
|
|
6466
|
-
}
|
|
6467
|
-
|
|
6468
|
-
function lifecycleReason({instance: instance, status: status, stageHasExits: stageHasExits}) {
|
|
6469
|
-
const terminal = terminalState(instance);
|
|
6470
|
-
if (terminal === "aborted" && instance.abortedAt !== void 0) return {
|
|
6471
|
-
kind: "instance-aborted",
|
|
6472
|
-
abortedAt: instance.abortedAt
|
|
6473
|
-
};
|
|
6474
|
-
if (terminal === "completed" && instance.completedAt !== void 0) return {
|
|
6475
|
-
kind: "instance-completed",
|
|
6476
|
-
completedAt: instance.completedAt
|
|
6477
|
-
};
|
|
6478
|
-
if (!stageHasExits) return {
|
|
6479
|
-
kind: "stage-terminal",
|
|
6480
|
-
stage: instance.currentStage
|
|
6481
|
-
};
|
|
6482
|
-
if (invariants.isTerminalActivityStatus(status)) return {
|
|
6483
|
-
kind: "activity-not-active",
|
|
6672
|
+
function subworkflowResolvedEntry({row: row, at: at, status: status}) {
|
|
6673
|
+
return {
|
|
6674
|
+
_key: randomKey(),
|
|
6675
|
+
_type: "subworkflowResolved",
|
|
6676
|
+
at: at,
|
|
6677
|
+
activity: row.activity,
|
|
6678
|
+
instanceRef: row.ref,
|
|
6484
6679
|
status: status
|
|
6485
6680
|
};
|
|
6486
6681
|
}
|
|
6487
6682
|
|
|
6488
|
-
function
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
|
|
6492
|
-
|
|
6493
|
-
|
|
6494
|
-
|
|
6683
|
+
async function propagateToAncestors({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, clock: clock, executionContext: executionContext, telemetry: telemetry}) {
|
|
6684
|
+
const loaded = await loadPropagationPair(client, instanceId);
|
|
6685
|
+
if (loaded === void 0) return;
|
|
6686
|
+
const {child: child, parent: parent} = loaded, definition = parseDefinitionSnapshot(parent), ctx = await buildEngineContext({
|
|
6687
|
+
client: client,
|
|
6688
|
+
clientForGdr: clientForGdr,
|
|
6689
|
+
instance: parent,
|
|
6690
|
+
definition: definition,
|
|
6691
|
+
...clock ? {
|
|
6692
|
+
clock: clock
|
|
6693
|
+
} : {},
|
|
6694
|
+
...actor ? {
|
|
6695
|
+
actor: actor
|
|
6696
|
+
} : {},
|
|
6697
|
+
...executionContext ? {
|
|
6698
|
+
executionContext: executionContext
|
|
6699
|
+
} : {},
|
|
6700
|
+
...telemetry ? {
|
|
6701
|
+
telemetry: telemetry
|
|
6495
6702
|
} : {}
|
|
6703
|
+
}), mutation = startMutation(parent), row = mutation.subworkflows.find(r => invariants.toBareId(r.ref.id) === child._id);
|
|
6704
|
+
if (row === void 0) {
|
|
6705
|
+
await recordOrphanedPropagation({
|
|
6706
|
+
ctx: ctx,
|
|
6707
|
+
mutation: mutation,
|
|
6708
|
+
child: child
|
|
6709
|
+
});
|
|
6710
|
+
return;
|
|
6711
|
+
}
|
|
6712
|
+
const changed = stampResolvedChild({
|
|
6713
|
+
mutation: mutation,
|
|
6714
|
+
row: row,
|
|
6715
|
+
child: child,
|
|
6716
|
+
now: ctx.now
|
|
6717
|
+
}), parentTerminal = parent.completedAt !== void 0;
|
|
6718
|
+
changed && await persist(ctx, mutation), !parentTerminal && (await cascadeAutoTransitions({
|
|
6719
|
+
client: client,
|
|
6720
|
+
instanceId: parent._id,
|
|
6721
|
+
actor: actor,
|
|
6722
|
+
clientForGdr: clientForGdr,
|
|
6723
|
+
clock: clock,
|
|
6724
|
+
executionContext: executionContext,
|
|
6725
|
+
telemetry: telemetry
|
|
6726
|
+
}), await propagateToAncestors({
|
|
6727
|
+
client: client,
|
|
6728
|
+
instanceId: parent._id,
|
|
6729
|
+
actor: actor,
|
|
6730
|
+
clientForGdr: clientForGdr,
|
|
6731
|
+
clock: clock,
|
|
6732
|
+
executionContext: executionContext,
|
|
6733
|
+
telemetry: telemetry
|
|
6734
|
+
}));
|
|
6735
|
+
}
|
|
6736
|
+
|
|
6737
|
+
async function loadPropagationPair(client, instanceId) {
|
|
6738
|
+
const child = await getInstanceDocument(client, instanceId);
|
|
6739
|
+
if (!child) return;
|
|
6740
|
+
const parentGdr = parentRef(child);
|
|
6741
|
+
if (parentGdr === void 0) return;
|
|
6742
|
+
const parent = await getInstanceDocument(client, invariants.toBareId(parentGdr.id));
|
|
6743
|
+
if (!(!parent || parent._type !== WORKFLOW_INSTANCE_TYPE) && typeof parent.definitionSnapshot == "string") return {
|
|
6744
|
+
child: child,
|
|
6745
|
+
parent: parent
|
|
6496
6746
|
};
|
|
6497
6747
|
}
|
|
6498
6748
|
|
|
6749
|
+
function stampResolvedChild({mutation: mutation, row: row, child: child, now: now}) {
|
|
6750
|
+
if (row.resolved !== void 0) return !1;
|
|
6751
|
+
const resolved = terminalResolution(child);
|
|
6752
|
+
return resolved === void 0 ? !1 : (row.resolved = resolved, mutation.history.push(subworkflowResolvedEntry({
|
|
6753
|
+
row: row,
|
|
6754
|
+
at: now,
|
|
6755
|
+
status: resolved.aborted === !0 ? "aborted" : "done"
|
|
6756
|
+
})), !0);
|
|
6757
|
+
}
|
|
6758
|
+
|
|
6759
|
+
async function recordOrphanedPropagation({ctx: ctx, mutation: mutation, child: child}) {
|
|
6760
|
+
const childUri = invariants.selfGdr(child);
|
|
6761
|
+
mutation.history.some(h => h._type === "subworkflowOrphaned" && h.instanceRef.id === childUri) || (mutation.history.push({
|
|
6762
|
+
_key: randomKey(),
|
|
6763
|
+
_type: "subworkflowOrphaned",
|
|
6764
|
+
at: ctx.now,
|
|
6765
|
+
instanceRef: {
|
|
6766
|
+
id: childUri,
|
|
6767
|
+
type: WORKFLOW_INSTANCE_TYPE
|
|
6768
|
+
},
|
|
6769
|
+
detail: `Instance "${child._id}" names this instance as its parent but no subworkflow-registry row matches it, so its state cannot drive any gate here.`
|
|
6770
|
+
}), await persist(ctx, mutation));
|
|
6771
|
+
}
|
|
6772
|
+
|
|
6773
|
+
function inFlightFilter() {
|
|
6774
|
+
return "!defined(completedAt)";
|
|
6775
|
+
}
|
|
6776
|
+
|
|
6499
6777
|
const FIELD_REFS_DOC = "count(fields[value.id in $documents]) > 0 || count(fields[count(value[id in $documents]) > 0]) > 0", ACTIVITY_REFS_DOC = `count(stages[count(activities[${FIELD_REFS_DOC}]) > 0]) > 0`;
|
|
6500
6778
|
|
|
6779
|
+
function documentPrefilter(documents, params) {
|
|
6780
|
+
for (const document of documents) if (!invariants.isGdrUri(document)) throw new invariants.ContractViolationError(`every document must be a resource-qualified GDR URI (e.g. "dataset:project:dataset:doc-id"); got: ${JSON.stringify(document)}`);
|
|
6781
|
+
return params.documents = [ ...documents ], params.bareIds = documents.map(invariants.extractDocumentId),
|
|
6782
|
+
`_id in $bareIds || count(ancestors[id in $documents]) > 0 || ${FIELD_REFS_DOC} || count(stages[${FIELD_REFS_DOC}]) > 0 || ${ACTIVITY_REFS_DOC}`;
|
|
6783
|
+
}
|
|
6784
|
+
|
|
6501
6785
|
function documentArm(filter, params) {
|
|
6502
6786
|
if (filter.documents === void 0 && filter.document === void 0) return;
|
|
6503
6787
|
const documents = [ ...filter.documents ?? [], ...filter.document !== void 0 ? [ filter.document ] : [] ];
|
|
6504
|
-
|
|
6505
|
-
return params.documents = documents, params.bareIds = documents.map(invariants.extractDocumentId),
|
|
6506
|
-
`_id in $bareIds || count(ancestors[id in $documents]) > 0 || ${FIELD_REFS_DOC} || count(stages[${FIELD_REFS_DOC}]) > 0 || ${ACTIVITY_REFS_DOC}`;
|
|
6788
|
+
return documentPrefilter(documents, params);
|
|
6507
6789
|
}
|
|
6508
6790
|
|
|
6509
6791
|
function idsArm(filter, params) {
|
|
@@ -6519,7 +6801,7 @@ function instancesQuery(args) {
|
|
|
6519
6801
|
const conditions = [ `_type == "${WORKFLOW_INSTANCE_TYPE}"`, invariants.tagScopeFilter() ], params = {
|
|
6520
6802
|
tag: tag
|
|
6521
6803
|
};
|
|
6522
|
-
filter.includeCompleted !== !0 && conditions.push(
|
|
6804
|
+
filter.includeCompleted !== !0 && conditions.push(inFlightFilter()), filter.definition !== void 0 && (conditions.push("definition == $definition"),
|
|
6523
6805
|
params.definition = filter.definition), filter.stage !== void 0 && (conditions.push("currentStage == $stage"),
|
|
6524
6806
|
params.stage = filter.stage);
|
|
6525
6807
|
const arms = [ documentArm(filter, params), idsArm(filter, params) ].filter(arm => arm !== void 0);
|
|
@@ -6596,17 +6878,12 @@ function topoSortDefinitions(definitions, ctx) {
|
|
|
6596
6878
|
}
|
|
6597
6879
|
|
|
6598
6880
|
function refsOf(def) {
|
|
6599
|
-
|
|
6600
|
-
|
|
6601
|
-
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
version: ref.version
|
|
6606
|
-
} : {}
|
|
6607
|
-
});
|
|
6608
|
-
}
|
|
6609
|
-
return out;
|
|
6881
|
+
return def.stages.flatMap(stage => stage.activities ?? []).flatMap(activity => activity.actions ?? []).flatMap(action => action.spawn === void 0 ? [] : [ action.spawn.definition ]).map(ref => ({
|
|
6882
|
+
name: ref.name,
|
|
6883
|
+
...ref.version !== void 0 ? {
|
|
6884
|
+
version: ref.version
|
|
6885
|
+
} : {}
|
|
6886
|
+
}));
|
|
6610
6887
|
}
|
|
6611
6888
|
|
|
6612
6889
|
function hashDefinitionContent(def) {
|
|
@@ -6681,284 +6958,66 @@ function parseDefinitionInput(def, caller) {
|
|
|
6681
6958
|
|
|
6682
6959
|
function stripSystemFields(doc) {
|
|
6683
6960
|
const out = {};
|
|
6684
|
-
for (const [k, v2] of Object.entries(doc)) k === "_rev" || k === "_createdAt" || k === "_updatedAt" || (out[k] = v2);
|
|
6685
|
-
return out;
|
|
6686
|
-
}
|
|
6687
|
-
|
|
6688
|
-
function stableStringify(value) {
|
|
6689
|
-
return value === null || typeof value != "object" ? JSON.stringify(value) : Array.isArray(value) ? `[${value.map(stableStringify).join(",")}]` : `{${Object.entries(value).toSorted(([a], [b]) => a.localeCompare(b)).map(([k, v2]) => `${JSON.stringify(k)}:${stableStringify(v2)}`).join(",")}}`;
|
|
6690
|
-
}
|
|
6691
|
-
|
|
6692
|
-
async function loadDefinition({client: client, definition: definition, version: version, tag: tag}) {
|
|
6693
|
-
if (version !== void 0) {
|
|
6694
|
-
const doc = await client.fetch(definitionLookupGroq(!0), {
|
|
6695
|
-
definition: definition,
|
|
6696
|
-
version: version,
|
|
6697
|
-
tag: tag
|
|
6698
|
-
});
|
|
6699
|
-
if (!doc) throw new invariants.DefinitionNotFoundError({
|
|
6700
|
-
definition: definition,
|
|
6701
|
-
version: version
|
|
6702
|
-
});
|
|
6703
|
-
return assertReadableModel(doc);
|
|
6704
|
-
}
|
|
6705
|
-
const latest = await loadLatestDeployed({
|
|
6706
|
-
client: client,
|
|
6707
|
-
definition: definition,
|
|
6708
|
-
tag: tag
|
|
6709
|
-
});
|
|
6710
|
-
if (!latest) throw new invariants.DefinitionNotFoundError({
|
|
6711
|
-
definition: definition
|
|
6712
|
-
});
|
|
6713
|
-
return latest;
|
|
6714
|
-
}
|
|
6715
|
-
|
|
6716
|
-
async function loadLatestDeployed({client: client, definition: definition, tag: tag}) {
|
|
6717
|
-
const doc = await client.fetch(definitionLookupGroq(!1), {
|
|
6718
|
-
definition: definition,
|
|
6719
|
-
tag: tag
|
|
6720
|
-
});
|
|
6721
|
-
return doc ? assertReadableModel(doc) : void 0;
|
|
6722
|
-
}
|
|
6723
|
-
|
|
6724
|
-
async function loadDefinitionVersions({client: client, definition: definition, tag: tag}) {
|
|
6725
|
-
return (await client.fetch(`*[_type == "${invariants.WORKFLOW_DEFINITION_TYPE}" && name == $definition && ${invariants.tagScopeFilter()}] | order(version desc)`, {
|
|
6726
|
-
definition: definition,
|
|
6727
|
-
tag: tag
|
|
6728
|
-
})).map(assertReadableModel);
|
|
6729
|
-
}
|
|
6730
|
-
|
|
6731
|
-
async function loadDefinitionVersionsOrThrow({client: client, definition: definition, tag: tag}) {
|
|
6732
|
-
const versions = await loadDefinitionVersions({
|
|
6733
|
-
client: client,
|
|
6734
|
-
definition: definition,
|
|
6735
|
-
tag: tag
|
|
6736
|
-
});
|
|
6737
|
-
if (versions.length === 0) throw new invariants.DefinitionNotFoundError({
|
|
6738
|
-
definition: definition
|
|
6739
|
-
});
|
|
6740
|
-
return versions;
|
|
6741
|
-
}
|
|
6742
|
-
|
|
6743
|
-
function subjectDenialLabels(denied) {
|
|
6744
|
-
return denied.map(d => `${d.permission} on ${d.subject} (${d.resource})`);
|
|
6745
|
-
}
|
|
6746
|
-
|
|
6747
|
-
class ActionDisabledError extends invariants.WorkflowError {
|
|
6748
|
-
reason;
|
|
6749
|
-
activity;
|
|
6750
|
-
action;
|
|
6751
|
-
constructor(args) {
|
|
6752
|
-
super("action-disabled", formatDisabledReason({
|
|
6753
|
-
activity: args.activity,
|
|
6754
|
-
action: args.action,
|
|
6755
|
-
reason: args.reason
|
|
6756
|
-
})), this.name = "ActionDisabledError", this.reason = args.reason, this.activity = args.activity,
|
|
6757
|
-
this.action = args.action;
|
|
6758
|
-
}
|
|
6759
|
-
}
|
|
6760
|
-
|
|
6761
|
-
const disabledReasonDetail = {
|
|
6762
|
-
"filter-failed": r => `action filter returned false${r.detail ? ` (${r.detail})` : ""}`,
|
|
6763
|
-
"activity-not-active": r => `activity status is "${r.status}"`,
|
|
6764
|
-
"stage-terminal": r => `stage "${r.stage}" is terminal`,
|
|
6765
|
-
"instance-completed": r => `instance completed at ${r.completedAt}`,
|
|
6766
|
-
"instance-aborted": r => `instance aborted at ${r.abortedAt}`,
|
|
6767
|
-
"requirements-unmet": r => `unmet requirement(s): ${r.unmetRequirements.join(", ")}`,
|
|
6768
|
-
"subject-permission-denied": r => `missing subject permission(s): ${subjectDenialLabels(r.denied).join(", ")}`
|
|
6769
|
-
};
|
|
6770
|
-
|
|
6771
|
-
function actionDisabledDetail(reason) {
|
|
6772
|
-
return disabledReasonDetail[reason.kind](reason);
|
|
6773
|
-
}
|
|
6774
|
-
|
|
6775
|
-
function formatDisabledReason({activity: activity, action: action, reason: reason}) {
|
|
6776
|
-
return `Action "${activity}:${action}" is not allowed: ${actionDisabledDetail(reason)}`;
|
|
6777
|
-
}
|
|
6778
|
-
|
|
6779
|
-
class EditFieldDeniedError extends invariants.WorkflowError {
|
|
6780
|
-
reason;
|
|
6781
|
-
target;
|
|
6782
|
-
constructor(args) {
|
|
6783
|
-
super("edit-field-denied", formatEditDisabledReason(args.target, args.reason)),
|
|
6784
|
-
this.name = "EditFieldDeniedError", this.reason = args.reason, this.target = args.target;
|
|
6785
|
-
}
|
|
6786
|
-
}
|
|
6787
|
-
|
|
6788
|
-
const editDisabledReasonDetail = {
|
|
6789
|
-
"not-editable": () => "field is not declared editable",
|
|
6790
|
-
"instance-completed": r => `instance completed at ${r.completedAt}`,
|
|
6791
|
-
"instance-aborted": r => `instance aborted at ${r.abortedAt}`,
|
|
6792
|
-
"edit-window-closed": r => `edit window closed (${r.detail})`,
|
|
6793
|
-
"editor-not-permitted": r => `editor not permitted (${r.predicate})`
|
|
6794
|
-
};
|
|
6795
|
-
|
|
6796
|
-
function formatEditDisabledReason(target, reason) {
|
|
6797
|
-
const detail = editDisabledReasonDetail[reason.kind](reason), where = target.activity !== void 0 ? `${target.activity}.${target.field}` : target.field;
|
|
6798
|
-
return `Field "${target.scope}:${where}" is not editable: ${detail}`;
|
|
6961
|
+
for (const [k, v2] of Object.entries(doc)) k === "_rev" || k === "_createdAt" || k === "_updatedAt" || (out[k] = v2);
|
|
6962
|
+
return out;
|
|
6799
6963
|
}
|
|
6800
6964
|
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
return
|
|
6804
|
-
|
|
6805
|
-
|
|
6806
|
-
options: options,
|
|
6807
|
-
commit: ctx => commitAction({
|
|
6808
|
-
ctx: ctx,
|
|
6809
|
-
activityName: activity,
|
|
6810
|
-
actionName: action,
|
|
6811
|
-
callerParams: params,
|
|
6812
|
-
requestRecord: requestRecord,
|
|
6813
|
-
options: options
|
|
6814
|
-
}),
|
|
6815
|
-
onExhausted: () => new ConcurrentFireActionError({
|
|
6816
|
-
instanceId: instanceId,
|
|
6817
|
-
activity: activity,
|
|
6818
|
-
action: action,
|
|
6819
|
-
attempts: CONCURRENT_FIRE_ACTION_MAX_ATTEMPTS
|
|
6820
|
-
})
|
|
6821
|
-
});
|
|
6965
|
+
function stableStringify(value) {
|
|
6966
|
+
if (value === null || typeof value != "object") return JSON.stringify(value);
|
|
6967
|
+
if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
|
|
6968
|
+
const record = value;
|
|
6969
|
+
return `{${Object.keys(record).toSorted().map(k => `${JSON.stringify(k)}:${stableStringify(record[k])}`).join(",")}}`;
|
|
6822
6970
|
}
|
|
6823
6971
|
|
|
6824
|
-
async function
|
|
6825
|
-
|
|
6826
|
-
|
|
6827
|
-
|
|
6828
|
-
|
|
6829
|
-
|
|
6830
|
-
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
|
|
6834
|
-
ctx: ctx,
|
|
6835
|
-
condition: action.filter,
|
|
6836
|
-
opts: {
|
|
6837
|
-
activityName: activityName,
|
|
6838
|
-
...actor !== void 0 ? {
|
|
6839
|
-
actor: actor
|
|
6840
|
-
} : {},
|
|
6841
|
-
...can !== void 0 ? {
|
|
6842
|
-
vars: {
|
|
6843
|
-
can: can
|
|
6844
|
-
}
|
|
6845
|
-
} : {}
|
|
6846
|
-
}
|
|
6847
|
-
})) throw new ActionDisabledError({
|
|
6848
|
-
activity: activityName,
|
|
6849
|
-
action: actionName,
|
|
6850
|
-
reason: {
|
|
6851
|
-
kind: "filter-failed",
|
|
6852
|
-
filter: action.filter,
|
|
6853
|
-
detail: "commit re-check"
|
|
6854
|
-
}
|
|
6972
|
+
async function loadDefinition({client: client, definition: definition, version: version, tag: tag}) {
|
|
6973
|
+
if (version !== void 0) {
|
|
6974
|
+
const doc = await client.fetch(definitionLookupGroq(!0), {
|
|
6975
|
+
definition: definition,
|
|
6976
|
+
version: version,
|
|
6977
|
+
tag: tag
|
|
6978
|
+
});
|
|
6979
|
+
if (!doc) throw new invariants.DefinitionNotFoundError({
|
|
6980
|
+
definition: definition,
|
|
6981
|
+
version: version
|
|
6855
6982
|
});
|
|
6983
|
+
return assertReadableModel(doc);
|
|
6856
6984
|
}
|
|
6857
|
-
const
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
status: entry?.status
|
|
6985
|
+
const latest = await loadLatestDeployed({
|
|
6986
|
+
client: client,
|
|
6987
|
+
definition: definition,
|
|
6988
|
+
tag: tag
|
|
6862
6989
|
});
|
|
6863
|
-
|
|
6864
|
-
|
|
6865
|
-
activityName: activityName,
|
|
6866
|
-
callerParams: callerParams
|
|
6990
|
+
if (!latest) throw new invariants.DefinitionNotFoundError({
|
|
6991
|
+
definition: definition
|
|
6867
6992
|
});
|
|
6868
|
-
return
|
|
6869
|
-
stage: stage,
|
|
6870
|
-
activity: activity,
|
|
6871
|
-
action: action,
|
|
6872
|
-
params: params
|
|
6873
|
-
};
|
|
6993
|
+
return latest;
|
|
6874
6994
|
}
|
|
6875
6995
|
|
|
6876
|
-
function
|
|
6877
|
-
const
|
|
6878
|
-
|
|
6879
|
-
|
|
6880
|
-
|
|
6881
|
-
|
|
6882
|
-
kind: "activity-not-active",
|
|
6883
|
-
status: status
|
|
6884
|
-
}
|
|
6885
|
-
}) : new Error(`Activity "${activityName}" must be active to fire action "${actionName}"; status is ${status ?? "missing"}`);
|
|
6996
|
+
async function loadLatestDeployed({client: client, definition: definition, tag: tag}) {
|
|
6997
|
+
const doc = await client.fetch(definitionLookupGroq(!1), {
|
|
6998
|
+
definition: definition,
|
|
6999
|
+
tag: tag
|
|
7000
|
+
});
|
|
7001
|
+
return doc ? assertReadableModel(doc) : void 0;
|
|
6886
7002
|
}
|
|
6887
7003
|
|
|
6888
|
-
async function
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
|
|
6892
|
-
|
|
6893
|
-
|
|
6894
|
-
|
|
6895
|
-
|
|
6896
|
-
|
|
6897
|
-
|
|
6898
|
-
|
|
6899
|
-
|
|
6900
|
-
}), mutation = startMutation(ctx.instance);
|
|
6901
|
-
recordProcessedRequest({
|
|
6902
|
-
mutation: mutation,
|
|
6903
|
-
record: requestRecord,
|
|
6904
|
-
now: ctx.now
|
|
7004
|
+
async function loadDefinitionVersions({client: client, definition: definition, tag: tag}) {
|
|
7005
|
+
return (await client.fetch(`*[_type == "${invariants.WORKFLOW_DEFINITION_TYPE}" && name == $definition && ${invariants.tagScopeFilter()}] | order(version desc)`, {
|
|
7006
|
+
definition: definition,
|
|
7007
|
+
tag: tag
|
|
7008
|
+
})).map(assertReadableModel);
|
|
7009
|
+
}
|
|
7010
|
+
|
|
7011
|
+
async function loadDefinitionVersionsOrThrow({client: client, definition: definition, tag: tag}) {
|
|
7012
|
+
const versions = await loadDefinitionVersions({
|
|
7013
|
+
client: client,
|
|
7014
|
+
definition: definition,
|
|
7015
|
+
tag: tag
|
|
6905
7016
|
});
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
_key: randomKey(),
|
|
6909
|
-
_type: "actionFired",
|
|
6910
|
-
at: now,
|
|
6911
|
-
stage: stage.name,
|
|
6912
|
-
activity: activityName,
|
|
6913
|
-
action: actionName,
|
|
6914
|
-
...actor !== void 0 ? {
|
|
6915
|
-
actor: actor,
|
|
6916
|
-
driverKind: driverKind(actor)
|
|
6917
|
-
} : {}
|
|
7017
|
+
if (versions.length === 0) throw new invariants.DefinitionNotFoundError({
|
|
7018
|
+
definition: definition
|
|
6918
7019
|
});
|
|
6919
|
-
|
|
6920
|
-
ops: action.ops,
|
|
6921
|
-
mutation: mutation,
|
|
6922
|
-
stage: stage.name,
|
|
6923
|
-
origin: {
|
|
6924
|
-
activity: activityName,
|
|
6925
|
-
action: actionName
|
|
6926
|
-
},
|
|
6927
|
-
params: params,
|
|
6928
|
-
actor: actor,
|
|
6929
|
-
self: invariants.selfGdr(ctx.instance),
|
|
6930
|
-
now: now,
|
|
6931
|
-
snapshot: ctx.snapshot
|
|
6932
|
-
}), newStatus = mutEntry.status !== statusBefore ? mutEntry.status : void 0;
|
|
6933
|
-
return await queueEffects({
|
|
6934
|
-
ctx: ctx,
|
|
6935
|
-
mutation: mutation,
|
|
6936
|
-
effects: action.effects,
|
|
6937
|
-
origin: {
|
|
6938
|
-
kind: "action",
|
|
6939
|
-
name: actionName
|
|
6940
|
-
},
|
|
6941
|
-
actor: actor,
|
|
6942
|
-
opts: {
|
|
6943
|
-
callerParams: params,
|
|
6944
|
-
activityName: activityName
|
|
6945
|
-
}
|
|
6946
|
-
}), await persistThenMaybeRefresh({
|
|
6947
|
-
ctx: ctx,
|
|
6948
|
-
mutation: mutation,
|
|
6949
|
-
stageName: stage.name,
|
|
6950
|
-
didChangeState: ranOps.some(isFieldOp)
|
|
6951
|
-
}), {
|
|
6952
|
-
fired: !0,
|
|
6953
|
-
activity: activityName,
|
|
6954
|
-
action: actionName,
|
|
6955
|
-
...newStatus !== void 0 ? {
|
|
6956
|
-
newStatus: newStatus
|
|
6957
|
-
} : {},
|
|
6958
|
-
...ranOps.length > 0 ? {
|
|
6959
|
-
ranOps: ranOps
|
|
6960
|
-
} : {}
|
|
6961
|
-
};
|
|
7020
|
+
return versions;
|
|
6962
7021
|
}
|
|
6963
7022
|
|
|
6964
7023
|
async function editField(args) {
|
|
@@ -6978,7 +7037,7 @@ async function editField(args) {
|
|
|
6978
7037
|
onExhausted: () => new ConcurrentEditFieldError({
|
|
6979
7038
|
instanceId: instanceId,
|
|
6980
7039
|
target: labelTarget(target),
|
|
6981
|
-
attempts:
|
|
7040
|
+
attempts: CONCURRENT_COMMIT_MAX_ATTEMPTS
|
|
6982
7041
|
})
|
|
6983
7042
|
});
|
|
6984
7043
|
}
|
|
@@ -7274,7 +7333,7 @@ async function abortAndPropagate(args) {
|
|
|
7274
7333
|
...telemetry !== void 0 ? {
|
|
7275
7334
|
telemetry: telemetry
|
|
7276
7335
|
} : {}
|
|
7277
|
-
}),
|
|
7336
|
+
}), await propagateToAncestors({
|
|
7278
7337
|
client: client,
|
|
7279
7338
|
instanceId: instanceId,
|
|
7280
7339
|
actor: actor,
|
|
@@ -7289,12 +7348,12 @@ async function abortAndPropagate(args) {
|
|
|
7289
7348
|
}), result.fired;
|
|
7290
7349
|
}
|
|
7291
7350
|
|
|
7292
|
-
function
|
|
7293
|
-
return Object.entries(ctx).map(([id, value]) =>
|
|
7351
|
+
function toContextEntries(ctx) {
|
|
7352
|
+
return Object.entries(ctx).map(([id, value]) => toContextSeedEntry(id, value));
|
|
7294
7353
|
}
|
|
7295
7354
|
|
|
7296
|
-
function
|
|
7297
|
-
return typeof value == "string" || typeof value == "number" || typeof value == "boolean" || invariants.isGdr(value) ?
|
|
7355
|
+
function toContextSeedEntry(name, value) {
|
|
7356
|
+
return typeof value == "string" || typeof value == "number" || typeof value == "boolean" || invariants.isGdr(value) ? contextEntry(name, value) : contextJsonEntry(name, value);
|
|
7298
7357
|
}
|
|
7299
7358
|
|
|
7300
7359
|
async function dispatchGatedWrite(args) {
|
|
@@ -7414,12 +7473,7 @@ function buildEngineCallOptions(args) {
|
|
|
7414
7473
|
|
|
7415
7474
|
async function applyAction(args) {
|
|
7416
7475
|
const {client: client, instance: instance, activity: activity, action: action, params: params, requestRecord: requestRecord, ...rest} = args, options = buildEngineCallOptions(rest);
|
|
7417
|
-
return
|
|
7418
|
-
client: client,
|
|
7419
|
-
instanceId: instance._id,
|
|
7420
|
-
activity: activity,
|
|
7421
|
-
options: options
|
|
7422
|
-
}), (await fireAction({
|
|
7476
|
+
return (await fireAction({
|
|
7423
7477
|
client: client,
|
|
7424
7478
|
instanceId: instance._id,
|
|
7425
7479
|
activity: activity,
|
|
@@ -7513,7 +7567,7 @@ async function assertNoSpawnReferrers({client: client, tag: tag, definition: def
|
|
|
7513
7567
|
|
|
7514
7568
|
async function nonTerminalInstanceIds({client: client, tag: tag, definition: definition, version: version}) {
|
|
7515
7569
|
const versionFilter = version === void 0 ? "" : " && pinnedVersion == $version";
|
|
7516
|
-
return (await client.fetch(`*[_type == "${WORKFLOW_INSTANCE_TYPE}" && definition == $definition && ${invariants.tagScopeFilter()} &&
|
|
7570
|
+
return (await client.fetch(`*[_type == "${WORKFLOW_INSTANCE_TYPE}" && definition == $definition && ${invariants.tagScopeFilter()} && ${inFlightFilter()}${versionFilter}] | order(_id asc){_id}`, {
|
|
7517
7571
|
definition: definition,
|
|
7518
7572
|
tag: tag,
|
|
7519
7573
|
...version !== void 0 ? {
|
|
@@ -7541,6 +7595,16 @@ async function abortInstances(args) {
|
|
|
7541
7595
|
return aborted;
|
|
7542
7596
|
}
|
|
7543
7597
|
|
|
7598
|
+
async function fetchStartFilterSlice(args) {
|
|
7599
|
+
const {client: client, tag: tag} = args, {query: query, params: params} = instancesQuery({
|
|
7600
|
+
tag: tag,
|
|
7601
|
+
filter: {
|
|
7602
|
+
includeCompleted: !0
|
|
7603
|
+
}
|
|
7604
|
+
});
|
|
7605
|
+
return (await client.fetch(query, params)).map(assertReadableModel);
|
|
7606
|
+
}
|
|
7607
|
+
|
|
7544
7608
|
const workflow = {
|
|
7545
7609
|
deployDefinitions: async rawArgs => {
|
|
7546
7610
|
const args = taggedScope(rawArgs, REQUEST_TAG.deploy), {client: client, tag: tag, resourceAliases: resourceAliases} = args;
|
|
@@ -7615,16 +7679,22 @@ const workflow = {
|
|
|
7615
7679
|
}), result;
|
|
7616
7680
|
},
|
|
7617
7681
|
startInstance: async rawArgs => {
|
|
7618
|
-
const args = taggedScope(rawArgs, REQUEST_TAG.start), {client: client, tag: tag, workflowResource: workflowResource, definition: definitionName, version: version, initialFields: initialFields, ancestors: ancestors,
|
|
7682
|
+
const args = taggedScope(rawArgs, REQUEST_TAG.start), {client: client, tag: tag, workflowResource: workflowResource, definition: definitionName, version: version, initialFields: initialFields, ancestors: ancestors, context: context, instanceId: instanceId, perspective: perspective, executionContext: executionContext} = args, {actor: actor, clientForGdr: clientForGdr} = await resolveOperationContext(args), clock = args.clock ?? wallClock, seedFields = initialFields ?? [], definition = await loadDefinition({
|
|
7619
7683
|
client: client,
|
|
7620
7684
|
definition: definitionName,
|
|
7621
7685
|
version: version,
|
|
7622
7686
|
tag: tag
|
|
7623
7687
|
}), id = instanceId ?? instanceDocId(tag);
|
|
7624
7688
|
if (definition.stages.find(s => s.name === definition.initialStage) === void 0) throw new Error(`Initial stage "${definition.initialStage}" missing in ${definitionName} v${definition.version}`);
|
|
7625
|
-
const now = clock()
|
|
7689
|
+
const now = clock();
|
|
7690
|
+
assertRequiredInputProvided({
|
|
7626
7691
|
entryDefs: definition.fields ?? [],
|
|
7627
|
-
initialFields:
|
|
7692
|
+
initialFields: seedFields,
|
|
7693
|
+
definitionName: definition.name
|
|
7694
|
+
});
|
|
7695
|
+
const contextEntries = context ? toContextEntries(context) : [], fieldDiscards = [], resolvedFields = await resolveDeclaredFields({
|
|
7696
|
+
entryDefs: definition.fields ?? [],
|
|
7697
|
+
initialFields: seedFields,
|
|
7628
7698
|
ctx: {
|
|
7629
7699
|
client: client,
|
|
7630
7700
|
now: now,
|
|
@@ -7652,7 +7722,7 @@ const workflow = {
|
|
|
7652
7722
|
pinnedContentHash: definition.contentHash,
|
|
7653
7723
|
definition: definition,
|
|
7654
7724
|
fields: resolvedFields,
|
|
7655
|
-
|
|
7725
|
+
context: contextEntries,
|
|
7656
7726
|
ancestors: ancestors ?? [],
|
|
7657
7727
|
perspective: effectivePerspective,
|
|
7658
7728
|
initialStage: definition.initialStage,
|
|
@@ -7662,20 +7732,8 @@ const workflow = {
|
|
|
7662
7732
|
extraHistory: fieldDiscards
|
|
7663
7733
|
} : {}
|
|
7664
7734
|
});
|
|
7665
|
-
await client.create(base, SYNC_COMMIT)
|
|
7666
|
-
|
|
7667
|
-
instanceId: id,
|
|
7668
|
-
actor: actor,
|
|
7669
|
-
clientForGdr: clientForGdr,
|
|
7670
|
-
clock: clock,
|
|
7671
|
-
...executionContext !== void 0 ? {
|
|
7672
|
-
executionContext: executionContext
|
|
7673
|
-
} : {},
|
|
7674
|
-
...args.telemetry !== void 0 ? {
|
|
7675
|
-
telemetry: args.telemetry
|
|
7676
|
-
} : {}
|
|
7677
|
-
});
|
|
7678
|
-
const cascaded = await cascade({
|
|
7735
|
+
await client.create(base, SYNC_COMMIT);
|
|
7736
|
+
const runArgs = {
|
|
7679
7737
|
client: client,
|
|
7680
7738
|
instanceId: id,
|
|
7681
7739
|
actor: actor,
|
|
@@ -7687,11 +7745,13 @@ const workflow = {
|
|
|
7687
7745
|
...args.telemetry !== void 0 ? {
|
|
7688
7746
|
telemetry: args.telemetry
|
|
7689
7747
|
} : {}
|
|
7690
|
-
}
|
|
7748
|
+
};
|
|
7749
|
+
await primeInitialStage(runArgs);
|
|
7750
|
+
const cascaded = await cascade(runArgs);
|
|
7691
7751
|
return resolveTelemetry(args.telemetry).log(WorkflowInstanceStarted, instanceStartedData({
|
|
7692
7752
|
definition: definition,
|
|
7693
7753
|
instanceId: id,
|
|
7694
|
-
initialFieldCount:
|
|
7754
|
+
initialFieldCount: seedFields.length,
|
|
7695
7755
|
viaSpawn: !1
|
|
7696
7756
|
})), {
|
|
7697
7757
|
instance: await reload({
|
|
@@ -8264,12 +8324,21 @@ const workflow = {
|
|
|
8264
8324
|
definitionsForDocument: async rawArgs => {
|
|
8265
8325
|
const args = taggedScope(rawArgs, REQUEST_TAG.definitionsForDocument), {client: client, tag: tag, document: document} = args;
|
|
8266
8326
|
invariants.validateTag(tag);
|
|
8267
|
-
const deployed = (await client.fetch(definitionsListGroq("desc"), {
|
|
8327
|
+
const clock = args.clock ?? wallClock, deployed = (await client.fetch(definitionsListGroq("desc"), {
|
|
8268
8328
|
tag: tag
|
|
8269
8329
|
})).map(assertReadableModel), latest = latestDeployedDefinitions(deployed);
|
|
8330
|
+
let slice;
|
|
8270
8331
|
return applicableDefinitions({
|
|
8271
8332
|
definitions: latest,
|
|
8272
|
-
document: document
|
|
8333
|
+
document: document,
|
|
8334
|
+
scope: {
|
|
8335
|
+
tag: tag,
|
|
8336
|
+
now: clock(),
|
|
8337
|
+
fetchDataset: () => slice ??= fetchStartFilterSlice({
|
|
8338
|
+
client: client,
|
|
8339
|
+
tag: tag
|
|
8340
|
+
})
|
|
8341
|
+
}
|
|
8273
8342
|
});
|
|
8274
8343
|
},
|
|
8275
8344
|
permissions: {
|
|
@@ -9381,6 +9450,24 @@ async function computeDiffEntries({client: client, defs: defs, target: target})
|
|
|
9381
9450
|
return entries;
|
|
9382
9451
|
}
|
|
9383
9452
|
|
|
9453
|
+
function buildInitialFields({declared: declared, values: values}) {
|
|
9454
|
+
const settable = declared.filter(f => f.initialValue?.type === "input"), settableNames = settable.map(f => f.name).join(", ");
|
|
9455
|
+
return Object.entries(values).map(([name, value]) => {
|
|
9456
|
+
const entry = declared.find(f => f.name === name);
|
|
9457
|
+
if (entry === void 0) throw new Error(settable.length > 0 ? `workflow declares no field "${name}" — input fields: ${settableNames}` : `workflow declares no field "${name}" — it declares no input fields at all`);
|
|
9458
|
+
if (entry.initialValue?.type !== "input") throw new Error(`field "${name}" is not input-sourced — the engine would silently ignore the value. ` + (settable.length > 0 ? `Input fields: ${settableNames}` : "This workflow has no input fields."));
|
|
9459
|
+
return {
|
|
9460
|
+
type: entry.type,
|
|
9461
|
+
name: name,
|
|
9462
|
+
value: value
|
|
9463
|
+
};
|
|
9464
|
+
});
|
|
9465
|
+
}
|
|
9466
|
+
|
|
9467
|
+
function startRefusal(definition) {
|
|
9468
|
+
if (!invariants.isStartableDefinition(definition)) return "definition is spawn-only (lifecycle: 'child') — instances of it are spawned by a parent workflow's activity, not started standalone";
|
|
9469
|
+
}
|
|
9470
|
+
|
|
9384
9471
|
const HISTORY_DISPLAY = {
|
|
9385
9472
|
stageEntered: {
|
|
9386
9473
|
title: "Stage entered",
|
|
@@ -9390,17 +9477,13 @@ const HISTORY_DISPLAY = {
|
|
|
9390
9477
|
title: "Stage exited",
|
|
9391
9478
|
description: "The instance left a stage on the way to the next one."
|
|
9392
9479
|
},
|
|
9393
|
-
activityActivated: {
|
|
9394
|
-
title: "Activity activated",
|
|
9395
|
-
description: "An activity flipped from `pending` to `active` and its onEnter effects queued."
|
|
9396
|
-
},
|
|
9397
9480
|
activityStatusChanged: {
|
|
9398
9481
|
title: "Activity status changed",
|
|
9399
9482
|
description: "An action or op flipped an activity's status (active → done / skipped / failed)."
|
|
9400
9483
|
},
|
|
9401
9484
|
actionFired: {
|
|
9402
9485
|
title: "Action fired",
|
|
9403
|
-
description: "An action
|
|
9486
|
+
description: "An action fired against an activity — via fireAction, or cascade-fired when its `when` turned true (`triggered`) — ops ran, status may have flipped, effects queued, spawn reconciled."
|
|
9404
9487
|
},
|
|
9405
9488
|
transitionFired: {
|
|
9406
9489
|
title: "Transition fired",
|
|
@@ -9412,7 +9495,7 @@ const HISTORY_DISPLAY = {
|
|
|
9412
9495
|
},
|
|
9413
9496
|
effectCompleted: {
|
|
9414
9497
|
title: "Effect settled",
|
|
9415
|
-
description: "A queued effect settled: a drainer ran its handler to completion (done or failed, outputs
|
|
9498
|
+
description: "A queued effect settled: a drainer ran its handler to completion (done or failed, outputs recorded on the run row and readable as $effects), or an abort cancelled it before dispatch."
|
|
9416
9499
|
},
|
|
9417
9500
|
effectClaimReleased: {
|
|
9418
9501
|
title: "Stale claim released",
|
|
@@ -9420,7 +9503,7 @@ const HISTORY_DISPLAY = {
|
|
|
9420
9503
|
},
|
|
9421
9504
|
spawned: {
|
|
9422
9505
|
title: "Spawned child",
|
|
9423
|
-
description: "An
|
|
9506
|
+
description: "An action's `spawn` declaration spawned a child workflow instance."
|
|
9424
9507
|
},
|
|
9425
9508
|
subworkflowAdopted: {
|
|
9426
9509
|
title: "Child adopted",
|
|
@@ -9446,6 +9529,23 @@ const HISTORY_DISPLAY = {
|
|
|
9446
9529
|
title: "Query result discarded",
|
|
9447
9530
|
description: "A query-sourced field's GROQ result didn't fit the field's declared kind, so it was discarded and the field fell back to its default."
|
|
9448
9531
|
}
|
|
9532
|
+
}, EXECUTOR_CLASSIFICATION_DISPLAY = {
|
|
9533
|
+
autonomous: {
|
|
9534
|
+
title: "Autonomous",
|
|
9535
|
+
description: "Every action is cascade-fired — the engine advances it; no caller is ever needed."
|
|
9536
|
+
},
|
|
9537
|
+
interactive: {
|
|
9538
|
+
title: "Interactive",
|
|
9539
|
+
description: "Only fireAction-fired actions — a caller (editor, robot, agent) must act."
|
|
9540
|
+
},
|
|
9541
|
+
"off-system": {
|
|
9542
|
+
title: "Off-system",
|
|
9543
|
+
description: "The work happens outside the system (the `target` deep-link); someone records it here."
|
|
9544
|
+
},
|
|
9545
|
+
hybrid: {
|
|
9546
|
+
title: "Hybrid",
|
|
9547
|
+
description: "A mix of caller-fired actions and cascade-fired triggers."
|
|
9548
|
+
}
|
|
9449
9549
|
}, FIELD_KIND_DISPLAY = {
|
|
9450
9550
|
"doc.ref": {
|
|
9451
9551
|
title: "Document reference",
|
|
@@ -9522,7 +9622,7 @@ const HISTORY_DISPLAY = {
|
|
|
9522
9622
|
},
|
|
9523
9623
|
"field.updateWhere": {
|
|
9524
9624
|
title: "Update matching rows",
|
|
9525
|
-
description: "Merge fields into rows of an array
|
|
9625
|
+
description: "Merge declared sub-fields into rows of an `array` entry that match the `where` condition (`array` only — `doc.refs`/`assignees` rows are engine-shaped)."
|
|
9526
9626
|
},
|
|
9527
9627
|
"field.removeWhere": {
|
|
9528
9628
|
title: "Remove matching rows",
|
|
@@ -9532,26 +9632,26 @@ const HISTORY_DISPLAY = {
|
|
|
9532
9632
|
title: "Set activity status",
|
|
9533
9633
|
description: "Flip an activity's status explicitly (the action-level `status:` sugar desugars to this)."
|
|
9534
9634
|
}
|
|
9535
|
-
},
|
|
9536
|
-
"
|
|
9635
|
+
}, CONTEXT_ENTRY_DISPLAY = {
|
|
9636
|
+
"context.string": {
|
|
9537
9637
|
title: "String",
|
|
9538
|
-
description: "
|
|
9638
|
+
description: "Start-seeded string value, read as `$context.<name>`."
|
|
9539
9639
|
},
|
|
9540
|
-
"
|
|
9640
|
+
"context.number": {
|
|
9541
9641
|
title: "Number",
|
|
9542
|
-
description: "
|
|
9642
|
+
description: "Start-seeded numeric value, read as `$context.<name>`."
|
|
9543
9643
|
},
|
|
9544
|
-
"
|
|
9644
|
+
"context.boolean": {
|
|
9545
9645
|
title: "Boolean",
|
|
9546
|
-
description: "
|
|
9646
|
+
description: "Start-seeded boolean value, read as `$context.<name>`."
|
|
9547
9647
|
},
|
|
9548
|
-
"
|
|
9648
|
+
"context.ref": {
|
|
9549
9649
|
title: "GDR reference",
|
|
9550
|
-
description: "
|
|
9650
|
+
description: "Start-seeded GDR pointer, read as `$context.<name>`."
|
|
9551
9651
|
},
|
|
9552
|
-
"
|
|
9652
|
+
"context.json": {
|
|
9553
9653
|
title: "JSON",
|
|
9554
|
-
description: "
|
|
9654
|
+
description: "Start-seeded JSON payload, read as `$context.<name>`."
|
|
9555
9655
|
}
|
|
9556
9656
|
}, AUTHORING_DISPLAY = {
|
|
9557
9657
|
pendingEffect: {
|
|
@@ -9581,7 +9681,7 @@ const HISTORY_DISPLAY = {
|
|
|
9581
9681
|
},
|
|
9582
9682
|
script: {
|
|
9583
9683
|
title: "Script activity",
|
|
9584
|
-
description: "The engine runs a step inline and resolves
|
|
9684
|
+
description: "The engine runs a step inline and resolves it in the same cascade hop — an audit row."
|
|
9585
9685
|
},
|
|
9586
9686
|
manual: {
|
|
9587
9687
|
title: "Manual activity",
|
|
@@ -9621,7 +9721,7 @@ const HISTORY_DISPLAY = {
|
|
|
9621
9721
|
...HISTORY_DISPLAY,
|
|
9622
9722
|
...FIELD_KIND_DISPLAY,
|
|
9623
9723
|
...OP_DISPLAY,
|
|
9624
|
-
...
|
|
9724
|
+
...CONTEXT_ENTRY_DISPLAY,
|
|
9625
9725
|
...AUTHORING_DISPLAY
|
|
9626
9726
|
};
|
|
9627
9727
|
|
|
@@ -9641,12 +9741,16 @@ exports.CONDITION_VARS = invariants.CONDITION_VARS;
|
|
|
9641
9741
|
|
|
9642
9742
|
exports.ContractViolationError = invariants.ContractViolationError;
|
|
9643
9743
|
|
|
9744
|
+
exports.DEFAULT_TRANSITION_WHEN = invariants.DEFAULT_TRANSITION_WHEN;
|
|
9745
|
+
|
|
9644
9746
|
exports.DRIVER_KINDS = invariants.DRIVER_KINDS;
|
|
9645
9747
|
|
|
9646
9748
|
exports.DefinitionInUseError = invariants.DefinitionInUseError;
|
|
9647
9749
|
|
|
9648
9750
|
exports.DefinitionNotFoundError = invariants.DefinitionNotFoundError;
|
|
9649
9751
|
|
|
9752
|
+
exports.EXECUTOR_CLASSIFICATIONS = invariants.EXECUTOR_CLASSIFICATIONS;
|
|
9753
|
+
|
|
9650
9754
|
exports.EffectNotFoundError = invariants.EffectNotFoundError;
|
|
9651
9755
|
|
|
9652
9756
|
exports.FILTER_SCOPE_VARS = invariants.FILTER_SCOPE_VARS;
|
|
@@ -9661,14 +9765,24 @@ exports.InstanceNotFoundError = invariants.InstanceNotFoundError;
|
|
|
9661
9765
|
|
|
9662
9766
|
exports.RESERVED_CONDITION_VARS = invariants.RESERVED_CONDITION_VARS;
|
|
9663
9767
|
|
|
9768
|
+
exports.START_FILTER_VARS = invariants.START_FILTER_VARS;
|
|
9769
|
+
|
|
9664
9770
|
exports.WORKFLOW_DEFINITION_TYPE = invariants.WORKFLOW_DEFINITION_TYPE;
|
|
9665
9771
|
|
|
9666
9772
|
exports.WorkflowError = invariants.WorkflowError;
|
|
9667
9773
|
|
|
9668
9774
|
exports.clientConfigFromResource = invariants.clientConfigFromResource;
|
|
9669
9775
|
|
|
9776
|
+
exports.conditionFieldReadNames = invariants.conditionFieldReadNames;
|
|
9777
|
+
|
|
9670
9778
|
exports.datasetResourceParts = invariants.datasetResourceParts;
|
|
9671
9779
|
|
|
9780
|
+
exports.deriveActivityKind = invariants.deriveActivityKind;
|
|
9781
|
+
|
|
9782
|
+
exports.deriveExecutorClassification = invariants.deriveExecutorClassification;
|
|
9783
|
+
|
|
9784
|
+
exports.driverKind = invariants.driverKind;
|
|
9785
|
+
|
|
9672
9786
|
exports.errorMessage = invariants.errorMessage;
|
|
9673
9787
|
|
|
9674
9788
|
exports.extractDocumentId = invariants.extractDocumentId;
|
|
@@ -9681,12 +9795,16 @@ exports.gdrUri = invariants.gdrUri;
|
|
|
9681
9795
|
|
|
9682
9796
|
exports.groupMembershipNames = invariants.groupMembershipNames;
|
|
9683
9797
|
|
|
9798
|
+
exports.isCascadeFired = invariants.isCascadeFired;
|
|
9799
|
+
|
|
9684
9800
|
exports.isGdr = invariants.isGdr;
|
|
9685
9801
|
|
|
9686
9802
|
exports.isNotesEntry = invariants.isNotesEntry;
|
|
9687
9803
|
|
|
9688
9804
|
exports.isStartableDefinition = invariants.isStartableDefinition;
|
|
9689
9805
|
|
|
9806
|
+
exports.isSubjectEntry = invariants.isSubjectEntry;
|
|
9807
|
+
|
|
9690
9808
|
exports.isTerminalActivityStatus = invariants.isTerminalActivityStatus;
|
|
9691
9809
|
|
|
9692
9810
|
exports.isTodoListEntry = invariants.isTodoListEntry;
|
|
@@ -9697,6 +9815,8 @@ exports.parseGdr = invariants.parseGdr;
|
|
|
9697
9815
|
|
|
9698
9816
|
exports.parseResourceGdr = invariants.parseResourceGdr;
|
|
9699
9817
|
|
|
9818
|
+
exports.readsRootDocument = invariants.readsRootDocument;
|
|
9819
|
+
|
|
9700
9820
|
exports.refCanvas = invariants.refCanvas;
|
|
9701
9821
|
|
|
9702
9822
|
exports.refDashboard = invariants.refDashboard;
|
|
@@ -9717,6 +9837,8 @@ exports.resourceFromParsed = invariants.resourceFromParsed;
|
|
|
9717
9837
|
|
|
9718
9838
|
exports.resourceGdr = invariants.resourceGdr;
|
|
9719
9839
|
|
|
9840
|
+
exports.startKindOf = invariants.startKindOf;
|
|
9841
|
+
|
|
9720
9842
|
exports.tagScopeFilter = invariants.tagScopeFilter;
|
|
9721
9843
|
|
|
9722
9844
|
exports.toBareId = invariants.toBareId;
|
|
@@ -9810,8 +9932,12 @@ exports.ActionDisabledError = ActionDisabledError;
|
|
|
9810
9932
|
|
|
9811
9933
|
exports.ActionParamsInvalidError = ActionParamsInvalidError;
|
|
9812
9934
|
|
|
9935
|
+
exports.CONTEXT_ENTRY_DISPLAY = CONTEXT_ENTRY_DISPLAY;
|
|
9936
|
+
|
|
9813
9937
|
exports.CascadeLimitError = CascadeLimitError;
|
|
9814
9938
|
|
|
9939
|
+
exports.ConcurrentCompleteEffectError = ConcurrentCompleteEffectError;
|
|
9940
|
+
|
|
9815
9941
|
exports.ConcurrentEditFieldError = ConcurrentEditFieldError;
|
|
9816
9942
|
|
|
9817
9943
|
exports.ConcurrentFireActionError = ConcurrentFireActionError;
|
|
@@ -9830,12 +9956,12 @@ exports.DISPLAY = DISPLAY;
|
|
|
9830
9956
|
|
|
9831
9957
|
exports.DRIVER_KIND_DISPLAY = DRIVER_KIND_DISPLAY;
|
|
9832
9958
|
|
|
9833
|
-
exports.EFFECTS_CONTEXT_DISPLAY = EFFECTS_CONTEXT_DISPLAY;
|
|
9834
|
-
|
|
9835
9959
|
exports.ENGINE_API_VERSION = ENGINE_API_VERSION;
|
|
9836
9960
|
|
|
9837
9961
|
exports.EXECUTION_KINDS = EXECUTION_KINDS;
|
|
9838
9962
|
|
|
9963
|
+
exports.EXECUTOR_CLASSIFICATION_DISPLAY = EXECUTOR_CLASSIFICATION_DISPLAY;
|
|
9964
|
+
|
|
9839
9965
|
exports.EditFieldDeniedError = EditFieldDeniedError;
|
|
9840
9966
|
|
|
9841
9967
|
exports.EffectOpsInvalidError = EffectOpsInvalidError;
|
|
@@ -9900,9 +10026,9 @@ exports.aclPathForResource = aclPathForResource;
|
|
|
9900
10026
|
|
|
9901
10027
|
exports.actionDisabledDetail = actionDisabledDetail;
|
|
9902
10028
|
|
|
9903
|
-
exports.
|
|
10029
|
+
exports.actionRendering = actionRendering;
|
|
9904
10030
|
|
|
9905
|
-
exports.
|
|
10031
|
+
exports.actionVerdict = actionVerdict;
|
|
9906
10032
|
|
|
9907
10033
|
exports.applicableDefinitions = applicableDefinitions;
|
|
9908
10034
|
|
|
@@ -9910,6 +10036,8 @@ exports.assertReadableModel = assertReadableModel;
|
|
|
9910
10036
|
|
|
9911
10037
|
exports.availableActions = availableActions;
|
|
9912
10038
|
|
|
10039
|
+
exports.buildInitialFields = buildInitialFields;
|
|
10040
|
+
|
|
9913
10041
|
exports.buildSnapshot = buildSnapshot;
|
|
9914
10042
|
|
|
9915
10043
|
exports.checklistLines = checklistLines;
|
|
@@ -9920,8 +10048,14 @@ exports.computeDiffEntries = computeDiffEntries;
|
|
|
9920
10048
|
|
|
9921
10049
|
exports.conditionSitesOf = conditionSitesOf;
|
|
9922
10050
|
|
|
10051
|
+
exports.contentDocQuery = contentDocQuery;
|
|
10052
|
+
|
|
10053
|
+
exports.contentDraftFallback = contentDraftFallback;
|
|
10054
|
+
|
|
9923
10055
|
exports.contentReleaseName = contentReleaseName;
|
|
9924
10056
|
|
|
10057
|
+
exports.contextMap = contextMap;
|
|
10058
|
+
|
|
9925
10059
|
exports.createEngine = createEngine;
|
|
9926
10060
|
|
|
9927
10061
|
exports.createTelemetryIntake = createTelemetryIntake;
|
|
@@ -9942,8 +10076,6 @@ exports.denyingGuards = denyingGuards;
|
|
|
9942
10076
|
|
|
9943
10077
|
exports.deployStageGuards = deployStageGuards;
|
|
9944
10078
|
|
|
9945
|
-
exports.deriveActivityKind = deriveActivityKind;
|
|
9946
|
-
|
|
9947
10079
|
exports.describeAtom = describeAtom;
|
|
9948
10080
|
|
|
9949
10081
|
exports.describeCondition = describeCondition;
|
|
@@ -9970,14 +10102,16 @@ exports.displayTitle = displayTitle;
|
|
|
9970
10102
|
|
|
9971
10103
|
exports.documentActionDenials = documentActionDenials;
|
|
9972
10104
|
|
|
9973
|
-
exports.
|
|
10105
|
+
exports.documentPrefilter = documentPrefilter;
|
|
9974
10106
|
|
|
9975
|
-
exports.
|
|
10107
|
+
exports.effectOutputsMap = effectOutputsMap;
|
|
9976
10108
|
|
|
9977
10109
|
exports.evaluateFromSnapshot = evaluateFromSnapshot;
|
|
9978
10110
|
|
|
9979
10111
|
exports.evaluateMutationGuard = evaluateMutationGuard;
|
|
9980
10112
|
|
|
10113
|
+
exports.evaluateStartFilter = evaluateStartFilter;
|
|
10114
|
+
|
|
9981
10115
|
exports.fieldTreeShape = fieldTreeShape;
|
|
9982
10116
|
|
|
9983
10117
|
exports.findCurrentActivityEntry = findCurrentActivityEntry;
|
|
@@ -9996,6 +10130,8 @@ exports.guardsForResource = guardsForResource;
|
|
|
9996
10130
|
|
|
9997
10131
|
exports.hashDefinitionContent = hashDefinitionContent;
|
|
9998
10132
|
|
|
10133
|
+
exports.inFlightFilter = inFlightFilter;
|
|
10134
|
+
|
|
9999
10135
|
exports.instanceGuardQuery = instanceGuardQuery;
|
|
10000
10136
|
|
|
10001
10137
|
exports.instanceWatchesDocument = instanceWatchesDocument;
|
|
@@ -10006,6 +10142,8 @@ exports.isClaimExpired = isClaimExpired;
|
|
|
10006
10142
|
|
|
10007
10143
|
exports.isDefinitionApplicable = isDefinitionApplicable;
|
|
10008
10144
|
|
|
10145
|
+
exports.isFilterScopedOut = isFilterScopedOut;
|
|
10146
|
+
|
|
10009
10147
|
exports.isGuardLifted = isGuardLifted;
|
|
10010
10148
|
|
|
10011
10149
|
exports.isTelemetryEnvDenied = isTelemetryEnvDenied;
|
|
@@ -10028,10 +10166,14 @@ exports.noopTelemetry = noopTelemetry;
|
|
|
10028
10166
|
|
|
10029
10167
|
exports.parentRef = parentRef;
|
|
10030
10168
|
|
|
10169
|
+
exports.parseDefinitionInput = parseDefinitionInput;
|
|
10170
|
+
|
|
10031
10171
|
exports.parseDefinitionSnapshot = parseDefinitionSnapshot;
|
|
10032
10172
|
|
|
10033
10173
|
exports.processShellUserProperties = processShellUserProperties;
|
|
10034
10174
|
|
|
10175
|
+
exports.projectToWatchRef = projectToWatchRef;
|
|
10176
|
+
|
|
10035
10177
|
exports.readsRaw = readsRaw;
|
|
10036
10178
|
|
|
10037
10179
|
exports.remediationsFor = remediationsFor;
|
|
@@ -10044,6 +10186,8 @@ exports.retractStageGuards = retractStageGuards;
|
|
|
10044
10186
|
|
|
10045
10187
|
exports.silentLogger = silentLogger;
|
|
10046
10188
|
|
|
10189
|
+
exports.startRefusal = startRefusal;
|
|
10190
|
+
|
|
10047
10191
|
exports.stripSystemFields = stripSystemFields;
|
|
10048
10192
|
|
|
10049
10193
|
exports.subjectDenialLabels = subjectDenialLabels;
|