@sanity/workflow-engine 0.32.0 → 0.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +193 -0
- package/DATAMODEL.md +144 -8
- package/README.md +2 -2
- package/dist/_chunks-cjs/invariants.cjs +2017 -1891
- package/dist/_chunks-es/invariants.js +2047 -1923
- package/dist/define.cjs +6 -3
- package/dist/define.d.cts +431 -255
- package/dist/define.d.ts +431 -255
- package/dist/define.js +7 -4
- package/dist/index.cjs +745 -210
- package/dist/index.d.cts +845 -429
- package/dist/index.d.ts +845 -429
- package/dist/index.js +737 -214
- package/package.json +4 -3
package/dist/index.cjs
CHANGED
|
@@ -81,6 +81,10 @@ function effectiveEditable(baseline, override) {
|
|
|
81
81
|
|
|
82
82
|
const DEFAULT_EDIT_MODE = "set";
|
|
83
83
|
|
|
84
|
+
function _fieldTargetLabel(target) {
|
|
85
|
+
return [ target.scope, target.activity, target.field ].filter(Boolean).join(".");
|
|
86
|
+
}
|
|
87
|
+
|
|
84
88
|
function fieldSites(definition, stage) {
|
|
85
89
|
const sites = [];
|
|
86
90
|
for (const entry of definition.fields ?? []) sites.push({
|
|
@@ -1057,6 +1061,7 @@ function describeSiteHeading(address, ctx) {
|
|
|
1057
1061
|
}
|
|
1058
1062
|
|
|
1059
1063
|
case "action":
|
|
1064
|
+
case "action-when":
|
|
1060
1065
|
{
|
|
1061
1066
|
const title = actionTitle(address, ctx), activity = activityTitle(address.activity, ctx);
|
|
1062
1067
|
return groqConditionDescribe.phrase(id, {
|
|
@@ -1064,7 +1069,7 @@ function describeSiteHeading(address, ctx) {
|
|
|
1064
1069
|
title: title,
|
|
1065
1070
|
activity: activity
|
|
1066
1071
|
},
|
|
1067
|
-
text: `action ${groqConditionDescribe.quoted(title)} on ${groqConditionDescribe.quoted(activity)} is available when:`
|
|
1072
|
+
text: `action ${groqConditionDescribe.quoted(title)} on ${groqConditionDescribe.quoted(activity)} ${address.kind === "action" ? "is available" : "fires automatically"} when:`
|
|
1068
1073
|
});
|
|
1069
1074
|
}
|
|
1070
1075
|
|
|
@@ -1091,18 +1096,6 @@ function describeSiteHeading(address, ctx) {
|
|
|
1091
1096
|
});
|
|
1092
1097
|
}
|
|
1093
1098
|
|
|
1094
|
-
case "action-when":
|
|
1095
|
-
{
|
|
1096
|
-
const title = actionTitle(address, ctx), activity = activityTitle(address.activity, ctx);
|
|
1097
|
-
return groqConditionDescribe.phrase(id, {
|
|
1098
|
-
params: {
|
|
1099
|
-
title: title,
|
|
1100
|
-
activity: activity
|
|
1101
|
-
},
|
|
1102
|
-
text: `action ${groqConditionDescribe.quoted(title)} on ${groqConditionDescribe.quoted(activity)} fires automatically when:`
|
|
1103
|
-
});
|
|
1104
|
-
}
|
|
1105
|
-
|
|
1106
1099
|
case "editable-field":
|
|
1107
1100
|
{
|
|
1108
1101
|
const field = fieldTitleOf({
|
|
@@ -1146,6 +1139,7 @@ function describeSite(site, ctx) {
|
|
|
1146
1139
|
}
|
|
1147
1140
|
|
|
1148
1141
|
case "action":
|
|
1142
|
+
case "action-when":
|
|
1149
1143
|
{
|
|
1150
1144
|
const title = actionTitle(site, ctx), activity = activityTitle(site.activity, ctx);
|
|
1151
1145
|
return groqConditionDescribe.phrase(id, {
|
|
@@ -1153,7 +1147,7 @@ function describeSite(site, ctx) {
|
|
|
1153
1147
|
title: title,
|
|
1154
1148
|
activity: activity
|
|
1155
1149
|
},
|
|
1156
|
-
text: `the ${groqConditionDescribe.quoted(title)} action on ${groqConditionDescribe.quoted(activity)}`
|
|
1150
|
+
text: `the ${groqConditionDescribe.quoted(title)} ${site.kind === "action" ? "action" : "trigger"} on ${groqConditionDescribe.quoted(activity)}`
|
|
1157
1151
|
});
|
|
1158
1152
|
}
|
|
1159
1153
|
|
|
@@ -1169,18 +1163,6 @@ function describeSite(site, ctx) {
|
|
|
1169
1163
|
});
|
|
1170
1164
|
}
|
|
1171
1165
|
|
|
1172
|
-
case "action-when":
|
|
1173
|
-
{
|
|
1174
|
-
const title = actionTitle(site, ctx), activity = activityTitle(site.activity, ctx);
|
|
1175
|
-
return groqConditionDescribe.phrase(id, {
|
|
1176
|
-
params: {
|
|
1177
|
-
title: title,
|
|
1178
|
-
activity: activity
|
|
1179
|
-
},
|
|
1180
|
-
text: `the ${groqConditionDescribe.quoted(title)} trigger on ${groqConditionDescribe.quoted(activity)}`
|
|
1181
|
-
});
|
|
1182
|
-
}
|
|
1183
|
-
|
|
1184
1166
|
case "activity-filter":
|
|
1185
1167
|
{
|
|
1186
1168
|
const activity = activityTitle(site.activity, ctx);
|
|
@@ -1238,10 +1220,10 @@ function unsatisfiedTransitionSummaries(evaluation) {
|
|
|
1238
1220
|
definition: evaluation.definition
|
|
1239
1221
|
};
|
|
1240
1222
|
return evaluation.currentStage.transitions.filter(t => !t.whenSatisfied).flatMap(t => {
|
|
1241
|
-
const
|
|
1242
|
-
return
|
|
1223
|
+
const summary2 = describeCondition(t.insight, ctx).summary;
|
|
1224
|
+
return summary2 !== void 0 ? [ {
|
|
1243
1225
|
transition: t.transition.name,
|
|
1244
|
-
summary:
|
|
1226
|
+
summary: summary2
|
|
1245
1227
|
} ] : [];
|
|
1246
1228
|
});
|
|
1247
1229
|
}
|
|
@@ -2872,8 +2854,8 @@ class CascadeLimitError extends invariants.WorkflowError {
|
|
|
2872
2854
|
|
|
2873
2855
|
const FIELD_OP_TYPES = [ "field.set", "field.setIfMissing", "field.unset", "field.append", "field.inc", "field.dec", "field.updateWhere", "field.removeWhere" ];
|
|
2874
2856
|
|
|
2875
|
-
function isFieldOp(
|
|
2876
|
-
return FIELD_OP_TYPES.includes(
|
|
2857
|
+
function isFieldOp(summary2) {
|
|
2858
|
+
return FIELD_OP_TYPES.includes(summary2.opType);
|
|
2877
2859
|
}
|
|
2878
2860
|
|
|
2879
2861
|
function validateActionParams({action: action, activityName: activityName, callerParams: callerParams}) {
|
|
@@ -2952,7 +2934,7 @@ async function runOps(args) {
|
|
|
2952
2934
|
if (ops === void 0 || ops.length === 0) return [];
|
|
2953
2935
|
const opsFromDefinition = origin.effect === void 0 && origin.edit !== !0, summaries = [];
|
|
2954
2936
|
for (const op of ops) {
|
|
2955
|
-
const
|
|
2937
|
+
const summary2 = await applyOp(op, {
|
|
2956
2938
|
mutation: mutation,
|
|
2957
2939
|
stage: stage,
|
|
2958
2940
|
activityName: origin.activity,
|
|
@@ -2966,9 +2948,9 @@ async function runOps(args) {
|
|
|
2966
2948
|
memberRoles: args.memberRoles,
|
|
2967
2949
|
roleAliases: args.roleAliases
|
|
2968
2950
|
});
|
|
2969
|
-
summaries.push(
|
|
2951
|
+
summaries.push(summary2), shouldRecordOpApplied(summary2) && mutation.history.push(opAppliedEntry({
|
|
2970
2952
|
origin: origin,
|
|
2971
|
-
summary:
|
|
2953
|
+
summary: summary2,
|
|
2972
2954
|
stage: stage,
|
|
2973
2955
|
actor: actor,
|
|
2974
2956
|
now: now
|
|
@@ -2977,12 +2959,12 @@ async function runOps(args) {
|
|
|
2977
2959
|
return summaries;
|
|
2978
2960
|
}
|
|
2979
2961
|
|
|
2980
|
-
function shouldRecordOpApplied(
|
|
2981
|
-
return
|
|
2962
|
+
function shouldRecordOpApplied(summary2) {
|
|
2963
|
+
return summary2.opType !== "field.setIfMissing" || summary2.resolved !== void 0;
|
|
2982
2964
|
}
|
|
2983
2965
|
|
|
2984
2966
|
function opAppliedEntry(args) {
|
|
2985
|
-
const {origin: origin, summary:
|
|
2967
|
+
const {origin: origin, summary: summary2, stage: stage, actor: actor, now: now} = args;
|
|
2986
2968
|
return {
|
|
2987
2969
|
_key: randomKey(),
|
|
2988
2970
|
_type: "opApplied",
|
|
@@ -3000,12 +2982,12 @@ function opAppliedEntry(args) {
|
|
|
3000
2982
|
...origin.effect !== void 0 ? {
|
|
3001
2983
|
effect: origin.effect
|
|
3002
2984
|
} : {},
|
|
3003
|
-
opType:
|
|
3004
|
-
...
|
|
3005
|
-
target:
|
|
2985
|
+
opType: summary2.opType,
|
|
2986
|
+
...summary2.target !== void 0 ? {
|
|
2987
|
+
target: summary2.target
|
|
3006
2988
|
} : {},
|
|
3007
|
-
...
|
|
3008
|
-
resolved:
|
|
2989
|
+
...summary2.resolved !== void 0 ? {
|
|
2990
|
+
resolved: summary2.resolved
|
|
3009
2991
|
} : {},
|
|
3010
2992
|
...actor !== void 0 ? {
|
|
3011
2993
|
actor: actor
|
|
@@ -3782,7 +3764,13 @@ function siblingConfig(target) {
|
|
|
3782
3764
|
};
|
|
3783
3765
|
}
|
|
3784
3766
|
|
|
3785
|
-
const wallClock = () => /* @__PURE__ */ (new Date).toISOString()
|
|
3767
|
+
const wallClock = () => /* @__PURE__ */ (new Date).toISOString(), tick = ms => new Promise(resolve => {
|
|
3768
|
+
setTimeout(resolve, ms);
|
|
3769
|
+
}), wallSleeper = async ms => {
|
|
3770
|
+
let left = ms;
|
|
3771
|
+
for (;left > 2147483647; ) await tick(2147483647), left -= 2147483647;
|
|
3772
|
+
await tick(left);
|
|
3773
|
+
};
|
|
3786
3774
|
|
|
3787
3775
|
function addMs(instant, ms) {
|
|
3788
3776
|
return new Date(Date.parse(instant) + ms).toISOString();
|
|
@@ -3792,7 +3780,7 @@ function hasPassed(instant, now) {
|
|
|
3792
3780
|
return Date.parse(instant) <= Date.parse(now);
|
|
3793
3781
|
}
|
|
3794
3782
|
|
|
3795
|
-
const DATA_MODEL_VERSION =
|
|
3783
|
+
const DATA_MODEL_VERSION = 10, DATA_MODEL_MIN_READER = 4, DATA_MODEL_MAX_READER = 10, READER_MODEL_ROLLOUT_URL = "https://www.sanity.io/docs/workflows/prerelease";
|
|
3796
3784
|
|
|
3797
3785
|
class ReaderModelAcknowledgementError extends invariants.WorkflowError {
|
|
3798
3786
|
code="WORKFLOW_READER_MODEL_ACKNOWLEDGEMENT_MISMATCH";
|
|
@@ -3948,6 +3936,22 @@ const DATA_MODEL_CHANGES = Object.freeze([ Object.freeze({
|
|
|
3948
3936
|
compatibility: "reader-floor",
|
|
3949
3937
|
applicability: "detectable",
|
|
3950
3938
|
summary: "Singular assignee fields use member lists with at most one user and any number of roles."
|
|
3939
|
+
}), Object.freeze({
|
|
3940
|
+
id: "required-content-references",
|
|
3941
|
+
introducedInModel: 10,
|
|
3942
|
+
minReaderModel: 10,
|
|
3943
|
+
documentTypes: Object.freeze([ "definition", "instance" ]),
|
|
3944
|
+
compatibility: "reader-floor",
|
|
3945
|
+
applicability: "detectable",
|
|
3946
|
+
summary: "Required content references must remain readable after initialization before actions or transitions advance."
|
|
3947
|
+
}), Object.freeze({
|
|
3948
|
+
id: "effect-retry-policy",
|
|
3949
|
+
introducedInModel: 10,
|
|
3950
|
+
minReaderModel: 10,
|
|
3951
|
+
documentTypes: Object.freeze([ "definition", "instance" ]),
|
|
3952
|
+
compatibility: "reader-floor",
|
|
3953
|
+
applicability: "detectable",
|
|
3954
|
+
summary: "An effect node declares a bounded retry policy the engine enforces on every runtime."
|
|
3951
3955
|
}) ]);
|
|
3952
3956
|
|
|
3953
3957
|
function recordOf(value) {
|
|
@@ -4079,6 +4083,10 @@ function hasSingularAssigneeLists(document) {
|
|
|
4079
4083
|
return typeof snapshot?.modelVersion == "number" && isAssignmentListModel(snapshot.modelVersion) && persistedFieldEntries(document).some(entry => entryHasKind(entry, "assignee"));
|
|
4080
4084
|
}
|
|
4081
4085
|
|
|
4086
|
+
function hasEffectRetryPolicy(document) {
|
|
4087
|
+
return persistedDefinitionTree(document)?.actions.some(action => recordsAt(action, "effects").some(effect => recordOf(effect.retry) !== void 0)) ?? !1;
|
|
4088
|
+
}
|
|
4089
|
+
|
|
4082
4090
|
function isMutationGuardAction(action) {
|
|
4083
4091
|
return typeof action == "string" && invariants.MUTATION_GUARD_ACTIONS.some(candidate => candidate === action);
|
|
4084
4092
|
}
|
|
@@ -4090,6 +4098,10 @@ function hasSplitGuardIdSpaces(document) {
|
|
|
4090
4098
|
})) ?? !1;
|
|
4091
4099
|
}
|
|
4092
4100
|
|
|
4101
|
+
function hasRequiredContentReferences(document) {
|
|
4102
|
+
return persistedDefinitionTree(document)?.roots.some(root => recordsAt(root, "fields").some(field => field.required === !0 && typeof field.type == "string" && invariants.refKindAcceptsTypes(field.type))) ?? !1;
|
|
4103
|
+
}
|
|
4104
|
+
|
|
4093
4105
|
const featureDetectors = {
|
|
4094
4106
|
"governed-model-stamps": () => !0,
|
|
4095
4107
|
"subject-field-kind": document => hasFieldKind(document, "subject"),
|
|
@@ -4104,6 +4116,8 @@ const featureDetectors = {
|
|
|
4104
4116
|
"role-constrained-assignment-fields": hasRoleConstrainedAssignments,
|
|
4105
4117
|
"singular-assignee-lists": hasSingularAssigneeLists,
|
|
4106
4118
|
"split-guard-id-spaces": hasSplitGuardIdSpaces,
|
|
4119
|
+
"required-content-references": hasRequiredContentReferences,
|
|
4120
|
+
"effect-retry-policy": hasEffectRetryPolicy,
|
|
4107
4121
|
"node-semantics": hasNodeSemantics,
|
|
4108
4122
|
"field-patch-ops": document => hasPersistedOpType(document, [ "field.inc", "field.dec", "field.setIfMissing" ]),
|
|
4109
4123
|
"attributes-condition-var": () => !0
|
|
@@ -4340,9 +4354,13 @@ function abortReason(instance) {
|
|
|
4340
4354
|
}
|
|
4341
4355
|
|
|
4342
4356
|
function diagnoseInputFromEvaluation(evaluation) {
|
|
4357
|
+
if (evaluation.missingDocuments?.length && evaluation.blockingMissingDocuments === void 0) throw new Error("Missing blocking document evidence in workflow evaluation");
|
|
4343
4358
|
const stage = openStage(evaluation.instance);
|
|
4344
4359
|
return {
|
|
4345
4360
|
instance: evaluation.instance,
|
|
4361
|
+
...evaluation.missingDocuments !== void 0 ? {
|
|
4362
|
+
missingDocuments: evaluation.blockingMissingDocuments ?? []
|
|
4363
|
+
} : {},
|
|
4346
4364
|
activities: evaluation.currentStage.activities,
|
|
4347
4365
|
transitions: evaluation.currentStage.transitions,
|
|
4348
4366
|
assignees: Object.fromEntries(evaluation.currentStage.activities.map(t => [ t.activity.name, assigneesOf(stage, t.activity.name) ]))
|
|
@@ -4461,7 +4479,7 @@ function diagnoseInstance(input) {
|
|
|
4461
4479
|
at: instance.completedAt,
|
|
4462
4480
|
...liveChildrenField(instance)
|
|
4463
4481
|
};
|
|
4464
|
-
const cause = stuckFromDocumentState(input) ?? noTransitionFiresCause(input);
|
|
4482
|
+
const cause = stuckFromDocumentState(input) ?? missingDocumentCause(input.missingDocuments) ?? noTransitionFiresCause(input);
|
|
4465
4483
|
return cause !== void 0 ? {
|
|
4466
4484
|
state: "stuck",
|
|
4467
4485
|
cause: cause
|
|
@@ -4470,10 +4488,27 @@ function diagnoseInstance(input) {
|
|
|
4470
4488
|
};
|
|
4471
4489
|
}
|
|
4472
4490
|
|
|
4491
|
+
function missingDocumentCause(documents = []) {
|
|
4492
|
+
return documents.length > 0 ? {
|
|
4493
|
+
kind: "document-missing",
|
|
4494
|
+
documents: documents
|
|
4495
|
+
} : void 0;
|
|
4496
|
+
}
|
|
4497
|
+
|
|
4498
|
+
function _additionalMissingDocuments(diagnosis, documents) {
|
|
4499
|
+
return diagnosis.state === "stuck" && diagnosis.cause.kind !== "document-missing" ? documents ?? [] : [];
|
|
4500
|
+
}
|
|
4501
|
+
|
|
4473
4502
|
const RUNNABLE_VERBS = /* @__PURE__ */ new Set([ "set-stage", "abort", "drain-effects", "reset-activity" ]);
|
|
4474
4503
|
|
|
4475
4504
|
function remediationsForCause(cause) {
|
|
4476
4505
|
switch (cause.kind) {
|
|
4506
|
+
case "document-missing":
|
|
4507
|
+
return [ {
|
|
4508
|
+
verb: "abort",
|
|
4509
|
+
rationale: "Required content cannot be read. Investigate the referenced documents, or abort the instance if it is no longer needed."
|
|
4510
|
+
} ];
|
|
4511
|
+
|
|
4477
4512
|
case "failed-effect":
|
|
4478
4513
|
return [ {
|
|
4479
4514
|
verb: "retry-effect",
|
|
@@ -4568,12 +4603,12 @@ function joinCacheFor(client) {
|
|
|
4568
4603
|
}
|
|
4569
4604
|
|
|
4570
4605
|
function isClientProjectMember(value) {
|
|
4571
|
-
return invariants.isRecord(value) && typeof value.id == "string";
|
|
4606
|
+
return invariants.isRecord(value) && typeof value.id == "string" && (value.isRobot === void 0 || typeof value.isRobot == "boolean");
|
|
4572
4607
|
}
|
|
4573
4608
|
|
|
4574
|
-
async function
|
|
4609
|
+
async function _requestProjectMembers(request, projectId) {
|
|
4575
4610
|
const project = await request({
|
|
4576
|
-
|
|
4611
|
+
url: `/projects/${encodeURIComponent(projectId)}`
|
|
4577
4612
|
}), members = invariants.isRecord(project) ? project.members : void 0;
|
|
4578
4613
|
if (!Array.isArray(members) || !members.every(isClientProjectMember)) throw new Error(`Project member directory response is invalid for project "${projectId}"`);
|
|
4579
4614
|
return members;
|
|
@@ -4581,7 +4616,7 @@ async function requestProjectMembers(request, projectId) {
|
|
|
4581
4616
|
|
|
4582
4617
|
async function requestProjectUsers(args) {
|
|
4583
4618
|
const {request: request, projectId: projectId, memberIds: memberIds} = args, response = await request({
|
|
4584
|
-
|
|
4619
|
+
url: `/projects/${encodeURIComponent(projectId)}/users/${memberIds.map(encodeURIComponent).join(",")}`
|
|
4585
4620
|
}), rows = Array.isArray(response) ? response : [ response ];
|
|
4586
4621
|
if (!rows.every(isClientProjectUser)) throw new Error(`Project user directory response is invalid for project "${projectId}"`);
|
|
4587
4622
|
const requested = new Set(memberIds);
|
|
@@ -4589,7 +4624,7 @@ async function requestProjectUsers(args) {
|
|
|
4589
4624
|
}
|
|
4590
4625
|
|
|
4591
4626
|
async function requestProjectMemberDirectory(args) {
|
|
4592
|
-
const {request: request, projectId: projectId} = args, members = await
|
|
4627
|
+
const {request: request, projectId: projectId} = args, members = await _requestProjectMembers(request, projectId), memberIds = members.map(member => member.id).filter(id => invariants.classifyPrincipalId(id).namespace === "project");
|
|
4593
4628
|
if (memberIds.length === 0) return {
|
|
4594
4629
|
members: members,
|
|
4595
4630
|
globalUsers: []
|
|
@@ -4664,7 +4699,7 @@ function clientProjectUserDirectory(client, projectId) {
|
|
|
4664
4699
|
});
|
|
4665
4700
|
try {
|
|
4666
4701
|
const response = await client.request({
|
|
4667
|
-
|
|
4702
|
+
url: `/projects/${encodeURIComponent(projectId)}/users/${encodeURIComponent(id)}`
|
|
4668
4703
|
}), candidate = Array.isArray(response) ? response[0] : response;
|
|
4669
4704
|
return candidate == null ? {
|
|
4670
4705
|
status: "missing"
|
|
@@ -4776,8 +4811,30 @@ function collectWatchRefs(instance) {
|
|
|
4776
4811
|
}
|
|
4777
4812
|
|
|
4778
4813
|
function collectSubjectRefs(instance) {
|
|
4779
|
-
const
|
|
4780
|
-
return [ ...entryDocRefs(
|
|
4814
|
+
const entries = activeFieldEntries(instance).map(({entry: entry}) => entry);
|
|
4815
|
+
return [ ...entryDocRefs(entries), ...entryReleaseRefs(entries) ];
|
|
4816
|
+
}
|
|
4817
|
+
|
|
4818
|
+
function activeFieldEntries(instance) {
|
|
4819
|
+
const stage = invariants.findOpenStageEntry(instance);
|
|
4820
|
+
return [ ...scopedEntries(instance.fields, {
|
|
4821
|
+
scope: "workflow"
|
|
4822
|
+
}), ...scopedEntries(stage?.fields, {
|
|
4823
|
+
scope: "stage"
|
|
4824
|
+
}), ...(stage?.activities ?? []).flatMap(activity => scopedEntries(activity.fields, {
|
|
4825
|
+
scope: "activity",
|
|
4826
|
+
activity: activity.name
|
|
4827
|
+
})) ];
|
|
4828
|
+
}
|
|
4829
|
+
|
|
4830
|
+
function scopedEntries(entries, scope) {
|
|
4831
|
+
return (entries ?? []).map(entry => ({
|
|
4832
|
+
target: {
|
|
4833
|
+
...scope,
|
|
4834
|
+
field: entry.name
|
|
4835
|
+
},
|
|
4836
|
+
entry: entry
|
|
4837
|
+
}));
|
|
4781
4838
|
}
|
|
4782
4839
|
|
|
4783
4840
|
function foreignSubjectRefs(instance) {
|
|
@@ -4878,6 +4935,10 @@ function entrySubjectRefs(entries) {
|
|
|
4878
4935
|
return fieldEntries(entries).flatMap(entry => (typeof entry._type == "string" && invariants.isSingleDocRefKind(entry._type) || entry._type === "release.ref") && invariants.isGdr(entry.value) ? [ entry.value ] : []);
|
|
4879
4936
|
}
|
|
4880
4937
|
|
|
4938
|
+
function missingEntryDocRefs(entries, knownIds) {
|
|
4939
|
+
return entryDocRefs(entries).filter(reference => !knownIds.has(reference.id));
|
|
4940
|
+
}
|
|
4941
|
+
|
|
4881
4942
|
function fieldEntries(entries) {
|
|
4882
4943
|
return Array.isArray(entries) ? entries.filter(entry => !!entry && typeof entry == "object") : [];
|
|
4883
4944
|
}
|
|
@@ -6148,7 +6209,7 @@ async function resolveThroughDirectory(args) {
|
|
|
6148
6209
|
if (safeMisses.length === 0 || client.request === void 0) return out;
|
|
6149
6210
|
try {
|
|
6150
6211
|
const response = await client.request({
|
|
6151
|
-
|
|
6212
|
+
url: `/projects/${encodeURIComponent(projectId)}/users/${safeMisses.map(encodeURIComponent).join(",")}`,
|
|
6152
6213
|
tag: REQUEST_TAG.accessResolveActor
|
|
6153
6214
|
});
|
|
6154
6215
|
ingestDirectoryRows({
|
|
@@ -6348,6 +6409,12 @@ function memoizedBy(compute) {
|
|
|
6348
6409
|
};
|
|
6349
6410
|
}
|
|
6350
6411
|
|
|
6412
|
+
function chunks(values, size) {
|
|
6413
|
+
return Array.from({
|
|
6414
|
+
length: Math.ceil(values.length / size)
|
|
6415
|
+
}, (_, index) => values.slice(index * size, (index + 1) * size));
|
|
6416
|
+
}
|
|
6417
|
+
|
|
6351
6418
|
async function hydrateSnapshot(args) {
|
|
6352
6419
|
const {client: client, clientForGdr: clientForGdr, instance: instance, overlay: overlay} = args, loaded = [], visited = /* @__PURE__ */ new Set;
|
|
6353
6420
|
loaded.push({
|
|
@@ -6458,12 +6525,6 @@ function groupReads(reads) {
|
|
|
6458
6525
|
return [ ...byClient.values() ].flatMap(groups => [ ...groups.values() ]);
|
|
6459
6526
|
}
|
|
6460
6527
|
|
|
6461
|
-
function chunks(values, size) {
|
|
6462
|
-
return Array.from({
|
|
6463
|
-
length: Math.ceil(values.length / size)
|
|
6464
|
-
}, (_, index) => values.slice(index * size, (index + 1) * size));
|
|
6465
|
-
}
|
|
6466
|
-
|
|
6467
6528
|
function collectEntryDocUris(resolvedFieldEntries) {
|
|
6468
6529
|
return entryDocRefs(resolvedFieldEntries).map(ref => ref.id);
|
|
6469
6530
|
}
|
|
@@ -7683,6 +7744,29 @@ async function refreshStageGuards({ctx: ctx, mutation: mutation, stageName: stag
|
|
|
7683
7744
|
});
|
|
7684
7745
|
}
|
|
7685
7746
|
|
|
7747
|
+
function missingRequiredDocuments({instance: instance, definition: definition, snapshot: snapshot}) {
|
|
7748
|
+
return (definition.fields ?? []).filter(field => field.required === !0 && invariants.refKindAcceptsTypes(field.type)).flatMap(field => {
|
|
7749
|
+
const entry = instance.fields.find(candidate => candidate.name === field.name && candidate._type === field.type);
|
|
7750
|
+
return missingEntryDocRefs(entry === void 0 ? [] : [ entry ], snapshot.knownIds).map(reference => ({
|
|
7751
|
+
target: {
|
|
7752
|
+
scope: "workflow",
|
|
7753
|
+
field: field.name
|
|
7754
|
+
},
|
|
7755
|
+
reference: reference
|
|
7756
|
+
}));
|
|
7757
|
+
});
|
|
7758
|
+
}
|
|
7759
|
+
|
|
7760
|
+
function requiredDocumentRequirements(args) {
|
|
7761
|
+
return [ ...new Set(missingRequiredDocuments(args).map(({target: target}) => target.field)) ].map(field => {
|
|
7762
|
+
const title = args.definition.fields?.find(entry => entry.name === field)?.title ?? field;
|
|
7763
|
+
return {
|
|
7764
|
+
name: `field:${field}`,
|
|
7765
|
+
title: `Documents selected for “${title}” must be readable`
|
|
7766
|
+
};
|
|
7767
|
+
});
|
|
7768
|
+
}
|
|
7769
|
+
|
|
7686
7770
|
async function discoverItems(args) {
|
|
7687
7771
|
const {client: client, groq: groq, params: params, perspective: perspective} = args;
|
|
7688
7772
|
return client.fetch(groq, paramsForLake(params), {
|
|
@@ -8259,7 +8343,7 @@ async function persistStageMove({ctx: ctx, mutation: mutation, exitedStage: exit
|
|
|
8259
8343
|
}
|
|
8260
8344
|
|
|
8261
8345
|
async function pickTransition(ctx, stage) {
|
|
8262
|
-
for (const candidate of stage.transitions ?? []) {
|
|
8346
|
+
if (!(missingRequiredDocuments(ctx).length > 0)) for (const candidate of stage.transitions ?? []) {
|
|
8263
8347
|
const outcome = await ctxEvaluateConditionOutcome({
|
|
8264
8348
|
ctx: ctx,
|
|
8265
8349
|
condition: candidate.when
|
|
@@ -8361,7 +8445,7 @@ const actorCache = /* @__PURE__ */ new WeakMap, grantsCache = /* @__PURE__ */ ne
|
|
|
8361
8445
|
|
|
8362
8446
|
async function resolveAccess(taggedClient, args = {}) {
|
|
8363
8447
|
const client = unwrapRequestTag(taggedClient), requestFn = lazyRequest(client);
|
|
8364
|
-
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({
|
|
8448
|
+
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({url: '/users/me'})`. Supply a real `@sanity/client` configured with a token (the test bench serves these endpoints per registered token).");
|
|
8365
8449
|
const grantsPromise = args.grantsFromPath !== void 0 ? cachedGrants({
|
|
8366
8450
|
client: client,
|
|
8367
8451
|
requestFn: requestFn,
|
|
@@ -8430,20 +8514,32 @@ function cachedByKey(args) {
|
|
|
8430
8514
|
}
|
|
8431
8515
|
|
|
8432
8516
|
async function fetchActor(client, requestFn) {
|
|
8433
|
-
const
|
|
8517
|
+
const resourceUser = await fetchCurrentUser(requestFn, "the workflow resource host"), resourceId = usableId(resourceUser), resourceGlobalId = invariants.firstCarriedGlobalId([ resourceId ]) ?? invariants.directoryBridgeId(resourceUser?.sanityUserId);
|
|
8518
|
+
if (resourceGlobalId !== void 0) return identityFromUsers({
|
|
8519
|
+
id: resourceGlobalId,
|
|
8520
|
+
resourceUser: resourceUser
|
|
8521
|
+
});
|
|
8522
|
+
const globalUser = await fetchGlobalUser(client), globalHostId = usableId(globalUser.user), carriedId = invariants.firstCarriedGlobalId([ globalHostId ]), sessionId = carriedId ?? globalHostId ?? resourceId;
|
|
8434
8523
|
if (sessionId === void 0) return;
|
|
8435
8524
|
const bridge = carriedId === void 0 ? await bridgeProjectPrincipal({
|
|
8436
8525
|
client: client,
|
|
8437
8526
|
sessionId: sessionId,
|
|
8438
8527
|
resourceId: resourceId
|
|
8439
8528
|
}) : NO_BRIDGE, id = bridge.status === "resolved" ? bridge.globalId : sessionId;
|
|
8440
|
-
refuseProjectScopedActor({
|
|
8529
|
+
return refuseProjectScopedActor({
|
|
8441
8530
|
id: id,
|
|
8442
8531
|
globalUser: globalUser,
|
|
8443
8532
|
globalHostId: globalHostId,
|
|
8444
8533
|
bridgeFailure: bridge.status === "unavailable" ? bridge : void 0
|
|
8534
|
+
}), identityFromUsers({
|
|
8535
|
+
id: id,
|
|
8536
|
+
resourceUser: resourceUser,
|
|
8537
|
+
globalUser: globalUser.user
|
|
8445
8538
|
});
|
|
8446
|
-
|
|
8539
|
+
}
|
|
8540
|
+
|
|
8541
|
+
function identityFromUsers(args) {
|
|
8542
|
+
const {id: id, resourceUser: resourceUser, globalUser: globalUser} = args, resourceId = usableId(resourceUser), roleNames = roleNamesFor(resourceUser, globalUser);
|
|
8447
8543
|
return {
|
|
8448
8544
|
actor: {
|
|
8449
8545
|
kind: "person",
|
|
@@ -8459,7 +8555,7 @@ async function fetchActor(client, requestFn) {
|
|
|
8459
8555
|
}
|
|
8460
8556
|
|
|
8461
8557
|
function roleNamesFor(resourceUser, globalUser) {
|
|
8462
|
-
return (resourceUser?.roles?.length ? resourceUser : globalUser
|
|
8558
|
+
return (resourceUser?.roles?.length ? resourceUser : globalUser)?.roles?.map(r => r.name).filter(n => !!n) ?? [];
|
|
8463
8559
|
}
|
|
8464
8560
|
|
|
8465
8561
|
const NO_BRIDGE = {
|
|
@@ -8523,11 +8619,11 @@ function usableId(user) {
|
|
|
8523
8619
|
async function fetchCurrentUser(requestFn, hostDescription) {
|
|
8524
8620
|
try {
|
|
8525
8621
|
return await requestFn({
|
|
8526
|
-
|
|
8622
|
+
url: "/users/me",
|
|
8527
8623
|
tag: REQUEST_TAG.accessResolveActor
|
|
8528
8624
|
});
|
|
8529
8625
|
} catch (err) {
|
|
8530
|
-
throw new Error(`workflow: /users/me request against ${hostDescription} failed. The engine resolves the actor from the client's token via \`client.request({
|
|
8626
|
+
throw new Error(`workflow: /users/me request against ${hostDescription} failed. The engine resolves the actor from the client's token via \`client.request({url: '/users/me'})\`. Check the token/connectivity.`, {
|
|
8531
8627
|
cause: err
|
|
8532
8628
|
});
|
|
8533
8629
|
}
|
|
@@ -8544,7 +8640,7 @@ async function fetchGlobalUser(client) {
|
|
|
8544
8640
|
};
|
|
8545
8641
|
try {
|
|
8546
8642
|
const user = await sibling.request({
|
|
8547
|
-
|
|
8643
|
+
url: "/users/me",
|
|
8548
8644
|
tag: REQUEST_TAG.accessResolveActor
|
|
8549
8645
|
}), id = usableId(user);
|
|
8550
8646
|
return sibling.requireGlobalPrincipal && id !== void 0 && invariants.classifyPrincipalId(id).namespace === "project" ? {
|
|
@@ -8667,7 +8763,7 @@ async function resolveOrganizationId(args) {
|
|
|
8667
8763
|
load: async () => {
|
|
8668
8764
|
try {
|
|
8669
8765
|
const project = await requestFn({
|
|
8670
|
-
|
|
8766
|
+
url: `/projects/${encodeURIComponent(projectId)}`,
|
|
8671
8767
|
tag: REQUEST_TAG.accessResolveOrg
|
|
8672
8768
|
});
|
|
8673
8769
|
if (!invariants.isRecord(project)) return;
|
|
@@ -8692,15 +8788,17 @@ function httpStatusOf(err) {
|
|
|
8692
8788
|
return typeof status == "number" ? status : void 0;
|
|
8693
8789
|
}
|
|
8694
8790
|
|
|
8791
|
+
function attributesRequestOf(client) {
|
|
8792
|
+
const version = client.config?.().apiVersion, request = typeof client.withConfig == "function" || version === void 0 || version === ENGINE_API_VERSION ? lazyRequest(pinApiVersion(client)) : void 0;
|
|
8793
|
+
return request === void 0 && console.warn("workflow: skipped org attributes fetch; the client cannot issue API-version-pinned requests. Advisory $attributes stays undefined (conditions referencing it fail closed)."),
|
|
8794
|
+
request;
|
|
8795
|
+
}
|
|
8796
|
+
|
|
8695
8797
|
async function fetchAttributesCached(args) {
|
|
8696
|
-
const {client: client, orgId: orgId} = args,
|
|
8697
|
-
if (
|
|
8698
|
-
|
|
8699
|
-
|
|
8700
|
-
}
|
|
8701
|
-
try {
|
|
8702
|
-
const response = await sibling.request({
|
|
8703
|
-
uri: `/organizations/${encodeURIComponent(orgId)}/users/me/attributes?limit=${ATTRIBUTES_FETCH_LIMIT}`,
|
|
8798
|
+
const {client: client, orgId: orgId} = args, request = attributesRequestOf(client);
|
|
8799
|
+
if (request !== void 0) try {
|
|
8800
|
+
const response = await request({
|
|
8801
|
+
url: `/organizations/${encodeURIComponent(orgId)}/users/me/attributes?limit=${ATTRIBUTES_FETCH_LIMIT}`,
|
|
8704
8802
|
tag: REQUEST_TAG.accessAttributes
|
|
8705
8803
|
}), envelope = invariants.isRecord(response) ? response : void 0;
|
|
8706
8804
|
if (envelope?.hasMore === !0) {
|
|
@@ -8814,6 +8912,15 @@ async function resolveActionCommit({ctx: ctx, activityName: activityName, action
|
|
|
8814
8912
|
actionName: actionName,
|
|
8815
8913
|
status: entry?.status
|
|
8816
8914
|
});
|
|
8915
|
+
const unmetRequirements = requiredDocumentRequirements(ctx);
|
|
8916
|
+
if (unmetRequirements.length > 0) throw new ActionDisabledError({
|
|
8917
|
+
activity: activityName,
|
|
8918
|
+
action: actionName,
|
|
8919
|
+
reason: {
|
|
8920
|
+
kind: "requirements-unmet",
|
|
8921
|
+
unmetRequirements: unmetRequirements
|
|
8922
|
+
}
|
|
8923
|
+
});
|
|
8817
8924
|
const params = validateActionParams({
|
|
8818
8925
|
action: action,
|
|
8819
8926
|
activityName: activityName,
|
|
@@ -8970,13 +9077,13 @@ async function runTriggeredActions({ctx: ctx, mutation: mutation, stage: stage})
|
|
|
8970
9077
|
async function runActivityTriggers({ctx: ctx, mutation: mutation, activity: activity}) {
|
|
8971
9078
|
let fires = 0, ranFieldOps = !1;
|
|
8972
9079
|
for (const action of activity.actions ?? []) {
|
|
8973
|
-
if (!invariants.isCascadeFired(action)) continue;
|
|
8974
9080
|
const entry = currentActivities(mutation).find(e => e.name === activity.name);
|
|
8975
9081
|
if (entry === void 0 || entry.status !== "active") break;
|
|
8976
|
-
if (
|
|
9082
|
+
if (!await triggerIsLive({
|
|
8977
9083
|
ctx: ctx,
|
|
8978
9084
|
activity: activity,
|
|
8979
|
-
action: action
|
|
9085
|
+
action: action,
|
|
9086
|
+
entry: entry
|
|
8980
9087
|
})) continue;
|
|
8981
9088
|
const result = await fireTriggeredAction({
|
|
8982
9089
|
ctx: ctx,
|
|
@@ -8993,8 +9100,12 @@ async function runActivityTriggers({ctx: ctx, mutation: mutation, activity: acti
|
|
|
8993
9100
|
};
|
|
8994
9101
|
}
|
|
8995
9102
|
|
|
8996
|
-
|
|
8997
|
-
|
|
9103
|
+
function isUnfiredTrigger(action, entry) {
|
|
9104
|
+
return invariants.isCascadeFired(action) && !(entry.firedActions ?? []).includes(action.name);
|
|
9105
|
+
}
|
|
9106
|
+
|
|
9107
|
+
async function triggerIsLive({ctx: ctx, activity: activity, action: action, entry: entry}) {
|
|
9108
|
+
if (!isUnfiredTrigger(action, entry) || missingRequiredDocuments(ctx).length > 0 || !tokenMayExecute({
|
|
8998
9109
|
actor: ctx.actor,
|
|
8999
9110
|
action: action,
|
|
9000
9111
|
ctx: ctx
|
|
@@ -10098,7 +10209,7 @@ function staleClaimReason(args) {
|
|
|
10098
10209
|
if (isClaimExpired(claim, args.now)) return "lease-expired";
|
|
10099
10210
|
}
|
|
10100
10211
|
|
|
10101
|
-
function renewClaimLease(args) {
|
|
10212
|
+
function renewClaimLease$1(args) {
|
|
10102
10213
|
const {mutation: mutation, effectKey: effectKey, now: now, leaseMs: leaseMs} = args;
|
|
10103
10214
|
mutation.pendingEffects = mutation.pendingEffects.map(entry => entry._key === effectKey && entry.claim !== void 0 ? {
|
|
10104
10215
|
...entry,
|
|
@@ -10135,7 +10246,7 @@ async function commitReport({ctx: ctx, effectKey: effectKey, claimToken: claimTo
|
|
|
10135
10246
|
mutation: mutation,
|
|
10136
10247
|
record: requestRecord,
|
|
10137
10248
|
now: ctx.now
|
|
10138
|
-
}), renewClaimLease({
|
|
10249
|
+
}), renewClaimLease$1({
|
|
10139
10250
|
mutation: mutation,
|
|
10140
10251
|
effectKey: effectKey,
|
|
10141
10252
|
now: ctx.now,
|
|
@@ -10224,6 +10335,100 @@ async function commitResetActivity({ctx: ctx, activity: activity, to: to, reques
|
|
|
10224
10335
|
};
|
|
10225
10336
|
}
|
|
10226
10337
|
|
|
10338
|
+
const AvailabilityResponse = v__namespace.object({
|
|
10339
|
+
documents: v__namespace.array(v__namespace.object({
|
|
10340
|
+
_id: v__namespace.string()
|
|
10341
|
+
})),
|
|
10342
|
+
omitted: v__namespace.array(v__namespace.object({
|
|
10343
|
+
id: v__namespace.string(),
|
|
10344
|
+
reason: v__namespace.picklist([ "existence", "permission" ])
|
|
10345
|
+
}))
|
|
10346
|
+
}), AVAILABILITY_BATCH_SIZE = 100, AVAILABILITY_CONCURRENCY = 4, pendingReads = /* @__PURE__ */ new WeakMap;
|
|
10347
|
+
|
|
10348
|
+
function groupsFor(documents, clientForGdr) {
|
|
10349
|
+
const groups = /* @__PURE__ */ new Map;
|
|
10350
|
+
for (const {reference: reference} of documents) {
|
|
10351
|
+
const parsed = invariants.parseGdr(reference.id), resource = invariants.resourceFromParsed(parsed), key = invariants.resourceGdr(resource), group = groups.get(key) ?? {
|
|
10352
|
+
client: clientForGdr(parsed),
|
|
10353
|
+
resource: resource,
|
|
10354
|
+
documents: /* @__PURE__ */ new Map
|
|
10355
|
+
};
|
|
10356
|
+
group.documents.set(reference.id, parsed.documentId), groups.set(key, group);
|
|
10357
|
+
}
|
|
10358
|
+
return [ ...groups.values() ];
|
|
10359
|
+
}
|
|
10360
|
+
|
|
10361
|
+
async function readGroup(group) {
|
|
10362
|
+
let response;
|
|
10363
|
+
try {
|
|
10364
|
+
const url = documentUrl(group);
|
|
10365
|
+
url !== void 0 && group.client.request !== void 0 ? response = await readResponse(group.client, url) : logUnavailable({
|
|
10366
|
+
resource: group.resource,
|
|
10367
|
+
reason: "unsupported-client"
|
|
10368
|
+
});
|
|
10369
|
+
} catch (error) {
|
|
10370
|
+
logUnavailable({
|
|
10371
|
+
resource: group.resource,
|
|
10372
|
+
reason: v__namespace.isValiError(error) ? "invalid-response" : "request-failed",
|
|
10373
|
+
error: error
|
|
10374
|
+
});
|
|
10375
|
+
}
|
|
10376
|
+
return [ ...group.documents ].map(([uri, id]) => [ uri, availabilityOf(response, id) ]);
|
|
10377
|
+
}
|
|
10378
|
+
|
|
10379
|
+
function logUnavailable({resource: resource, reason: reason, error: error}) {
|
|
10380
|
+
const statusCode = typeof error == "object" && error !== null && "statusCode" in error ? error.statusCode : void 0;
|
|
10381
|
+
console.warn("[workflows] Document availability could not be confirmed.", {
|
|
10382
|
+
resource: invariants.resourceGdr(resource),
|
|
10383
|
+
reason: reason,
|
|
10384
|
+
...typeof statusCode == "number" ? {
|
|
10385
|
+
statusCode: statusCode
|
|
10386
|
+
} : {}
|
|
10387
|
+
});
|
|
10388
|
+
}
|
|
10389
|
+
|
|
10390
|
+
function availabilityOf(response, id) {
|
|
10391
|
+
if (response === void 0) return "unreadable";
|
|
10392
|
+
const omissions = response.omitted.filter(entry => idUtils.getPublishedId(idUtils.DocumentId(entry.id)) === id);
|
|
10393
|
+
return omissions.some(entry => entry.reason === "permission") ? "unreadable" : response.documents.some(doc => idUtils.getPublishedId(idUtils.DocumentId(doc._id)) === id) ? "outside-perspective" : omissions.some(entry => entry.id === id && entry.reason === "existence") ? "deleted" : "unreadable";
|
|
10394
|
+
}
|
|
10395
|
+
|
|
10396
|
+
function documentUrl(group) {
|
|
10397
|
+
const ids = [ ...new Set(group.documents.values()) ].sort().map(encodeURIComponent).join(",");
|
|
10398
|
+
let url;
|
|
10399
|
+
if ("getDataUrl" in group.client && group.client.getDataUrl !== void 0) url = group.client.getDataUrl("doc", ids); else {
|
|
10400
|
+
if (group.resource.type !== "dataset") return;
|
|
10401
|
+
const {dataset: dataset} = invariants.datasetResourceParts(group.resource.id);
|
|
10402
|
+
url = `/data/doc/${dataset}/${ids}`;
|
|
10403
|
+
}
|
|
10404
|
+
return `${url}?includeAllVersions=true`;
|
|
10405
|
+
}
|
|
10406
|
+
|
|
10407
|
+
function readResponse(client, url) {
|
|
10408
|
+
const identity = unwrapRequestTag(client), pending = pendingReads.get(identity) ?? /* @__PURE__ */ new Map;
|
|
10409
|
+
pendingReads.set(identity, pending);
|
|
10410
|
+
const existing = pending.get(url);
|
|
10411
|
+
if (existing !== void 0) return existing;
|
|
10412
|
+
const result = client.request({
|
|
10413
|
+
url: url
|
|
10414
|
+
}).then(response => v__namespace.parse(AvailabilityResponse, response)).finally(() => pending.delete(url));
|
|
10415
|
+
return pending.set(url, result), result;
|
|
10416
|
+
}
|
|
10417
|
+
|
|
10418
|
+
function documentAvailabilityReader(args) {
|
|
10419
|
+
return async documents => {
|
|
10420
|
+
const result = /* @__PURE__ */ new Map, groups = groupsFor(documents, args.clientForGdr).flatMap(group => chunks([ ...group.documents ], AVAILABILITY_BATCH_SIZE).map(entries => ({
|
|
10421
|
+
...group,
|
|
10422
|
+
documents: new Map(entries)
|
|
10423
|
+
})));
|
|
10424
|
+
for (const wave of chunks(groups, AVAILABILITY_CONCURRENCY)) {
|
|
10425
|
+
const rows = await Promise.all(wave.map(readGroup));
|
|
10426
|
+
for (const [uri, availability] of rows.flat()) result.set(uri, availability);
|
|
10427
|
+
}
|
|
10428
|
+
return result;
|
|
10429
|
+
};
|
|
10430
|
+
}
|
|
10431
|
+
|
|
10227
10432
|
const REPLAY_SURFACE = {
|
|
10228
10433
|
allows: () => !0,
|
|
10229
10434
|
description: "any resource (in-memory replay; the committing surface enforces)"
|
|
@@ -10375,6 +10580,80 @@ function fabricatable(assignment) {
|
|
|
10375
10580
|
return assignment.target.path.every(segment => typeof segment != "number" || segment <= groqConditionDescribe.MAX_COUNTERFACTUAL_INDEX);
|
|
10376
10581
|
}
|
|
10377
10582
|
|
|
10583
|
+
function missingDocumentsOf({instance: instance, snapshot: snapshot, definition: definition}) {
|
|
10584
|
+
const stage = findStageNode({
|
|
10585
|
+
definition: definition,
|
|
10586
|
+
stageName: instance.currentStage
|
|
10587
|
+
});
|
|
10588
|
+
return activeFieldEntries(instance).flatMap(({target: target, entry: entry}) => {
|
|
10589
|
+
const declarations = {
|
|
10590
|
+
workflow: definition?.fields,
|
|
10591
|
+
stage: stage?.fields,
|
|
10592
|
+
activity: stage?.activities?.find(activity => activity.name === target.activity)?.fields
|
|
10593
|
+
}, title = entry.title ?? declarations[target.scope ?? "workflow"]?.find(field => field.name === target.field)?.title;
|
|
10594
|
+
return missingEntryDocRefs([ entry ], snapshot.knownIds).map(reference => ({
|
|
10595
|
+
target: target,
|
|
10596
|
+
reference: reference,
|
|
10597
|
+
...title !== void 0 ? {
|
|
10598
|
+
title: title
|
|
10599
|
+
} : {}
|
|
10600
|
+
}));
|
|
10601
|
+
});
|
|
10602
|
+
}
|
|
10603
|
+
|
|
10604
|
+
function blockingConditions(evaluation) {
|
|
10605
|
+
const transitions = evaluation.currentStage.transitions.some(entry => entry.whenSatisfied) ? [] : evaluation.currentStage.transitions;
|
|
10606
|
+
return [ ...evaluation.currentStage.activities.filter(entry => entry.status === "active" && (entry.activity.actions ?? []).length > 0).flatMap(entry => (entry.unmetRequirements ?? []).map(requirement => ({
|
|
10607
|
+
insight: entry.requirementInsights?.[requirement.name],
|
|
10608
|
+
activity: entry.activity.name
|
|
10609
|
+
}))), ...transitions.map(entry => ({
|
|
10610
|
+
insight: entry.insight
|
|
10611
|
+
})) ];
|
|
10612
|
+
}
|
|
10613
|
+
|
|
10614
|
+
function readsDocumentContent({entry: entry, read: read, atom: atom}) {
|
|
10615
|
+
return groqConditionDescribe.atomReadsDataset(atom) ? !0 : invariants.isSingleDocRefKind(entry._type) && read.path.length > 1 && read.path[1] !== "_id" && read.path[1] !== "_type";
|
|
10616
|
+
}
|
|
10617
|
+
|
|
10618
|
+
function contentTargets({atoms: atoms, fields: fields, activity: activity}) {
|
|
10619
|
+
return atoms.flatMap(atom => atom.reads.flatMap(read => {
|
|
10620
|
+
if (read.variable !== "fields") return [];
|
|
10621
|
+
const field = fields.findLast(({entry: entry, target: target}) => entry.name === read.path[0] && (target.scope !== "activity" || target.activity === activity));
|
|
10622
|
+
return field !== void 0 && readsDocumentContent({
|
|
10623
|
+
entry: field.entry,
|
|
10624
|
+
read: read,
|
|
10625
|
+
atom: atom
|
|
10626
|
+
}) ? [ _fieldTargetLabel(field.target) ] : [];
|
|
10627
|
+
}));
|
|
10628
|
+
}
|
|
10629
|
+
|
|
10630
|
+
async function blockingAtoms(condition, args) {
|
|
10631
|
+
const atoms = condition.insight?.blockedBy.map(({atom: atom}) => atom) ?? [], names = [ ...new Set(atoms.flatMap(atom => atom.reads).filter(read => read.path.length === 0 && args.definition.predicates?.[read.variable] !== void 0).map(read => read.variable)) ];
|
|
10632
|
+
if (names.length === 0) return atoms;
|
|
10633
|
+
const site = args.sites.find(entry => entry.insight === condition.insight);
|
|
10634
|
+
if (site === void 0) throw new Error("Missing evaluated scope for a blocking condition");
|
|
10635
|
+
const dependencies = await Promise.all(names.map(async name => {
|
|
10636
|
+
const predicate = args.definition.predicates?.[name];
|
|
10637
|
+
if (predicate === void 0) throw new Error(`Missing predicate "${name}"`);
|
|
10638
|
+
return (await groqConditionDescribe.explainCondition({
|
|
10639
|
+
condition: site.params[name] === !0 ? `!(${predicate})` : predicate,
|
|
10640
|
+
dataset: args.snapshot.docs,
|
|
10641
|
+
params: site.params
|
|
10642
|
+
})).blockedBy.map(({atom: atom}) => atom);
|
|
10643
|
+
}));
|
|
10644
|
+
return [ ...atoms, ...dependencies.flat() ];
|
|
10645
|
+
}
|
|
10646
|
+
|
|
10647
|
+
async function blockingMissingDocuments(args) {
|
|
10648
|
+
if (!args.missingDocuments?.length) return [];
|
|
10649
|
+
const fields = activeFieldEntries(args.instance), targets = (await Promise.all(blockingConditions(args).map(async condition => contentTargets({
|
|
10650
|
+
atoms: await blockingAtoms(condition, args),
|
|
10651
|
+
fields: fields,
|
|
10652
|
+
activity: condition.activity
|
|
10653
|
+
})))).flat(), blocked = /* @__PURE__ */ new Set([ ...targets, ...missingRequiredDocuments(args).map(({target: target}) => _fieldTargetLabel(target)) ]);
|
|
10654
|
+
return args.missingDocuments.filter(({target: target}) => blocked.has(_fieldTargetLabel(target)));
|
|
10655
|
+
}
|
|
10656
|
+
|
|
10378
10657
|
async function subjectResourceGrants(args) {
|
|
10379
10658
|
const {clientForGdr: clientForGdr, instance: instance} = args, clients = /* @__PURE__ */ new Map;
|
|
10380
10659
|
for (const {parsed: parsed, resource: resource} of foreignSubjectRefs(instance)) {
|
|
@@ -10437,6 +10716,9 @@ async function evaluateInstance(args) {
|
|
|
10437
10716
|
guards: guards,
|
|
10438
10717
|
now: now,
|
|
10439
10718
|
resourceGrants: resourceGrants,
|
|
10719
|
+
documentAvailability: documentAvailabilityReader({
|
|
10720
|
+
clientForGdr: clientForGdr
|
|
10721
|
+
}),
|
|
10440
10722
|
guardDereference: clientGuardDereference(client),
|
|
10441
10723
|
...localPrincipalId !== void 0 ? {
|
|
10442
10724
|
localPrincipalId: localPrincipalId
|
|
@@ -10544,7 +10826,11 @@ async function evaluateFromSnapshot(args) {
|
|
|
10544
10826
|
}), subjectPermissionReason = subjectDenials.length > 0 ? {
|
|
10545
10827
|
kind: "subject-permission-denied",
|
|
10546
10828
|
denied: subjectDenials
|
|
10547
|
-
} : void 0, sites = [], activityEvaluations = []
|
|
10829
|
+
} : void 0, sites = [], activityEvaluations = [], requiredDocuments = requiredDocumentRequirements({
|
|
10830
|
+
instance: instance,
|
|
10831
|
+
definition: definition,
|
|
10832
|
+
snapshot: snapshot
|
|
10833
|
+
});
|
|
10548
10834
|
for (const activity of stage.activities ?? []) activityEvaluations.push(await evaluateActivity({
|
|
10549
10835
|
activity: activity,
|
|
10550
10836
|
statusEntry: currentActivityEntries.find(t => t.name === activity.name),
|
|
@@ -10555,36 +10841,23 @@ async function evaluateFromSnapshot(args) {
|
|
|
10555
10841
|
stageHasExits: !isTerminalStage(stage),
|
|
10556
10842
|
guardDenial: guardDenial,
|
|
10557
10843
|
subjectPermissionReason: subjectPermissionReason,
|
|
10844
|
+
requiredDocuments: requiredDocuments,
|
|
10558
10845
|
sites: sites,
|
|
10559
10846
|
autonomy: activityAutonomyOf(stageAutonomy2, activity.name),
|
|
10560
10847
|
fireConsequence: fireConsequence
|
|
10561
10848
|
}));
|
|
10562
|
-
const cascadeParams = await renderConditionScope(scopeSource), transitionEvaluations =
|
|
10563
|
-
|
|
10564
|
-
|
|
10565
|
-
|
|
10566
|
-
|
|
10567
|
-
|
|
10568
|
-
},
|
|
10569
|
-
condition: transition.when,
|
|
10570
|
-
params: cascadeParams,
|
|
10571
|
-
snapshot: snapshot,
|
|
10572
|
-
sites: sites
|
|
10573
|
-
});
|
|
10574
|
-
transitionEvaluations.push({
|
|
10575
|
-
transition: transition,
|
|
10576
|
-
whenSatisfied: insight.outcome === "satisfied",
|
|
10577
|
-
unevaluable: insight.outcome === "unevaluable",
|
|
10578
|
-
insight: insight
|
|
10579
|
-
});
|
|
10580
|
-
}
|
|
10581
|
-
const currentStage = {
|
|
10849
|
+
const cascadeParams = await renderConditionScope(scopeSource), transitionEvaluations = await evaluateTransitions({
|
|
10850
|
+
stage: stage,
|
|
10851
|
+
cascadeParams: cascadeParams,
|
|
10852
|
+
snapshot: snapshot,
|
|
10853
|
+
sites: sites
|
|
10854
|
+
}), currentStage = {
|
|
10582
10855
|
stage: stage,
|
|
10583
10856
|
...semanticsOf(stage),
|
|
10584
10857
|
activities: activityEvaluations,
|
|
10585
10858
|
transitions: transitionEvaluations,
|
|
10586
10859
|
autonomy: stageAutonomy2
|
|
10587
|
-
}, pendingOnYou = activityEvaluations.filter(t => t.pendingOnActor), canInteract = activityEvaluations.some(t => t.actions.some(a => a.allowed)), editGuardDenial = guardDenial?.kind === "mutation-guard-denied" ? guardDenial : void 0, editableFields = await evaluateEditableFields({
|
|
10860
|
+
}, missingDocuments = await readMissingDocuments(args), pendingOnYou = activityEvaluations.filter(t => t.pendingOnActor), canInteract = activityEvaluations.some(t => t.actions.some(a => a.allowed)), editGuardDenial = guardDenial?.kind === "mutation-guard-denied" ? guardDenial : void 0, editableFields = await evaluateEditableFields({
|
|
10588
10861
|
instance: instance,
|
|
10589
10862
|
definition: definition,
|
|
10590
10863
|
stage: stage,
|
|
@@ -10611,6 +10884,17 @@ async function evaluateFromSnapshot(args) {
|
|
|
10611
10884
|
sites: sites,
|
|
10612
10885
|
snapshot: snapshot
|
|
10613
10886
|
}),
|
|
10887
|
+
...missingDocuments.length > 0 ? {
|
|
10888
|
+
missingDocuments: missingDocuments,
|
|
10889
|
+
blockingMissingDocuments: await blockingMissingDocuments({
|
|
10890
|
+
instance: instance,
|
|
10891
|
+
definition: definition,
|
|
10892
|
+
currentStage: currentStage,
|
|
10893
|
+
missingDocuments: missingDocuments,
|
|
10894
|
+
snapshot: snapshot,
|
|
10895
|
+
sites: sites
|
|
10896
|
+
})
|
|
10897
|
+
} : {},
|
|
10614
10898
|
autonomy: autonomy,
|
|
10615
10899
|
...nextEvaluationAt !== void 0 ? {
|
|
10616
10900
|
nextEvaluationAt: nextEvaluationAt
|
|
@@ -10618,6 +10902,42 @@ async function evaluateFromSnapshot(args) {
|
|
|
10618
10902
|
};
|
|
10619
10903
|
}
|
|
10620
10904
|
|
|
10905
|
+
async function readMissingDocuments(args) {
|
|
10906
|
+
const missingDocuments = missingDocumentsOf(args);
|
|
10907
|
+
if (args.documentAvailability === void 0 || missingDocuments.length === 0) return missingDocuments;
|
|
10908
|
+
const availability = await args.documentAvailability(missingDocuments);
|
|
10909
|
+
return missingDocuments.map(document => {
|
|
10910
|
+
const verdict = availability.get(document.reference.id);
|
|
10911
|
+
return verdict === void 0 ? document : {
|
|
10912
|
+
...document,
|
|
10913
|
+
availability: verdict
|
|
10914
|
+
};
|
|
10915
|
+
});
|
|
10916
|
+
}
|
|
10917
|
+
|
|
10918
|
+
async function evaluateTransitions(args) {
|
|
10919
|
+
const {stage: stage, cascadeParams: cascadeParams, snapshot: snapshot, sites: sites} = args, evaluations = [];
|
|
10920
|
+
for (const transition of stage.transitions ?? []) {
|
|
10921
|
+
const insight = await explainSite({
|
|
10922
|
+
site: {
|
|
10923
|
+
kind: "transition",
|
|
10924
|
+
transition: transition.name
|
|
10925
|
+
},
|
|
10926
|
+
condition: transition.when,
|
|
10927
|
+
params: cascadeParams,
|
|
10928
|
+
snapshot: snapshot,
|
|
10929
|
+
sites: sites
|
|
10930
|
+
});
|
|
10931
|
+
evaluations.push({
|
|
10932
|
+
transition: transition,
|
|
10933
|
+
whenSatisfied: insight.outcome === "satisfied",
|
|
10934
|
+
unevaluable: insight.outcome === "unevaluable",
|
|
10935
|
+
insight: insight
|
|
10936
|
+
});
|
|
10937
|
+
}
|
|
10938
|
+
return evaluations;
|
|
10939
|
+
}
|
|
10940
|
+
|
|
10621
10941
|
const AUTONOMY_CACHE = /* @__PURE__ */ new WeakMap;
|
|
10622
10942
|
|
|
10623
10943
|
function autonomyOf(definition) {
|
|
@@ -10628,54 +10948,56 @@ function autonomyOf(definition) {
|
|
|
10628
10948
|
}
|
|
10629
10949
|
|
|
10630
10950
|
async function evaluateEditableFields(args) {
|
|
10631
|
-
const
|
|
10632
|
-
for (const site of editableFieldsInStage(definition, stage))
|
|
10633
|
-
const window = fieldWindowOpen(instance, site), insight = await editPredicateInsight({
|
|
10634
|
-
site: site,
|
|
10635
|
-
snapshot: snapshot,
|
|
10636
|
-
scope: scope,
|
|
10637
|
-
scopeForActivity: scopeForActivity,
|
|
10638
|
-
sites: sites
|
|
10639
|
-
}), predicateSatisfied = insight === void 0 || insight.outcome === "satisfied", reason = editDisabledReason({
|
|
10640
|
-
effective: site.effective,
|
|
10641
|
-
instance: instance,
|
|
10642
|
-
window: window,
|
|
10643
|
-
guardDenial: guardDenial,
|
|
10644
|
-
predicateSatisfied: predicateSatisfied
|
|
10645
|
-
}), value = readFieldValue(instance, site);
|
|
10646
|
-
fields.push({
|
|
10647
|
-
scope: site.scope,
|
|
10648
|
-
...site.activity !== void 0 ? {
|
|
10649
|
-
activity: site.activity
|
|
10650
|
-
} : {},
|
|
10651
|
-
name: site.name,
|
|
10652
|
-
type: site.type,
|
|
10653
|
-
...site.title !== void 0 ? {
|
|
10654
|
-
title: site.title
|
|
10655
|
-
} : {},
|
|
10656
|
-
...site.validation !== void 0 ? {
|
|
10657
|
-
validation: site.validation
|
|
10658
|
-
} : {},
|
|
10659
|
-
...site.roles !== void 0 ? {
|
|
10660
|
-
roles: site.roles,
|
|
10661
|
-
...definition.roleAliases !== void 0 ? {
|
|
10662
|
-
roleAliases: definition.roleAliases
|
|
10663
|
-
} : {}
|
|
10664
|
-
} : {},
|
|
10665
|
-
value: value,
|
|
10666
|
-
editable: reason === void 0,
|
|
10667
|
-
...reason !== void 0 ? {
|
|
10668
|
-
disabledReason: reason
|
|
10669
|
-
} : {},
|
|
10670
|
-
...fieldProvenance(instance, site.ref),
|
|
10671
|
-
...insight !== void 0 ? {
|
|
10672
|
-
insight: insight
|
|
10673
|
-
} : {}
|
|
10674
|
-
});
|
|
10675
|
-
}
|
|
10951
|
+
const fields = [];
|
|
10952
|
+
for (const site of editableFieldsInStage(args.definition, args.stage)) fields.push(await evaluateEditableField(args, site));
|
|
10676
10953
|
return fields;
|
|
10677
10954
|
}
|
|
10678
10955
|
|
|
10956
|
+
async function evaluateEditableField(args, site) {
|
|
10957
|
+
const {instance: instance, definition: definition, snapshot: snapshot, scope: scope, scopeForActivity: scopeForActivity, guardDenial: guardDenial, sites: sites} = args, window = fieldWindowOpen(instance, site), insight = await editPredicateInsight({
|
|
10958
|
+
site: site,
|
|
10959
|
+
snapshot: snapshot,
|
|
10960
|
+
scope: scope,
|
|
10961
|
+
scopeForActivity: scopeForActivity,
|
|
10962
|
+
sites: sites
|
|
10963
|
+
}), predicateSatisfied = insight === void 0 || insight.outcome === "satisfied", reason = editDisabledReason({
|
|
10964
|
+
effective: site.effective,
|
|
10965
|
+
instance: instance,
|
|
10966
|
+
window: window,
|
|
10967
|
+
guardDenial: guardDenial,
|
|
10968
|
+
predicateSatisfied: predicateSatisfied
|
|
10969
|
+
});
|
|
10970
|
+
return {
|
|
10971
|
+
scope: site.scope,
|
|
10972
|
+
...site.activity !== void 0 ? {
|
|
10973
|
+
activity: site.activity
|
|
10974
|
+
} : {},
|
|
10975
|
+
name: site.name,
|
|
10976
|
+
type: site.type,
|
|
10977
|
+
...site.title !== void 0 ? {
|
|
10978
|
+
title: site.title
|
|
10979
|
+
} : {},
|
|
10980
|
+
...site.validation !== void 0 ? {
|
|
10981
|
+
validation: site.validation
|
|
10982
|
+
} : {},
|
|
10983
|
+
...site.roles !== void 0 ? {
|
|
10984
|
+
roles: site.roles,
|
|
10985
|
+
...definition.roleAliases !== void 0 ? {
|
|
10986
|
+
roleAliases: definition.roleAliases
|
|
10987
|
+
} : {}
|
|
10988
|
+
} : {},
|
|
10989
|
+
value: readFieldValue(instance, site),
|
|
10990
|
+
editable: reason === void 0,
|
|
10991
|
+
...reason !== void 0 ? {
|
|
10992
|
+
disabledReason: reason
|
|
10993
|
+
} : {},
|
|
10994
|
+
...fieldProvenance(instance, site.ref),
|
|
10995
|
+
...insight !== void 0 ? {
|
|
10996
|
+
insight: insight
|
|
10997
|
+
} : {}
|
|
10998
|
+
};
|
|
10999
|
+
}
|
|
11000
|
+
|
|
10679
11001
|
async function editPredicateInsight(args) {
|
|
10680
11002
|
const {site: site, snapshot: snapshot, scope: scope, scopeForActivity: scopeForActivity, sites: sites} = args;
|
|
10681
11003
|
if (typeof site.effective != "string") return;
|
|
@@ -10699,14 +11021,10 @@ async function editPredicateInsight(args) {
|
|
|
10699
11021
|
async function forecastSubjectDenials(args) {
|
|
10700
11022
|
const {instance: instance, snapshot: snapshot, resourceGrants: resourceGrants} = args;
|
|
10701
11023
|
if (resourceGrants === void 0 || resourceGrants.size === 0) return [];
|
|
10702
|
-
const denials = [],
|
|
10703
|
-
for (const {ref: ref, parsed: parsed, resource: resource} of
|
|
10704
|
-
|
|
10705
|
-
|
|
10706
|
-
const access = resourceGrants.get(invariants.resourceGdr(resource));
|
|
10707
|
-
if (access === void 0) continue;
|
|
10708
|
-
const doc = snapshot.docs.find(d => d._id === ref.id);
|
|
10709
|
-
doc !== void 0 && await subjectUpdateAllowed({
|
|
11024
|
+
const denials = [], subjects = new Map(foreignSubjectRefs(instance).map(subject => [ subject.ref.id, subject ]));
|
|
11025
|
+
for (const {ref: ref, parsed: parsed, resource: resource} of subjects.values()) {
|
|
11026
|
+
const access = resourceGrants.get(invariants.resourceGdr(resource)), doc = snapshot.docs.find(d => d._id === ref.id);
|
|
11027
|
+
access === void 0 || doc === void 0 || await subjectUpdateAllowed({
|
|
10710
11028
|
doc: doc,
|
|
10711
11029
|
parsed: parsed,
|
|
10712
11030
|
grants: access.grants,
|
|
@@ -10739,13 +11057,13 @@ async function subjectUpdateAllowed(args) {
|
|
|
10739
11057
|
}
|
|
10740
11058
|
|
|
10741
11059
|
async function evaluateActivity(args) {
|
|
10742
|
-
const {activity: activity, statusEntry: statusEntry, instance: instance, snapshot: snapshot, activityScope: activityScope, cascadeActivityScope: cascadeActivityScope, stageHasExits: stageHasExits, guardDenial: guardDenial, subjectPermissionReason: subjectPermissionReason, sites: sites, autonomy: autonomy, fireConsequence: fireConsequence} = args, status = statusEntry?.status ?? "skipped", assigned = activityScope.assigned === !0, {unmetRequirements:
|
|
11060
|
+
const {activity: activity, statusEntry: statusEntry, instance: instance, snapshot: snapshot, activityScope: activityScope, cascadeActivityScope: cascadeActivityScope, stageHasExits: stageHasExits, guardDenial: guardDenial, subjectPermissionReason: subjectPermissionReason, requiredDocuments: requiredDocuments, sites: sites, autonomy: autonomy, fireConsequence: fireConsequence} = args, status = statusEntry?.status ?? "skipped", assigned = activityScope.assigned === !0, {unmetRequirements: activityRequirements, ...conditionInsights} = await explainActivityConditions({
|
|
10743
11061
|
activity: activity,
|
|
10744
11062
|
activityScope: activityScope,
|
|
10745
11063
|
cascadeActivityScope: cascadeActivityScope,
|
|
10746
11064
|
snapshot: snapshot,
|
|
10747
11065
|
sites: sites
|
|
10748
|
-
}), requirementsReason = unmetRequirements.length > 0 ? {
|
|
11066
|
+
}), unmetRequirements = [ ...requiredDocuments, ...activityRequirements ], requirementsReason = unmetRequirements.length > 0 ? {
|
|
10749
11067
|
kind: "requirements-unmet",
|
|
10750
11068
|
unmetRequirements: unmetRequirements
|
|
10751
11069
|
} : void 0, actions = [];
|
|
@@ -11121,6 +11439,46 @@ function claimReleasedEntry(args) {
|
|
|
11121
11439
|
};
|
|
11122
11440
|
}
|
|
11123
11441
|
|
|
11442
|
+
const RENEW_MAX_ATTEMPTS = 3;
|
|
11443
|
+
|
|
11444
|
+
async function claimOf(args) {
|
|
11445
|
+
const {client: client, tag: tag, instanceId: instanceId, effectKey: effectKey} = args, instance = await reload({
|
|
11446
|
+
client: client,
|
|
11447
|
+
instanceId: instanceId,
|
|
11448
|
+
tag: tag
|
|
11449
|
+
});
|
|
11450
|
+
return {
|
|
11451
|
+
instance: instance,
|
|
11452
|
+
claim: instance.pendingEffects.find(e => e._key === effectKey)?.claim
|
|
11453
|
+
};
|
|
11454
|
+
}
|
|
11455
|
+
|
|
11456
|
+
async function claimStillHeld(args) {
|
|
11457
|
+
const {claimToken: claimToken, ...where} = args, {claim: claim} = await claimOf(where);
|
|
11458
|
+
return claim?.claimToken === claimToken;
|
|
11459
|
+
}
|
|
11460
|
+
|
|
11461
|
+
async function renewClaimLease(args) {
|
|
11462
|
+
const {client: client, tag: tag, instanceId: instanceId, effectKey: effectKey, claimToken: claimToken, holdMs: holdMs, clock: clock} = args;
|
|
11463
|
+
for (let attempt = 1; attempt <= RENEW_MAX_ATTEMPTS; attempt++) {
|
|
11464
|
+
const {instance: instance, claim: claim} = await claimOf({
|
|
11465
|
+
client: client,
|
|
11466
|
+
tag: tag,
|
|
11467
|
+
instanceId: instanceId,
|
|
11468
|
+
effectKey: effectKey
|
|
11469
|
+
});
|
|
11470
|
+
if (claim?.claimToken !== claimToken) return;
|
|
11471
|
+
const until = addMs(clock(), holdMs);
|
|
11472
|
+
try {
|
|
11473
|
+
return await client.patch(instance._id).ifRevisionId(instance._rev).set({
|
|
11474
|
+
[`pendingEffects[_key=="${effectKey}"].claim.leaseExpiresAt`]: until
|
|
11475
|
+
}).commit(SYNC_COMMIT), until;
|
|
11476
|
+
} catch (error) {
|
|
11477
|
+
if (!isRevisionConflict(error)) throw error;
|
|
11478
|
+
}
|
|
11479
|
+
}
|
|
11480
|
+
}
|
|
11481
|
+
|
|
11124
11482
|
const SWEEP_MAX_ATTEMPTS = 5;
|
|
11125
11483
|
|
|
11126
11484
|
async function sweepStaleClaims(args) {
|
|
@@ -11168,7 +11526,7 @@ function isProjectRole(value) {
|
|
|
11168
11526
|
|
|
11169
11527
|
async function projectRoleNames(request, projectId) {
|
|
11170
11528
|
const result = await request({
|
|
11171
|
-
|
|
11529
|
+
url: `/projects/${encodeURIComponent(projectId)}/roles`
|
|
11172
11530
|
});
|
|
11173
11531
|
if (!Array.isArray(result) || !result.every(isProjectRole)) throw new Error(`Project role catalog response is invalid for project "${projectId}"`);
|
|
11174
11532
|
return new Set(result.map(role => role.name));
|
|
@@ -11364,7 +11722,7 @@ async function validateDefinitionRoles(args) {
|
|
|
11364
11722
|
const references = args.definitions.flatMap(definitionRoleReferences);
|
|
11365
11723
|
if (references.length === 0) /* @__PURE__ */ return new Map;
|
|
11366
11724
|
if (args.client.request === void 0) throw new Error("workflow.deployDefinitions: definitions reference project roles, but the client cannot read the project role catalog");
|
|
11367
|
-
const projectId = roleCatalogProjectId(args.client, args.workflowResource), [known, members] = await Promise.all([ projectRoleNames(args.client.request, projectId),
|
|
11725
|
+
const projectId = roleCatalogProjectId(args.client, args.workflowResource), [known, members] = await Promise.all([ projectRoleNames(args.client.request, projectId), _requestProjectMembers(args.client.request, projectId) ]), unknown = references.filter(reference => !known.has(reference.role));
|
|
11368
11726
|
if (unknown.length > 0) {
|
|
11369
11727
|
const lines = unknown.map(({definition: definition, path: path, role: role}) => ` - ${definition}: ${path} references unknown role "${role}"`);
|
|
11370
11728
|
throw new Error(`workflow.deployDefinitions: unknown project roles for project "${projectId}":\n${lines.join(`\n`)}`);
|
|
@@ -11494,7 +11852,7 @@ function refsOf(def) {
|
|
|
11494
11852
|
}
|
|
11495
11853
|
|
|
11496
11854
|
function hashDefinitionContent(def) {
|
|
11497
|
-
const canonical = stableStringify(def);
|
|
11855
|
+
const canonical = stableStringify(withoutAuthoringRuntime(def));
|
|
11498
11856
|
let hash = 0xcbf29ce484222325n;
|
|
11499
11857
|
for (let i = 0; i < canonical.length; i++) hash ^= BigInt(canonical.charCodeAt(i)),
|
|
11500
11858
|
hash = hash * 0x100000001b3n & 0xffffffffffffffffn;
|
|
@@ -11507,7 +11865,7 @@ function planDefinitionDeploy({def: def, latest: latest, target: target}) {
|
|
|
11507
11865
|
definition: def,
|
|
11508
11866
|
workflowResource: target.workflowResource
|
|
11509
11867
|
});
|
|
11510
|
-
const expanded = expandResourceAliases(def, target.resourceAliases), contentHash = hashDefinitionContent(expanded), unchanged = latest !== void 0 && latest.contentHash === contentHash, version = unchanged ? latest.version : (latest?.version ?? 0) + 1, docId = invariants.definitionDocId({
|
|
11868
|
+
const expanded = withoutAuthoringRuntime(expandResourceAliases(def, target.resourceAliases)), contentHash = hashDefinitionContent(expanded), unchanged = latest !== void 0 && latest.contentHash === contentHash, version = unchanged ? latest.version : (latest?.version ?? 0) + 1, docId = invariants.definitionDocId({
|
|
11511
11869
|
tag: target.tag,
|
|
11512
11870
|
definition: expanded.name,
|
|
11513
11871
|
version: version
|
|
@@ -11564,10 +11922,17 @@ function isDocumentEnvelopeKey(key) {
|
|
|
11564
11922
|
return key.startsWith("_") || DEPLOY_STAMPED_KEYS.has(key);
|
|
11565
11923
|
}
|
|
11566
11924
|
|
|
11925
|
+
const AUTHORING_RUNTIME_KEY = "runtime";
|
|
11926
|
+
|
|
11927
|
+
function withoutAuthoringRuntime(def) {
|
|
11928
|
+
const content = Object.entries(def).filter(([key]) => key !== AUTHORING_RUNTIME_KEY);
|
|
11929
|
+
return Object.fromEntries(content);
|
|
11930
|
+
}
|
|
11931
|
+
|
|
11567
11932
|
function parseDefinitionInput(def, caller) {
|
|
11568
11933
|
const label = invariants.labelFor(caller, def);
|
|
11569
11934
|
if (def._type !== void 0 && def._type !== invariants.WORKFLOW_DEFINITION_TYPE) throw new Error(`${label}: expected a workflow definition or a "${invariants.WORKFLOW_DEFINITION_TYPE}" document, got _type "${String(def._type)}"`);
|
|
11570
|
-
const content = Object.fromEntries(Object.entries(def).filter(([key]) => !isDocumentEnvelopeKey(key)));
|
|
11935
|
+
const content = Object.fromEntries(Object.entries(withoutAuthoringRuntime(def)).filter(([key]) => !isDocumentEnvelopeKey(key)));
|
|
11571
11936
|
return invariants.parseStoredDefinition(content, label);
|
|
11572
11937
|
}
|
|
11573
11938
|
|
|
@@ -13487,7 +13852,7 @@ function pendingEntry(args) {
|
|
|
13487
13852
|
}
|
|
13488
13853
|
|
|
13489
13854
|
function createEffectCommitQueue(args) {
|
|
13490
|
-
const {effectKey: effectKey, claimToken: claimToken, commit: commit} = args, pending = [];
|
|
13855
|
+
const {effectKey: effectKey, claimToken: claimToken, attempt: attempt, commit: commit} = args, pending = [];
|
|
13491
13856
|
let admitted = 0, dead, closed = !1, tail = Promise.resolve();
|
|
13492
13857
|
function assertOpen() {
|
|
13493
13858
|
if (dead !== void 0) throw dead;
|
|
@@ -13542,7 +13907,7 @@ function createEffectCommitQueue(args) {
|
|
|
13542
13907
|
value: entry.latestValue
|
|
13543
13908
|
}
|
|
13544
13909
|
} ],
|
|
13545
|
-
idempotencyKey: `effect:${effectKey}:${claimToken}:progress:${coalesceKey}:${admitted}`,
|
|
13910
|
+
idempotencyKey: `effect:${effectKey}:${claimToken}:${attempt}:progress:${coalesceKey}:${admitted}`,
|
|
13546
13911
|
coalesceKey: coalesceKey,
|
|
13547
13912
|
latestValue: value
|
|
13548
13913
|
});
|
|
@@ -13552,6 +13917,29 @@ function createEffectCommitQueue(args) {
|
|
|
13552
13917
|
};
|
|
13553
13918
|
}
|
|
13554
13919
|
|
|
13920
|
+
function effectRetryPolicy(definition, effectName) {
|
|
13921
|
+
return findEffect(definition, effectName)?.retry;
|
|
13922
|
+
}
|
|
13923
|
+
|
|
13924
|
+
function retryWindowClosed(policy, elapsedMs) {
|
|
13925
|
+
return policy.expiryMs !== void 0 && elapsedMs >= policy.expiryMs;
|
|
13926
|
+
}
|
|
13927
|
+
|
|
13928
|
+
function nextRetryDelayMs(policy, at) {
|
|
13929
|
+
if (at.attempt >= policy.attempts) return;
|
|
13930
|
+
const delay = invariants.effectBackoffMs(policy, at.attempt);
|
|
13931
|
+
return retryWindowClosed(policy, at.elapsedMs + delay) ? void 0 : delay;
|
|
13932
|
+
}
|
|
13933
|
+
|
|
13934
|
+
function retryFailedDetail(args) {
|
|
13935
|
+
const window = args.windowClosed ? ", retry window expired" : "";
|
|
13936
|
+
return `failed after ${args.at.attempt} of ${args.attempts} attempts${window}: ${args.error.message}`;
|
|
13937
|
+
}
|
|
13938
|
+
|
|
13939
|
+
function retrySucceededDetail(args) {
|
|
13940
|
+
if (args.at.attempt !== 1) return `succeeded on attempt ${args.at.attempt} of ${args.attempts}`;
|
|
13941
|
+
}
|
|
13942
|
+
|
|
13555
13943
|
class MissingHandlerError extends invariants.WorkflowError {
|
|
13556
13944
|
info;
|
|
13557
13945
|
constructor(args) {
|
|
@@ -13634,7 +14022,7 @@ function isCancelledCompletion(data) {
|
|
|
13634
14022
|
}
|
|
13635
14023
|
|
|
13636
14024
|
async function drainEffectsInternal(args) {
|
|
13637
|
-
const {tag: tag, workflowResource: workflowResource, instanceId: instanceId, handlers: handlers, missingHandler: missingHandler, logger: logger, handlerClient: rawHandlerClient, handlerResourceClients: rawHandlerResourceClients} = args, cascadeTelemetry = drainCascadeTelemetry(args.telemetry), {client: client, resourceClients: resourceClients} = taggedScope(args, REQUEST_TAG.drain), leaseMs = args.leaseMs ?? DEFAULT_EFFECT_LEASE_MS, clock = args.clock ?? wallClock, handlerClient = effectHandlerClient(rawHandlerClient), handlerResourceClients = effectHandlerResolver(rawHandlerResourceClients), routeGdr = buildClientForGdr({
|
|
14025
|
+
const {tag: tag, workflowResource: workflowResource, instanceId: instanceId, handlers: handlers, missingHandler: missingHandler, logger: logger, handlerClient: rawHandlerClient, handlerResourceClients: rawHandlerResourceClients} = args, cascadeTelemetry = drainCascadeTelemetry(args.telemetry), {client: client, resourceClients: resourceClients} = taggedScope(args, REQUEST_TAG.drain), leaseMs = args.leaseMs ?? DEFAULT_EFFECT_LEASE_MS, clock = args.clock ?? wallClock, sleep = args.sleep ?? wallSleeper, handlerClient = effectHandlerClient(rawHandlerClient), handlerResourceClients = effectHandlerResolver(rawHandlerResourceClients), routeGdr = buildClientForGdr({
|
|
13638
14026
|
client: handlerClient,
|
|
13639
14027
|
workflowResource: workflowResource,
|
|
13640
14028
|
resourceClients: handlerResourceClients
|
|
@@ -13660,6 +14048,7 @@ async function drainEffectsInternal(args) {
|
|
|
13660
14048
|
log: log,
|
|
13661
14049
|
leaseMs: leaseMs,
|
|
13662
14050
|
clock: clock,
|
|
14051
|
+
sleep: sleep,
|
|
13663
14052
|
...executionContext !== void 0 ? {
|
|
13664
14053
|
executionContext: executionContext
|
|
13665
14054
|
} : {},
|
|
@@ -13667,14 +14056,14 @@ async function drainEffectsInternal(args) {
|
|
|
13667
14056
|
drainerActor: drainerActor,
|
|
13668
14057
|
clientFor: clientFor,
|
|
13669
14058
|
buckets: buckets,
|
|
13670
|
-
|
|
14059
|
+
handledKeys: /* @__PURE__ */ new Set
|
|
13671
14060
|
};
|
|
13672
14061
|
for (;await drainOneCandidate(pass); ) ;
|
|
13673
14062
|
return buckets;
|
|
13674
14063
|
}
|
|
13675
14064
|
|
|
13676
14065
|
async function drainOneCandidate(pass) {
|
|
13677
|
-
const {client: client, tag: tag, instanceId: instanceId, buckets: buckets,
|
|
14066
|
+
const {client: client, tag: tag, instanceId: instanceId, buckets: buckets, handledKeys: handledKeys} = pass, now = pass.clock(), before = await reload({
|
|
13678
14067
|
client: client,
|
|
13679
14068
|
instanceId: instanceId,
|
|
13680
14069
|
tag: tag
|
|
@@ -13683,7 +14072,7 @@ async function drainOneCandidate(pass) {
|
|
|
13683
14072
|
const candidate = findClaimableCandidate({
|
|
13684
14073
|
instance: before,
|
|
13685
14074
|
now: now,
|
|
13686
|
-
|
|
14075
|
+
handledKeys: handledKeys
|
|
13687
14076
|
});
|
|
13688
14077
|
if (candidate === void 0) return !1;
|
|
13689
14078
|
const handler = pass.handlers[candidate.name];
|
|
@@ -13692,7 +14081,7 @@ async function drainOneCandidate(pass) {
|
|
|
13692
14081
|
candidate: candidate,
|
|
13693
14082
|
instanceId: instanceId,
|
|
13694
14083
|
log: pass.log
|
|
13695
|
-
}), buckets.skipped.push(candidate),
|
|
14084
|
+
}), buckets.skipped.push(candidate), handledKeys.add(candidate._key), !0;
|
|
13696
14085
|
const claimed = await claimPendingEffect({
|
|
13697
14086
|
client: client,
|
|
13698
14087
|
instance: before,
|
|
@@ -13705,11 +14094,15 @@ async function drainOneCandidate(pass) {
|
|
|
13705
14094
|
} : {}
|
|
13706
14095
|
});
|
|
13707
14096
|
if (claimed === void 0) return !0;
|
|
13708
|
-
const outcome = await dispatchAndReport({
|
|
14097
|
+
const retry = effectRetryPolicy(parseDefinitionSnapshot(before), candidate.name), outcome = await dispatchAndReport({
|
|
13709
14098
|
handler: handler,
|
|
13710
14099
|
candidate: candidate,
|
|
13711
14100
|
claimToken: claimed.claimToken,
|
|
14101
|
+
...retry !== void 0 ? {
|
|
14102
|
+
retry: retry
|
|
14103
|
+
} : {},
|
|
13712
14104
|
leaseMs: pass.leaseMs,
|
|
14105
|
+
sleep: pass.sleep,
|
|
13713
14106
|
client: client,
|
|
13714
14107
|
handlerClient: pass.handlerClient,
|
|
13715
14108
|
tag: tag,
|
|
@@ -13727,50 +14120,80 @@ async function drainOneCandidate(pass) {
|
|
|
13727
14120
|
telemetry: pass.telemetry,
|
|
13728
14121
|
clock: pass.clock
|
|
13729
14122
|
});
|
|
13730
|
-
return buckets[outcome].push(candidate),
|
|
14123
|
+
return buckets[outcome].push(candidate), outcome === "lost" && handledKeys.add(candidate._key),
|
|
14124
|
+
!0;
|
|
13731
14125
|
}
|
|
13732
14126
|
|
|
13733
|
-
function findClaimableCandidate({instance: instance, now: now,
|
|
13734
|
-
return instance.pendingEffects.find(e => (e.claim === void 0 || isClaimExpired(e.claim, now)) && !
|
|
14127
|
+
function findClaimableCandidate({instance: instance, now: now, handledKeys: handledKeys}) {
|
|
14128
|
+
return instance.pendingEffects.find(e => (e.claim === void 0 || isClaimExpired(e.claim, now)) && !handledKeys.has(e._key));
|
|
13735
14129
|
}
|
|
13736
14130
|
|
|
13737
14131
|
async function dispatchAndReport(args) {
|
|
13738
|
-
const {handler: handler, candidate: candidate, claimToken: claimToken, leaseMs: leaseMs, client: client, handlerClient: handlerClient, tag: tag, workflowResource: workflowResource, resourceClients: resourceClients, instanceId: instanceId, clientFor: clientFor, logger: logger, log: log, executionContext: executionContext, telemetry: telemetry, clock: clock} = args,
|
|
13739
|
-
effectKey: candidate._key,
|
|
13740
|
-
claimToken: claimToken,
|
|
13741
|
-
commit: async req => {
|
|
13742
|
-
await workflow.commitEffectOps({
|
|
13743
|
-
client: client,
|
|
13744
|
-
tag: tag,
|
|
13745
|
-
workflowResource: workflowResource,
|
|
13746
|
-
...resourceClients !== void 0 ? {
|
|
13747
|
-
resourceClients: resourceClients
|
|
13748
|
-
} : {},
|
|
13749
|
-
...executionContext !== void 0 ? {
|
|
13750
|
-
executionContext: executionContext
|
|
13751
|
-
} : {},
|
|
13752
|
-
...telemetry !== void 0 ? {
|
|
13753
|
-
telemetry: telemetry
|
|
13754
|
-
} : {},
|
|
13755
|
-
instanceId: instanceId,
|
|
13756
|
-
effectKey: candidate._key,
|
|
13757
|
-
claimToken: claimToken,
|
|
13758
|
-
ops: req.ops,
|
|
13759
|
-
idempotencyKey: req.idempotencyKey,
|
|
13760
|
-
leaseMs: leaseMs,
|
|
13761
|
-
clock: clock
|
|
13762
|
-
});
|
|
13763
|
-
}
|
|
13764
|
-
}), {outputs: outputs, ops: ops, dispatchError: dispatchError} = await dispatchEffect({
|
|
14132
|
+
const {handler: handler, candidate: candidate, claimToken: claimToken, retry: retry, leaseMs: leaseMs, sleep: sleep, client: client, handlerClient: handlerClient, tag: tag, workflowResource: workflowResource, resourceClients: resourceClients, instanceId: instanceId, clientFor: clientFor, logger: logger, log: log, executionContext: executionContext, telemetry: telemetry, clock: clock} = args, attemptOnce = attempt => dispatchEffect({
|
|
13765
14133
|
handler: handler,
|
|
13766
14134
|
candidate: candidate,
|
|
13767
|
-
queue:
|
|
14135
|
+
queue: createEffectCommitQueue({
|
|
14136
|
+
effectKey: candidate._key,
|
|
14137
|
+
claimToken: claimToken,
|
|
14138
|
+
attempt: attempt,
|
|
14139
|
+
commit: async req => {
|
|
14140
|
+
await workflow.commitEffectOps({
|
|
14141
|
+
client: client,
|
|
14142
|
+
tag: tag,
|
|
14143
|
+
workflowResource: workflowResource,
|
|
14144
|
+
...resourceClients !== void 0 ? {
|
|
14145
|
+
resourceClients: resourceClients
|
|
14146
|
+
} : {},
|
|
14147
|
+
...executionContext !== void 0 ? {
|
|
14148
|
+
executionContext: executionContext
|
|
14149
|
+
} : {},
|
|
14150
|
+
...telemetry !== void 0 ? {
|
|
14151
|
+
telemetry: telemetry
|
|
14152
|
+
} : {},
|
|
14153
|
+
instanceId: instanceId,
|
|
14154
|
+
effectKey: candidate._key,
|
|
14155
|
+
claimToken: claimToken,
|
|
14156
|
+
ops: req.ops,
|
|
14157
|
+
idempotencyKey: req.idempotencyKey,
|
|
14158
|
+
leaseMs: leaseMs,
|
|
14159
|
+
clock: clock
|
|
14160
|
+
});
|
|
14161
|
+
}
|
|
14162
|
+
}),
|
|
13768
14163
|
ctx: {
|
|
13769
14164
|
client: handlerClient,
|
|
13770
14165
|
clientFor: clientFor,
|
|
13771
14166
|
instanceId: instanceId,
|
|
13772
14167
|
logger: logger
|
|
13773
14168
|
}
|
|
14169
|
+
}), claimed = {
|
|
14170
|
+
client: client,
|
|
14171
|
+
tag: tag,
|
|
14172
|
+
instanceId: instanceId,
|
|
14173
|
+
effectKey: candidate._key,
|
|
14174
|
+
claimToken: claimToken
|
|
14175
|
+
}, run = await runRetryPolicy({
|
|
14176
|
+
attemptOnce: attemptOnce,
|
|
14177
|
+
...retry !== void 0 ? {
|
|
14178
|
+
retry: retry
|
|
14179
|
+
} : {},
|
|
14180
|
+
clock: clock,
|
|
14181
|
+
sleep: sleep,
|
|
14182
|
+
holdThrough: holdMs => renewClaimLease({
|
|
14183
|
+
...claimed,
|
|
14184
|
+
holdMs: holdMs,
|
|
14185
|
+
clock: clock
|
|
14186
|
+
}),
|
|
14187
|
+
stillHeld: () => claimStillHeld(claimed),
|
|
14188
|
+
leaseMs: leaseMs
|
|
14189
|
+
});
|
|
14190
|
+
if (run.abandoned !== void 0) return log.warn(run.abandoned === "lease" ? `Effect "${candidate.name}" could not hold its claim between retry attempts — the run stopped with attempts left` : `Effect "${candidate.name}" was settled by another party during a retry backoff — the run stopped without dispatching again`, {
|
|
14191
|
+
effectKey: candidate._key,
|
|
14192
|
+
instanceId: instanceId
|
|
14193
|
+
}), "lost";
|
|
14194
|
+
const {outputs: outputs, ops: ops, dispatchError: dispatchError} = run.dispatch, detail = retry === void 0 ? void 0 : completionDetail({
|
|
14195
|
+
run: run,
|
|
14196
|
+
retry: retry
|
|
13774
14197
|
});
|
|
13775
14198
|
try {
|
|
13776
14199
|
await reportEffectOutcome({
|
|
@@ -13791,6 +14214,9 @@ async function dispatchAndReport(args) {
|
|
|
13791
14214
|
outputs: outputs,
|
|
13792
14215
|
ops: ops,
|
|
13793
14216
|
dispatchError: dispatchError,
|
|
14217
|
+
...detail !== void 0 ? {
|
|
14218
|
+
detail: detail
|
|
14219
|
+
} : {},
|
|
13794
14220
|
clock: clock
|
|
13795
14221
|
});
|
|
13796
14222
|
} catch (error) {
|
|
@@ -13803,6 +14229,71 @@ async function dispatchAndReport(args) {
|
|
|
13803
14229
|
return dispatchError === void 0 ? "drained" : "failed";
|
|
13804
14230
|
}
|
|
13805
14231
|
|
|
14232
|
+
async function runRetryPolicy(args) {
|
|
14233
|
+
const {attemptOnce: attemptOnce, retry: retry, clock: clock} = args, startedAt = Date.parse(clock()), elapsed = () => Date.parse(clock()) - startedAt;
|
|
14234
|
+
let attempt = 1, dispatch = await attemptOnce(attempt);
|
|
14235
|
+
for (;dispatch.dispatchError !== void 0 && retry !== void 0; ) {
|
|
14236
|
+
const at = {
|
|
14237
|
+
attempt: attempt,
|
|
14238
|
+
elapsedMs: elapsed()
|
|
14239
|
+
}, stop = await waitForNextAttempt({
|
|
14240
|
+
retry: retry,
|
|
14241
|
+
at: at,
|
|
14242
|
+
elapsed: elapsed,
|
|
14243
|
+
pace: args
|
|
14244
|
+
});
|
|
14245
|
+
if (stop !== void 0) return {
|
|
14246
|
+
...stop,
|
|
14247
|
+
dispatch: dispatch,
|
|
14248
|
+
at: at
|
|
14249
|
+
};
|
|
14250
|
+
attempt += 1, dispatch = await attemptOnce(attempt);
|
|
14251
|
+
}
|
|
14252
|
+
return {
|
|
14253
|
+
dispatch: dispatch,
|
|
14254
|
+
at: {
|
|
14255
|
+
attempt: attempt,
|
|
14256
|
+
elapsedMs: elapsed()
|
|
14257
|
+
},
|
|
14258
|
+
windowClosed: !1
|
|
14259
|
+
};
|
|
14260
|
+
}
|
|
14261
|
+
|
|
14262
|
+
async function waitForNextAttempt(args) {
|
|
14263
|
+
const {retry: retry, at: at, elapsed: elapsed} = args, {clock: clock, sleep: sleep, holdThrough: holdThrough, stillHeld: stillHeld, leaseMs: leaseMs} = args.pace, delay = nextRetryDelayMs(retry, at);
|
|
14264
|
+
if (delay === void 0) return {
|
|
14265
|
+
windowClosed: at.attempt < retry.attempts
|
|
14266
|
+
};
|
|
14267
|
+
const heldUntil = await holdThrough(delay + leaseMs);
|
|
14268
|
+
if (heldUntil === void 0 || hasPassed(heldUntil, addMs(clock(), delay))) return {
|
|
14269
|
+
windowClosed: !1,
|
|
14270
|
+
abandoned: "lease"
|
|
14271
|
+
};
|
|
14272
|
+
if (retryWindowClosed(retry, elapsed() + delay)) return {
|
|
14273
|
+
windowClosed: !0
|
|
14274
|
+
};
|
|
14275
|
+
if (await sleep(delay), !await stillHeld()) return {
|
|
14276
|
+
windowClosed: !1,
|
|
14277
|
+
abandoned: "settled"
|
|
14278
|
+
};
|
|
14279
|
+
if (retryWindowClosed(retry, elapsed())) return {
|
|
14280
|
+
windowClosed: !0
|
|
14281
|
+
};
|
|
14282
|
+
}
|
|
14283
|
+
|
|
14284
|
+
function completionDetail(args) {
|
|
14285
|
+
const {run: run, retry: retry} = args, {dispatchError: dispatchError} = run.dispatch;
|
|
14286
|
+
return dispatchError === void 0 ? retrySucceededDetail({
|
|
14287
|
+
at: run.at,
|
|
14288
|
+
attempts: retry.attempts
|
|
14289
|
+
}) : retryFailedDetail({
|
|
14290
|
+
at: run.at,
|
|
14291
|
+
attempts: retry.attempts,
|
|
14292
|
+
error: dispatchError,
|
|
14293
|
+
windowClosed: run.windowClosed
|
|
14294
|
+
});
|
|
14295
|
+
}
|
|
14296
|
+
|
|
13806
14297
|
async function reportEffectOutcome(args) {
|
|
13807
14298
|
const {outputs: outputs, ops: ops, dispatchError: dispatchError, resourceClients: resourceClients, ...rest} = args;
|
|
13808
14299
|
await workflow.completeEffect({
|
|
@@ -14093,6 +14584,9 @@ function createInstanceSession(args) {
|
|
|
14093
14584
|
definition: definitionOf(),
|
|
14094
14585
|
actor: actor,
|
|
14095
14586
|
snapshot: snapshotFrom(held, normalizedSelf),
|
|
14587
|
+
documentAvailability: documentAvailabilityReader({
|
|
14588
|
+
clientForGdr: evalScope.clientForGdr
|
|
14589
|
+
}),
|
|
14096
14590
|
guards: guards,
|
|
14097
14591
|
resourceGrants: resourceGrants,
|
|
14098
14592
|
guardDereference: clientGuardDereference(evalScope.client),
|
|
@@ -14506,6 +15000,9 @@ function createEngine(args) {
|
|
|
14506
15000
|
telemetry: telemetry,
|
|
14507
15001
|
...args.effects?.leaseMs !== void 0 ? {
|
|
14508
15002
|
leaseMs: args.effects.leaseMs
|
|
15003
|
+
} : {},
|
|
15004
|
+
...args.sleep !== void 0 ? {
|
|
15005
|
+
sleep: args.sleep
|
|
14509
15006
|
} : {}
|
|
14510
15007
|
}), drained = result.drained;
|
|
14511
15008
|
return (drained.length > 0 || emptyDrainDue()) && telemetry.log(WorkflowEffectsDrained, {
|
|
@@ -14527,27 +15024,43 @@ function createEngine(args) {
|
|
|
14527
15024
|
const CONSENT_TAG = "telemetry.consent", BATCH_TAG = "telemetry.batch";
|
|
14528
15025
|
|
|
14529
15026
|
function createTelemetryIntake(args) {
|
|
14530
|
-
const {client: client, projectId: projectId, denied: denied = !1} = args;
|
|
15027
|
+
const {client: client, projectId: projectId, denied: denied = !1, context: context} = args;
|
|
14531
15028
|
let cachedConsent;
|
|
14532
15029
|
return {
|
|
14533
15030
|
resolveConsent: async () => denied ? {
|
|
14534
15031
|
status: "denied"
|
|
14535
15032
|
} : (cachedConsent !== void 0 || (cachedConsent = await client.request({
|
|
14536
|
-
|
|
15033
|
+
url: "/intake/telemetry-status",
|
|
14537
15034
|
tag: CONSENT_TAG
|
|
14538
15035
|
})), cachedConsent),
|
|
14539
15036
|
sendEvents: batch => client.request({
|
|
14540
|
-
|
|
15037
|
+
url: "/intake/batch",
|
|
14541
15038
|
method: "POST",
|
|
14542
15039
|
body: {
|
|
14543
15040
|
projectId: projectId,
|
|
14544
|
-
batch: batch
|
|
15041
|
+
batch: context === void 0 ? batch : batch.map(event => withContext(event, context))
|
|
14545
15042
|
},
|
|
14546
15043
|
tag: BATCH_TAG
|
|
14547
15044
|
})
|
|
14548
15045
|
};
|
|
14549
15046
|
}
|
|
14550
15047
|
|
|
15048
|
+
function withContext(event, context) {
|
|
15049
|
+
if (!invariants.isRecord(event)) return event;
|
|
15050
|
+
const existing = invariants.isRecord(event.context) ? event.context : {};
|
|
15051
|
+
return {
|
|
15052
|
+
...event,
|
|
15053
|
+
context: {
|
|
15054
|
+
...existing,
|
|
15055
|
+
...context
|
|
15056
|
+
}
|
|
15057
|
+
};
|
|
15058
|
+
}
|
|
15059
|
+
|
|
15060
|
+
function _resolveTelemetryEnvironment(nodeEnv, fallback) {
|
|
15061
|
+
return nodeEnv === "production" ? "production" : nodeEnv === "development" || nodeEnv === "test" ? "development" : fallback;
|
|
15062
|
+
}
|
|
15063
|
+
|
|
14551
15064
|
function isTelemetryEnvDenied(env) {
|
|
14552
15065
|
return isTrueish(env.CI) || isTrueish(env.DO_NOT_TRACK);
|
|
14553
15066
|
}
|
|
@@ -14570,6 +15083,16 @@ function isTrueish(value) {
|
|
|
14570
15083
|
return normalized !== "0" && normalized !== "false";
|
|
14571
15084
|
}
|
|
14572
15085
|
|
|
15086
|
+
function summary(documents, deleted) {
|
|
15087
|
+
if (documents.length === 0) return "";
|
|
15088
|
+
const titles = [ ...new Set(documents.map(document => document.title ?? document.target.field)) ].map(title => `“${title}”`).join(", ");
|
|
15089
|
+
return documents.length === 1 ? `A document selected for ${titles} ${deleted ? "has been deleted" : "can’t be read by this workflow"}` : `${documents.length} documents selected for ${titles} ${deleted ? "have been deleted" : "can’t be read by this workflow"}`;
|
|
15090
|
+
}
|
|
15091
|
+
|
|
15092
|
+
function _missingDocumentsSummary(documents) {
|
|
15093
|
+
return [ summary(documents.filter(document => document.availability === "deleted"), !0), summary(documents.filter(document => document.availability !== "deleted"), !1) ].filter(Boolean).join(". ");
|
|
15094
|
+
}
|
|
15095
|
+
|
|
14573
15096
|
function groupSitesOf(definition) {
|
|
14574
15097
|
const sites = [], workflow2 = declarationIndex({
|
|
14575
15098
|
groups: definition.groups,
|
|
@@ -15463,6 +15986,16 @@ exports.WorkflowStageTransitioned = WorkflowStageTransitioned;
|
|
|
15463
15986
|
|
|
15464
15987
|
exports.WorkflowStateDivergedError = WorkflowStateDivergedError;
|
|
15465
15988
|
|
|
15989
|
+
exports._additionalMissingDocuments = _additionalMissingDocuments;
|
|
15990
|
+
|
|
15991
|
+
exports._fieldTargetLabel = _fieldTargetLabel;
|
|
15992
|
+
|
|
15993
|
+
exports._missingDocumentsSummary = _missingDocumentsSummary;
|
|
15994
|
+
|
|
15995
|
+
exports._requestProjectMembers = _requestProjectMembers;
|
|
15996
|
+
|
|
15997
|
+
exports._resolveTelemetryEnvironment = _resolveTelemetryEnvironment;
|
|
15998
|
+
|
|
15466
15999
|
exports.abortReason = abortReason;
|
|
15467
16000
|
|
|
15468
16001
|
exports.acceptsDocumentType = acceptsDocumentType;
|
|
@@ -15575,6 +16108,8 @@ exports.documentStuckCause = documentStuckCause;
|
|
|
15575
16108
|
|
|
15576
16109
|
exports.effectOutputsMap = effectOutputsMap;
|
|
15577
16110
|
|
|
16111
|
+
exports.effectSites = effectSites;
|
|
16112
|
+
|
|
15578
16113
|
exports.entryDocRefs = entryDocRefs;
|
|
15579
16114
|
|
|
15580
16115
|
exports.evaluateFromSnapshot = evaluateFromSnapshot;
|