@sanity/workflow-engine 0.18.0 → 0.19.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 +9 -0
- package/DATAMODEL.md +53 -0
- package/dist/_chunks-cjs/invariants.cjs +87 -17
- package/dist/_chunks-es/invariants.js +87 -17
- package/dist/define.d.cts +212 -4
- package/dist/define.d.ts +212 -4
- package/dist/index.cjs +2334 -1864
- package/dist/index.d.cts +455 -9
- package/dist/index.d.ts +455 -9
- package/dist/index.js +2313 -1855
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { terminalState, andConditions, deriveActivityKind, parseDefinitionSnapshot, CALLER_BOUND_VARS, START_ALLOWED_VARS, CONDITION_VARS, SUBJECT_HAS_IN_FLIGHT_INSTANCE_VAR, isSubjectEntry, isUnevaluable, ContractViolationError, isStartableDefinition, conditionFieldReadNames, rethrowWithContext, conditionParameterNames, isGdr, WORKFLOW_INSTANCE_TYPE, START_FILTER_VARS, gdrFromResource, selfGdr, isSingleDocRefKind, parentRef, actorFulfillsRole, toBareId, WorkflowError, sameResource, resourceFromParsed, tryParseGdr, isTerminalActivityStatus, errorMessage, validateFieldValue, validateFieldAppendItem, evaluateCondition, isSingleDocRefEntry, choiceValueIssues, scalarValidationIssues, StoredFieldOpSchema, formatIssues, conditionSyntaxIssues, checkWorkflowInvariants, formatIssuePath, isGuardReadExpr, conditionEffectReads, EFFECTS_READ, datasetResourceParts, evaluatePredicates, WORKFLOW_DEFINITION_TYPE, tagScopeFilter, isCascadeFired,
|
|
1
|
+
import { terminalState, andConditions, deriveActivityKind, parseDefinitionSnapshot, CALLER_BOUND_VARS, START_ALLOWED_VARS, CONDITION_VARS, SUBJECT_HAS_IN_FLIGHT_INSTANCE_VAR, isSubjectEntry, isUnevaluable, ContractViolationError, isStartableDefinition, conditionFieldReadNames, rethrowWithContext, conditionParameterNames, isGdr, WORKFLOW_INSTANCE_TYPE, START_FILTER_VARS, gdrFromResource, selfGdr, isSingleDocRefKind, parentRef, actorFulfillsRole, toBareId, WorkflowError, sameResource, resourceFromParsed, tryParseGdr, isTerminalActivityStatus, errorMessage, FieldValueShapeError, validateFieldValue, validateFieldAppendItem, evaluateCondition, isSingleDocRefEntry, choiceValueIssues, scalarValidationIssues, StoredFieldOpSchema, formatIssues, conditionSyntaxIssues, checkWorkflowInvariants, formatIssuePath, isGuardReadExpr, conditionEffectReads, EFFECTS_READ, datasetResourceParts, evaluatePredicates, WORKFLOW_DEFINITION_TYPE, tagScopeFilter, isCascadeFired, parseGdr, isGdrUri, gdrRef, isInputSourced, checkFieldValue, toPhysicalGdr, isBareSeedId, tolerantEntries, NonEmptyString, FIELD_VALUE_KINDS, tolerantObject, ActorShape, IsoTimestamp, ACTIVITY_STATUSES, GdrShape, DRIVER_KINDS, FIELD_SCOPES, fieldValueSchemas, parsePersistedDoc, assertReadableModel, InstanceNotFoundError, modelStampFor, evaluateConditionOutcome, runGroq, FIELD_READ, MUTATION_GUARD_ACTIONS, resourceGdr, minReaderModelOf, resourceFromGdrUri, refTypeIssues, rejectedRefTypes, DOCUMENT_VALUE_PERMISSIONS, driverKind, EffectNotFoundError, deriveExecutorClassification, validateTag, extractDocumentId, parseStoredDefinition, validateResourceAliasName, labelFor, DefinitionNotFoundError, definitionDocId, SpawnContractsInvalidError, gdrResourcePrefix, RESOURCE_ALIAS_NAME_SOURCE, isUnprimed, DefinitionInUseError, assertReaderModelAcknowledgement, isParseableInstant, groupMembershipNames } from "./_chunks-es/invariants.js";
|
|
2
2
|
|
|
3
3
|
import { ACTION_SEMANTICS, ACTIVITY_KINDS, ACTOR_KINDS, DATA_MODEL_CHANGES, DATA_MODEL_MIN_READER, DATA_MODEL_VERSION, DEFAULT_TRANSITION_WHEN, EXECUTOR_CLASSIFICATIONS, FILTER_SCOPE_VARS, GROUP_KINDS, GUARD_PREDICATE_VARS, ModelVersionAheadError, PersistedDocShapeError, READER_MODEL_ROLLOUT_URL, RESERVED_CONDITION_VARS, ReaderModelAcknowledgementError, clientConfigFromResource, fieldTreeShape, gdrUri, isNotesEntry, isTodoListEntry, isTodoListItem, modelVersionOf, parseResourceGdr, readsRootDocument, refCanvas, refDashboard, refDataset, refKindAcceptsTypes, refMediaLibrary, releaseDocId, releaseRef, requiredModelFeatures, requiredReaderModel, resourceAliasesToMap, schemaTreeShape, startKindOf } from "./_chunks-es/invariants.js";
|
|
4
4
|
|
|
@@ -248,6 +248,11 @@ const HIGH_FREQUENCY_SAMPLE_MS = 6e4, WorkflowDefinitionDeployed = defineWorkflo
|
|
|
248
248
|
name: "Editorial Workflows Effects Drained",
|
|
249
249
|
version: 1,
|
|
250
250
|
description: "A drain pass ran — drainedCount/drainedEffects report the successful dispatches. Unsampled when non-empty (outcome counts must be complete; volume is bounded by real effect work); empty polls are machine-cadence noise, throttled engine-side to at most one per minute"
|
|
251
|
+
}), WorkflowEffectStateReported = defineWorkflowEvent({
|
|
252
|
+
name: "Editorial Workflows Effect State Reported",
|
|
253
|
+
version: 1,
|
|
254
|
+
description: "A running effect handler committed mid-dispatch field state through commitEffectOps. Sampled (high-frequency by design — a dispatch may report many times): an adoption and usage signal, not an exact per-run report count",
|
|
255
|
+
maxSampleRate: HIGH_FREQUENCY_SAMPLE_MS
|
|
251
256
|
}), WorkflowEffectCompleted = defineWorkflowEvent({
|
|
252
257
|
name: "Editorial Workflows Effect Completed",
|
|
253
258
|
version: 1,
|
|
@@ -2219,6 +2224,21 @@ class StartNotSettledError extends WorkflowError {
|
|
|
2219
2224
|
}
|
|
2220
2225
|
}
|
|
2221
2226
|
|
|
2227
|
+
class StaleEffectClaimError extends WorkflowError {
|
|
2228
|
+
instanceId;
|
|
2229
|
+
effectKey;
|
|
2230
|
+
reason;
|
|
2231
|
+
constructor(args) {
|
|
2232
|
+
super("stale-effect-claim", `Mid-dispatch report for effect entry "${args.effectKey}" on ${args.instanceId} rejected: ${staleClaimDetail(args.reason)}. Nothing was written. Stop reporting from this dispatch — a newer dispatch (or none) owns the entry now.`),
|
|
2233
|
+
this.name = "StaleEffectClaimError", this.instanceId = args.instanceId, this.effectKey = args.effectKey,
|
|
2234
|
+
this.reason = args.reason;
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
|
|
2238
|
+
function staleClaimDetail(reason) {
|
|
2239
|
+
return reason === "unclaimed" ? "the entry holds no claim" : reason === "claim-superseded" ? "the claim was taken over by another dispatch" : "the claim's lease has expired";
|
|
2240
|
+
}
|
|
2241
|
+
|
|
2222
2242
|
const CONCURRENT_COMMIT_MAX_ATTEMPTS = 3;
|
|
2223
2243
|
|
|
2224
2244
|
function lostRaceMessage(args) {
|
|
@@ -2275,6 +2295,20 @@ class ConcurrentCompleteEffectError extends WorkflowError {
|
|
|
2275
2295
|
}
|
|
2276
2296
|
}
|
|
2277
2297
|
|
|
2298
|
+
class ConcurrentCommitEffectOpsError extends WorkflowError {
|
|
2299
|
+
instanceId;
|
|
2300
|
+
effectKey;
|
|
2301
|
+
attempts;
|
|
2302
|
+
constructor(args) {
|
|
2303
|
+
super("concurrent-commit-effect-ops", lostRaceMessage({
|
|
2304
|
+
what: `Mid-dispatch report for effect entry "${args.effectKey}"`,
|
|
2305
|
+
instanceId: args.instanceId,
|
|
2306
|
+
attempts: args.attempts
|
|
2307
|
+
})), this.name = "ConcurrentCommitEffectOpsError", this.instanceId = args.instanceId,
|
|
2308
|
+
this.effectKey = args.effectKey, this.attempts = args.attempts;
|
|
2309
|
+
}
|
|
2310
|
+
}
|
|
2311
|
+
|
|
2278
2312
|
class CascadeLimitError extends WorkflowError {
|
|
2279
2313
|
instanceId;
|
|
2280
2314
|
limit;
|
|
@@ -2609,8 +2643,17 @@ async function applyFieldUpdateWhere(op, ctx) {
|
|
|
2609
2643
|
fields: entry.of
|
|
2610
2644
|
}
|
|
2611
2645
|
});
|
|
2612
|
-
if (merge === null || typeof merge != "object" || Array.isArray(merge)) throw new
|
|
2613
|
-
|
|
2646
|
+
if (merge === null || typeof merge != "object" || Array.isArray(merge)) throw new FieldValueShapeError({
|
|
2647
|
+
entryType: entry._type,
|
|
2648
|
+
entryName: entry.name,
|
|
2649
|
+
mode: "value",
|
|
2650
|
+
issues: [ `field.updateWhere value must resolve to an object of fields to merge (target "${op.target.field}")` ]
|
|
2651
|
+
});
|
|
2652
|
+
requireNoReservedMergeKeys({
|
|
2653
|
+
merge: merge,
|
|
2654
|
+
entry: entry,
|
|
2655
|
+
op: op
|
|
2656
|
+
});
|
|
2614
2657
|
const matches = await rowMatches({
|
|
2615
2658
|
where: op.where,
|
|
2616
2659
|
rows: rows,
|
|
@@ -2656,9 +2699,14 @@ function requireMergeableRows(entry, op) {
|
|
|
2656
2699
|
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`);
|
|
2657
2700
|
}
|
|
2658
2701
|
|
|
2659
|
-
function requireNoReservedMergeKeys(
|
|
2660
|
-
const reserved = [ "_key", "_type" ].filter(key => key in merge);
|
|
2661
|
-
if (reserved.length !== 0) throw new
|
|
2702
|
+
function requireNoReservedMergeKeys(args) {
|
|
2703
|
+
const {merge: merge, entry: entry, op: op} = args, reserved = [ "_key", "_type" ].filter(key => key in merge);
|
|
2704
|
+
if (reserved.length !== 0) throw new FieldValueShapeError({
|
|
2705
|
+
entryType: entry._type,
|
|
2706
|
+
entryName: entry.name,
|
|
2707
|
+
mode: "value",
|
|
2708
|
+
issues: [ `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` ]
|
|
2709
|
+
});
|
|
2662
2710
|
}
|
|
2663
2711
|
|
|
2664
2712
|
function requireArrayValue(entry, op) {
|
|
@@ -3426,20 +3474,11 @@ function remediationsFor(diagnosis) {
|
|
|
3426
3474
|
}));
|
|
3427
3475
|
}
|
|
3428
3476
|
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
for (const [key, groq] of Object.entries(args.bindings ?? {})) resolved[key] = await runGroq({
|
|
3432
|
-
groq: groq,
|
|
3433
|
-
params: args.params,
|
|
3434
|
-
snapshot: args.snapshot
|
|
3435
|
-
});
|
|
3436
|
-
return {
|
|
3437
|
-
...resolved,
|
|
3438
|
-
...args.staticInput
|
|
3439
|
-
};
|
|
3477
|
+
function isClaimExpired(claim, now) {
|
|
3478
|
+
return claim.leaseExpiresAt === void 0 || hasPassed(claim.leaseExpiresAt, now);
|
|
3440
3479
|
}
|
|
3441
3480
|
|
|
3442
|
-
const EXECUTION_KINDS = {
|
|
3481
|
+
const EFFECT_RUN_STATUSES = [ "done", "failed", "cancelled" ], EXECUTION_KINDS = {
|
|
3443
3482
|
interactive: "interactive",
|
|
3444
3483
|
server: "server",
|
|
3445
3484
|
cli: "cli",
|
|
@@ -3998,7 +4037,7 @@ function coerceToGdr(raw, workflowResource) {
|
|
|
3998
4037
|
} : null;
|
|
3999
4038
|
}
|
|
4000
4039
|
|
|
4001
|
-
const
|
|
4040
|
+
const NonEmpty = NonEmptyString, UnknownRecord = v.record(v.string(), v.unknown()), PersistedChoiceOptionsSchema = v.looseObject({
|
|
4002
4041
|
list: v.array(v.looseObject({
|
|
4003
4042
|
title: v.string(),
|
|
4004
4043
|
value: v.union([ v.string(), v.number() ])
|
|
@@ -4058,7 +4097,7 @@ const OptionalRefTypes = v.exactOptional(v.array(v.string())), ResolvedFieldEntr
|
|
|
4058
4097
|
})), v.looseObject(tolerantEntries()({
|
|
4059
4098
|
...fieldArm("subject", fieldValueSchemas.subject),
|
|
4060
4099
|
types: OptionalRefTypes
|
|
4061
|
-
})), v.looseObject(tolerantEntries()(fieldArm("release.ref", fieldValueSchemas["release.ref"]))), v.looseObject(tolerantEntries()(fieldArm("string", fieldValueSchemas.string))), v.looseObject(tolerantEntries()(fieldArm("text", fieldValueSchemas.text))), v.looseObject(tolerantEntries()(fieldArm("number", fieldValueSchemas.number))), v.looseObject(tolerantEntries()(fieldArm("boolean", fieldValueSchemas.boolean))), v.looseObject(tolerantEntries()(fieldArm("date", fieldValueSchemas.date))), v.looseObject(tolerantEntries()(fieldArm("datetime", fieldValueSchemas.datetime))), v.looseObject(tolerantEntries()(fieldArm("url", fieldValueSchemas.url))), v.looseObject(tolerantEntries()(fieldArm("actor", fieldValueSchemas.actor))), v.looseObject(tolerantEntries()(fieldArm("assignee", fieldValueSchemas.assignee))), v.looseObject(tolerantEntries()(fieldArm("assignees", fieldValueSchemas.assignees))), v.looseObject(tolerantEntries()({
|
|
4100
|
+
})), v.looseObject(tolerantEntries()(fieldArm("release.ref", fieldValueSchemas["release.ref"]))), v.looseObject(tolerantEntries()(fieldArm("string", fieldValueSchemas.string))), v.looseObject(tolerantEntries()(fieldArm("text", fieldValueSchemas.text))), v.looseObject(tolerantEntries()(fieldArm("number", fieldValueSchemas.number))), v.looseObject(tolerantEntries()(fieldArm("progress", fieldValueSchemas.progress))), v.looseObject(tolerantEntries()(fieldArm("boolean", fieldValueSchemas.boolean))), v.looseObject(tolerantEntries()(fieldArm("date", fieldValueSchemas.date))), v.looseObject(tolerantEntries()(fieldArm("datetime", fieldValueSchemas.datetime))), v.looseObject(tolerantEntries()(fieldArm("url", fieldValueSchemas.url))), v.looseObject(tolerantEntries()(fieldArm("actor", fieldValueSchemas.actor))), v.looseObject(tolerantEntries()(fieldArm("assignee", fieldValueSchemas.assignee))), v.looseObject(tolerantEntries()(fieldArm("assignees", fieldValueSchemas.assignees))), v.looseObject(tolerantEntries()({
|
|
4062
4101
|
...fieldArm("object", v.union([ v.null(), UnknownRecord ])),
|
|
4063
4102
|
fields: v.array(PersistedFieldShapeSchema)
|
|
4064
4103
|
})), v.looseObject(tolerantEntries()({
|
|
@@ -4071,7 +4110,8 @@ const OptionalRefTypes = v.exactOptional(v.array(v.string())), ResolvedFieldEntr
|
|
|
4071
4110
|
_type: v.literal("pendingEffect.claim"),
|
|
4072
4111
|
claimedAt: IsoTimestamp,
|
|
4073
4112
|
claimedBy: ActorShape,
|
|
4074
|
-
leaseExpiresAt: v.exactOptional(IsoTimestamp)
|
|
4113
|
+
leaseExpiresAt: v.exactOptional(IsoTimestamp),
|
|
4114
|
+
claimToken: v.exactOptional(NonEmpty)
|
|
4075
4115
|
}), PendingEffectSchema = tolerantObject()({
|
|
4076
4116
|
_key: NonEmpty,
|
|
4077
4117
|
_type: v.exactOptional(v.literal("pendingEffect")),
|
|
@@ -4487,6 +4527,10 @@ function collectEntryDocUris(resolvedFieldEntries) {
|
|
|
4487
4527
|
return entryDocRefs(resolvedFieldEntries).map(ref => ref.id);
|
|
4488
4528
|
}
|
|
4489
4529
|
|
|
4530
|
+
function isEngineContext(ctx) {
|
|
4531
|
+
return "client" in ctx;
|
|
4532
|
+
}
|
|
4533
|
+
|
|
4490
4534
|
function loadCallContext({client: client, instanceId: instanceId, options: options}) {
|
|
4491
4535
|
return loadContext({
|
|
4492
4536
|
client: client,
|
|
@@ -4662,6 +4706,19 @@ async function resolveActivityFieldEntries(args) {
|
|
|
4662
4706
|
});
|
|
4663
4707
|
}
|
|
4664
4708
|
|
|
4709
|
+
async function resolveBindings(args) {
|
|
4710
|
+
const resolved = {};
|
|
4711
|
+
for (const [key, groq] of Object.entries(args.bindings ?? {})) resolved[key] = await runGroq({
|
|
4712
|
+
groq: groq,
|
|
4713
|
+
params: args.params,
|
|
4714
|
+
snapshot: args.snapshot
|
|
4715
|
+
});
|
|
4716
|
+
return {
|
|
4717
|
+
...resolved,
|
|
4718
|
+
...args.staticInput
|
|
4719
|
+
};
|
|
4720
|
+
}
|
|
4721
|
+
|
|
4665
4722
|
const DEFAULT_IDEMPOTENCY_TTL_MS = 1440 * 60 * 1e3;
|
|
4666
4723
|
|
|
4667
4724
|
class RequestAlreadyProcessedError extends Error {
|
|
@@ -4836,6 +4893,7 @@ const REQUEST_TAG = {
|
|
|
4836
4893
|
fireAction: "workflow.fire-action",
|
|
4837
4894
|
editField: "workflow.edit-field",
|
|
4838
4895
|
completeEffect: "workflow.complete-effect",
|
|
4896
|
+
commitEffectOps: "workflow.commit-effect-ops",
|
|
4839
4897
|
tick: "workflow.tick",
|
|
4840
4898
|
evaluate: "workflow.evaluate",
|
|
4841
4899
|
evaluateStart: "workflow.evaluate-start",
|
|
@@ -6104,1965 +6162,2134 @@ async function fetchGrants(args) {
|
|
|
6104
6162
|
});
|
|
6105
6163
|
}
|
|
6106
6164
|
|
|
6107
|
-
|
|
6108
|
-
|
|
6109
|
-
|
|
6110
|
-
const
|
|
6111
|
-
|
|
6112
|
-
|
|
6113
|
-
|
|
6114
|
-
|
|
6115
|
-
resourcePath: args.grantsFromPath
|
|
6116
|
-
}) : Promise.resolve(void 0), [actor, grants] = await Promise.all([ cachedActor(client, requestFn), grantsPromise ]);
|
|
6117
|
-
if (actor === void 0) throw new ContractViolationError("workflow: failed to resolve actor from `/users/me`. The client is configured but the endpoint returned no usable identity — check the token.");
|
|
6118
|
-
return {
|
|
6119
|
-
actor: actor,
|
|
6120
|
-
...grants !== void 0 ? {
|
|
6121
|
-
grants: grants
|
|
6122
|
-
} : {}
|
|
6123
|
-
};
|
|
6124
|
-
}
|
|
6125
|
-
|
|
6126
|
-
function cachedActor(client, requestFn) {
|
|
6127
|
-
const cached = actorCache.get(client);
|
|
6128
|
-
if (cached !== void 0) return cached;
|
|
6129
|
-
const pending = fetchActor(requestFn).catch(err => {
|
|
6130
|
-
throw actorCache.get(client) === pending && actorCache.delete(client), err;
|
|
6131
|
-
});
|
|
6132
|
-
return actorCache.set(client, pending), pending;
|
|
6133
|
-
}
|
|
6134
|
-
|
|
6135
|
-
function grantsForClientPath(taggedClient, resourcePath) {
|
|
6136
|
-
const client = unwrapRequestTag(taggedClient), requestFn = lazyRequest(client);
|
|
6137
|
-
return requestFn === void 0 ? Promise.resolve(void 0) : cachedGrants({
|
|
6138
|
-
client: client,
|
|
6139
|
-
requestFn: requestFn,
|
|
6140
|
-
resourcePath: resourcePath
|
|
6165
|
+
async function advisoryCan({instance: instance, actor: actor, grants: grants}) {
|
|
6166
|
+
if (grants === void 0) return;
|
|
6167
|
+
const can = {};
|
|
6168
|
+
for (const permission of DOCUMENT_VALUE_PERMISSIONS) can[permission] = await grantsPermissionOn({
|
|
6169
|
+
document: instance,
|
|
6170
|
+
grants: grants,
|
|
6171
|
+
permission: permission,
|
|
6172
|
+
userId: actor.id
|
|
6141
6173
|
});
|
|
6174
|
+
return can;
|
|
6142
6175
|
}
|
|
6143
6176
|
|
|
6144
|
-
function
|
|
6145
|
-
return
|
|
6146
|
-
}
|
|
6147
|
-
|
|
6148
|
-
function cachedGrants({client: client, requestFn: requestFn, resourcePath: resourcePath}) {
|
|
6149
|
-
let byPath = grantsCache.get(client);
|
|
6150
|
-
byPath === void 0 && (byPath = /* @__PURE__ */ new Map, grantsCache.set(client, byPath));
|
|
6151
|
-
let cached = byPath.get(resourcePath);
|
|
6152
|
-
return cached === void 0 && (cached = fetchGrantsCached(requestFn, resourcePath),
|
|
6153
|
-
byPath.set(resourcePath, cached)), cached;
|
|
6177
|
+
function subjectDenialLabels(denied) {
|
|
6178
|
+
return denied.map(d => `${d.permission} on ${d.subject} (${d.resource})`);
|
|
6154
6179
|
}
|
|
6155
6180
|
|
|
6156
|
-
|
|
6157
|
-
|
|
6158
|
-
|
|
6159
|
-
|
|
6160
|
-
|
|
6161
|
-
|
|
6162
|
-
|
|
6163
|
-
|
|
6164
|
-
|
|
6165
|
-
|
|
6166
|
-
|
|
6181
|
+
class ActionDisabledError extends WorkflowError {
|
|
6182
|
+
reason;
|
|
6183
|
+
activity;
|
|
6184
|
+
action;
|
|
6185
|
+
constructor(args) {
|
|
6186
|
+
super("action-disabled", formatDisabledReason({
|
|
6187
|
+
activity: args.activity,
|
|
6188
|
+
action: args.action,
|
|
6189
|
+
reason: args.reason
|
|
6190
|
+
})), this.name = "ActionDisabledError", this.reason = args.reason, this.activity = args.activity,
|
|
6191
|
+
this.action = args.action;
|
|
6167
6192
|
}
|
|
6168
|
-
if (!user || typeof user.id != "string" || user.id.length === 0) return;
|
|
6169
|
-
const roleNames = user.roles?.map(r => r.name).filter(n => !!n) ?? [];
|
|
6170
|
-
return {
|
|
6171
|
-
kind: "person",
|
|
6172
|
-
id: user.id,
|
|
6173
|
-
...roleNames.length > 0 ? {
|
|
6174
|
-
roles: roleNames
|
|
6175
|
-
} : {}
|
|
6176
|
-
};
|
|
6177
6193
|
}
|
|
6178
6194
|
|
|
6179
|
-
|
|
6180
|
-
|
|
6181
|
-
|
|
6182
|
-
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
resourcePath: resourcePath
|
|
6186
|
-
});
|
|
6187
|
-
} catch (err) {
|
|
6188
|
-
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: ${errorMessage(err)}`);
|
|
6189
|
-
return;
|
|
6195
|
+
class StartNotAllowedError extends WorkflowError {
|
|
6196
|
+
definition;
|
|
6197
|
+
insight;
|
|
6198
|
+
constructor(args) {
|
|
6199
|
+
super("start-not-allowed", `startInstance refused: start.allowed on definition "${args.definition}" evaluated ` + (args.insight.outcome === "unevaluable" ? `GROQ null ("can't decide" — fail-closed)` : "false") + " for the supplied initialFields. Pre-flight the verdict with evaluateStart."),
|
|
6200
|
+
this.name = "StartNotAllowedError", this.definition = args.definition, this.insight = args.insight;
|
|
6190
6201
|
}
|
|
6191
6202
|
}
|
|
6192
6203
|
|
|
6193
|
-
|
|
6194
|
-
const
|
|
6195
|
-
|
|
6196
|
-
const involved = sites.filter(entry => readsField(entry.insight, field));
|
|
6197
|
-
insights.push({
|
|
6198
|
-
field: field,
|
|
6199
|
-
reads: dedupeReads(involved.flatMap(entry => fieldReads(entry.insight, field))),
|
|
6200
|
-
involvedIn: involved.map(entry => entry.site),
|
|
6201
|
-
proposals: await verifyProposals({
|
|
6202
|
-
field: field,
|
|
6203
|
-
involved: involved,
|
|
6204
|
-
snapshot: snapshot
|
|
6205
|
-
})
|
|
6206
|
-
});
|
|
6207
|
-
}
|
|
6208
|
-
return insights;
|
|
6204
|
+
function actionRendering(action) {
|
|
6205
|
+
const kind = action.disabledReason?.kind;
|
|
6206
|
+
return kind === "filter-failed" ? "absent" : action.triggered === !0 || kind === "cascade-fired" ? "automation" : "button";
|
|
6209
6207
|
}
|
|
6210
6208
|
|
|
6211
|
-
|
|
6212
|
-
|
|
6213
|
-
|
|
6214
|
-
}
|
|
6209
|
+
const disabledReasonDetail = {
|
|
6210
|
+
"filter-failed": r => `action filter returned false${r.detail ? ` (${r.detail})` : ""}`,
|
|
6211
|
+
"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`,
|
|
6212
|
+
"activity-not-active": r => `activity status is "${r.status}"`,
|
|
6213
|
+
"stage-terminal": r => `stage "${r.stage}" is terminal`,
|
|
6214
|
+
"instance-completed": r => `instance completed at ${r.completedAt}`,
|
|
6215
|
+
"instance-aborted": r => `instance aborted at ${r.abortedAt}`,
|
|
6216
|
+
"requirements-unmet": r => `unmet requirement(s): ${r.unmetRequirements.join(", ")}`,
|
|
6217
|
+
"subject-permission-denied": r => `missing subject permission(s): ${subjectDenialLabels(r.denied).join(", ")}`
|
|
6218
|
+
};
|
|
6215
6219
|
|
|
6216
|
-
function
|
|
6217
|
-
return
|
|
6220
|
+
function actionDisabledDetail(reason) {
|
|
6221
|
+
return disabledReasonDetail[reason.kind](reason);
|
|
6218
6222
|
}
|
|
6219
6223
|
|
|
6220
|
-
function
|
|
6221
|
-
return
|
|
6224
|
+
function formatDisabledReason({activity: activity, action: action, reason: reason}) {
|
|
6225
|
+
return `Action "${activity}:${action}" is not allowed: ${actionDisabledDetail(reason)}`;
|
|
6222
6226
|
}
|
|
6223
6227
|
|
|
6224
|
-
|
|
6225
|
-
|
|
6226
|
-
|
|
6227
|
-
|
|
6228
|
-
|
|
6229
|
-
|
|
6230
|
-
condition: entry.condition,
|
|
6231
|
-
dataset: snapshot.docs,
|
|
6232
|
-
params: entry.params,
|
|
6233
|
-
assign: assign
|
|
6234
|
-
});
|
|
6235
|
-
result.changed && consequences.push({
|
|
6236
|
-
site: entry.site,
|
|
6237
|
-
before: result.before,
|
|
6238
|
-
after: result.after
|
|
6239
|
-
});
|
|
6240
|
-
}
|
|
6241
|
-
consequences.length > 0 && proposals.push({
|
|
6242
|
-
assign: assign,
|
|
6243
|
-
consequences: consequences
|
|
6244
|
-
});
|
|
6228
|
+
class EditFieldDeniedError extends WorkflowError {
|
|
6229
|
+
reason;
|
|
6230
|
+
target;
|
|
6231
|
+
constructor(args) {
|
|
6232
|
+
super("edit-field-denied", formatEditDisabledReason(args.target, args.reason)),
|
|
6233
|
+
this.name = "EditFieldDeniedError", this.reason = args.reason, this.target = args.target;
|
|
6245
6234
|
}
|
|
6246
|
-
return proposals;
|
|
6247
6235
|
}
|
|
6248
6236
|
|
|
6249
|
-
|
|
6250
|
-
|
|
6251
|
-
|
|
6252
|
-
}
|
|
6237
|
+
const editDisabledReasonDetail = {
|
|
6238
|
+
"not-editable": () => "field is not declared editable",
|
|
6239
|
+
"instance-completed": r => `instance completed at ${r.completedAt}`,
|
|
6240
|
+
"instance-aborted": r => `instance aborted at ${r.abortedAt}`,
|
|
6241
|
+
"edit-window-closed": r => `edit window closed (${r.detail})`,
|
|
6242
|
+
"editor-not-permitted": r => `editor not permitted (${r.predicate})`
|
|
6243
|
+
};
|
|
6253
6244
|
|
|
6254
|
-
function
|
|
6255
|
-
|
|
6256
|
-
|
|
6257
|
-
|
|
6258
|
-
target: requirement.target,
|
|
6259
|
-
value: requirement.value
|
|
6260
|
-
} ];
|
|
6245
|
+
function formatEditDisabledReason(target, reason) {
|
|
6246
|
+
const detail = editDisabledReasonDetail[reason.kind](reason), where = target.activity !== void 0 ? `${target.activity}.${target.field}` : target.field;
|
|
6247
|
+
return `Field "${target.scope}:${where}" is not editable: ${detail}`;
|
|
6248
|
+
}
|
|
6261
6249
|
|
|
6262
|
-
|
|
6263
|
-
|
|
6264
|
-
|
|
6265
|
-
|
|
6266
|
-
|
|
6267
|
-
|
|
6268
|
-
|
|
6269
|
-
|
|
6270
|
-
|
|
6271
|
-
|
|
6272
|
-
|
|
6273
|
-
|
|
6274
|
-
|
|
6275
|
-
|
|
6276
|
-
|
|
6277
|
-
|
|
6278
|
-
|
|
6279
|
-
|
|
6280
|
-
|
|
6281
|
-
|
|
6282
|
-
|
|
6283
|
-
return assignment.target.path.every(segment => typeof segment != "number" || segment <= MAX_COUNTERFACTUAL_INDEX);
|
|
6250
|
+
async function fireAction(args) {
|
|
6251
|
+
const {client: client, instanceId: instanceId, activity: activity, action: action, params: params, requestRecord: requestRecord, options: options} = args;
|
|
6252
|
+
return retryOnRevisionConflict({
|
|
6253
|
+
client: client,
|
|
6254
|
+
instanceId: instanceId,
|
|
6255
|
+
options: options,
|
|
6256
|
+
commit: ctx => commitAction({
|
|
6257
|
+
ctx: ctx,
|
|
6258
|
+
activityName: activity,
|
|
6259
|
+
actionName: action,
|
|
6260
|
+
callerParams: params,
|
|
6261
|
+
requestRecord: requestRecord,
|
|
6262
|
+
options: options
|
|
6263
|
+
}),
|
|
6264
|
+
onExhausted: () => new ConcurrentFireActionError({
|
|
6265
|
+
instanceId: instanceId,
|
|
6266
|
+
activity: activity,
|
|
6267
|
+
action: action,
|
|
6268
|
+
attempts: CONCURRENT_COMMIT_MAX_ATTEMPTS
|
|
6269
|
+
})
|
|
6270
|
+
});
|
|
6284
6271
|
}
|
|
6285
6272
|
|
|
6286
|
-
async function
|
|
6287
|
-
const {
|
|
6288
|
-
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
6273
|
+
async function resolveActionCommit({ctx: ctx, activityName: activityName, actionName: actionName, callerParams: callerParams, options: options}) {
|
|
6274
|
+
const actor = options?.actor, {stage: stage, activity: activity} = findActivityInCurrentStage(ctx, activityName), action = (activity.actions ?? []).find(a => a.name === actionName);
|
|
6275
|
+
if (action === void 0) throw new ContractViolationError(`Action "${actionName}" not declared on activity "${activityName}"`);
|
|
6276
|
+
if (action.when !== void 0) throw new ActionDisabledError({
|
|
6277
|
+
activity: activityName,
|
|
6278
|
+
action: actionName,
|
|
6279
|
+
reason: {
|
|
6280
|
+
kind: "cascade-fired",
|
|
6281
|
+
when: action.when
|
|
6282
|
+
}
|
|
6283
|
+
});
|
|
6284
|
+
if (action.filter !== void 0) {
|
|
6285
|
+
const can = options?.grants !== void 0 && actor !== void 0 ? await advisoryCan({
|
|
6286
|
+
instance: ctx.instance,
|
|
6287
|
+
actor: actor,
|
|
6288
|
+
grants: options.grants
|
|
6289
|
+
}) : void 0;
|
|
6290
|
+
if (!await ctxEvaluateCondition({
|
|
6291
|
+
ctx: ctx,
|
|
6292
|
+
condition: action.filter,
|
|
6293
|
+
opts: {
|
|
6294
|
+
activityName: activityName,
|
|
6295
|
+
...actor !== void 0 ? {
|
|
6296
|
+
actor: actor
|
|
6297
|
+
} : {},
|
|
6298
|
+
...can !== void 0 ? {
|
|
6299
|
+
vars: {
|
|
6300
|
+
can: can
|
|
6301
|
+
}
|
|
6302
|
+
} : {}
|
|
6303
|
+
}
|
|
6304
|
+
})) throw new ActionDisabledError({
|
|
6305
|
+
activity: activityName,
|
|
6306
|
+
action: actionName,
|
|
6307
|
+
reason: {
|
|
6308
|
+
kind: "filter-failed",
|
|
6309
|
+
filter: action.filter,
|
|
6310
|
+
detail: "commit re-check"
|
|
6311
|
+
}
|
|
6293
6312
|
});
|
|
6294
6313
|
}
|
|
6295
|
-
const
|
|
6296
|
-
|
|
6297
|
-
|
|
6298
|
-
|
|
6299
|
-
|
|
6300
|
-
}));
|
|
6301
|
-
return new Map(resolved.filter(entry => entry !== void 0));
|
|
6302
|
-
}
|
|
6303
|
-
|
|
6304
|
-
async function evaluateInstance(args) {
|
|
6305
|
-
const {client: client, tag: tag, workflowResource: workflowResource, instanceId: instanceId, resourceClients: resourceClients} = args, now = (args.clock ?? wallClock)();
|
|
6306
|
-
validateTag(tag);
|
|
6307
|
-
const {actor: actor, grants: grants} = await resolveAccess(client, {
|
|
6308
|
-
...args.grantsFromPath !== void 0 ? {
|
|
6309
|
-
grantsFromPath: args.grantsFromPath
|
|
6310
|
-
} : {}
|
|
6311
|
-
}), instance = await reload({
|
|
6312
|
-
client: client,
|
|
6313
|
-
instanceId: instanceId,
|
|
6314
|
-
tag: tag
|
|
6315
|
-
}), definition = parseDefinitionSnapshot(instance), clientForGdr = buildClientForGdr({
|
|
6316
|
-
client: client,
|
|
6317
|
-
workflowResource: workflowResource,
|
|
6318
|
-
resourceClients: resourceClients
|
|
6319
|
-
}), snapshot = await hydrateSnapshot({
|
|
6320
|
-
client: client,
|
|
6321
|
-
clientForGdr: clientForGdr,
|
|
6322
|
-
instance: instance
|
|
6323
|
-
}), guards = await verdictGuardsForInstance(client, instance._id), resourceGrants = await subjectResourceGrants({
|
|
6324
|
-
clientForGdr: clientForGdr,
|
|
6325
|
-
instance: instance
|
|
6314
|
+
const entry = findCurrentActivities(ctx.instance).find(t => t.name === activityName);
|
|
6315
|
+
if (entry === void 0 || entry.status !== "active") throw notActiveAtCommit({
|
|
6316
|
+
activityName: activityName,
|
|
6317
|
+
actionName: actionName,
|
|
6318
|
+
status: entry?.status
|
|
6326
6319
|
});
|
|
6327
|
-
|
|
6328
|
-
|
|
6329
|
-
|
|
6330
|
-
|
|
6331
|
-
snapshot: snapshot,
|
|
6332
|
-
guards: guards,
|
|
6333
|
-
now: now,
|
|
6334
|
-
resourceGrants: resourceGrants,
|
|
6335
|
-
...grants !== void 0 ? {
|
|
6336
|
-
grants: grants
|
|
6337
|
-
} : {}
|
|
6320
|
+
const params = validateActionParams({
|
|
6321
|
+
action: action,
|
|
6322
|
+
activityName: activityName,
|
|
6323
|
+
callerParams: callerParams
|
|
6338
6324
|
});
|
|
6339
|
-
|
|
6340
|
-
|
|
6341
|
-
|
|
6342
|
-
|
|
6343
|
-
|
|
6344
|
-
const hit = rendered.get(activityName);
|
|
6345
|
-
if (hit !== void 0) return hit;
|
|
6346
|
-
const scope = render(activityName);
|
|
6347
|
-
return rendered.set(activityName, scope), scope;
|
|
6325
|
+
return {
|
|
6326
|
+
stage: stage,
|
|
6327
|
+
activity: activity,
|
|
6328
|
+
action: action,
|
|
6329
|
+
params: params
|
|
6348
6330
|
};
|
|
6349
6331
|
}
|
|
6350
6332
|
|
|
6351
|
-
function
|
|
6352
|
-
|
|
6353
|
-
|
|
6354
|
-
|
|
6355
|
-
|
|
6356
|
-
|
|
6357
|
-
|
|
6358
|
-
|
|
6333
|
+
function notActiveAtCommit(args) {
|
|
6334
|
+
const {activityName: activityName, actionName: actionName, status: status} = args;
|
|
6335
|
+
return status !== void 0 && isTerminalActivityStatus(status) ? new ActionDisabledError({
|
|
6336
|
+
activity: activityName,
|
|
6337
|
+
action: actionName,
|
|
6338
|
+
reason: {
|
|
6339
|
+
kind: "activity-not-active",
|
|
6340
|
+
status: status
|
|
6341
|
+
}
|
|
6342
|
+
}) : new Error(`Activity "${activityName}" must be active to fire action "${actionName}"; status is ${status ?? "missing"}`);
|
|
6359
6343
|
}
|
|
6360
6344
|
|
|
6361
|
-
async function
|
|
6362
|
-
|
|
6363
|
-
|
|
6364
|
-
|
|
6365
|
-
|
|
6345
|
+
async function applyActionFire({ctx: ctx, mutation: mutation, activity: activity, action: action, params: params, actor: actor, triggered: triggered}) {
|
|
6346
|
+
mutation.history.push({
|
|
6347
|
+
_key: randomKey(),
|
|
6348
|
+
_type: "actionFired",
|
|
6349
|
+
at: ctx.now,
|
|
6350
|
+
stage: mutation.currentStage,
|
|
6351
|
+
activity: activity.name,
|
|
6352
|
+
action: action.name,
|
|
6353
|
+
...actor !== void 0 ? {
|
|
6354
|
+
actor: actor,
|
|
6355
|
+
driverKind: driverKind(actor)
|
|
6356
|
+
} : {},
|
|
6357
|
+
...triggered ? {
|
|
6358
|
+
triggered: !0
|
|
6359
|
+
} : {}
|
|
6366
6360
|
});
|
|
6367
|
-
|
|
6368
|
-
|
|
6369
|
-
|
|
6361
|
+
const ranOps = await runOps({
|
|
6362
|
+
ops: action.ops,
|
|
6363
|
+
mutation: mutation,
|
|
6364
|
+
stage: mutation.currentStage,
|
|
6365
|
+
origin: {
|
|
6366
|
+
activity: activity.name,
|
|
6367
|
+
action: action.name
|
|
6368
|
+
},
|
|
6370
6369
|
params: params,
|
|
6371
|
-
insight: insight
|
|
6372
|
-
}), insight;
|
|
6373
|
-
}
|
|
6374
|
-
|
|
6375
|
-
async function evaluateFromSnapshot(args) {
|
|
6376
|
-
const {instance: instance, definition: definition, actor: actor, grants: grants, snapshot: snapshot} = args, now = args.now ?? wallClock(), stage = currentStageOf(instance, definition), autonomy = autonomyOf(definition), stageAutonomy2 = stageAutonomyOf(autonomy, stage.name), scopeSource = {
|
|
6377
|
-
instance: instance,
|
|
6378
|
-
definition: definition,
|
|
6379
|
-
snapshot: snapshot,
|
|
6380
|
-
now: now
|
|
6381
|
-
}, can = await advisoryCan({
|
|
6382
|
-
instance: instance,
|
|
6383
6370
|
actor: actor,
|
|
6384
|
-
|
|
6385
|
-
|
|
6371
|
+
self: selfGdr(ctx.instance),
|
|
6372
|
+
now: ctx.now,
|
|
6373
|
+
snapshot: ctx.snapshot,
|
|
6374
|
+
refSurface: ctx.refSurface
|
|
6375
|
+
});
|
|
6376
|
+
if (await queueEffects({
|
|
6377
|
+
ctx: ctx,
|
|
6378
|
+
mutation: mutation,
|
|
6379
|
+
effects: action.effects,
|
|
6380
|
+
origin: {
|
|
6381
|
+
kind: "action",
|
|
6382
|
+
name: action.name
|
|
6383
|
+
},
|
|
6386
6384
|
actor: actor,
|
|
6387
|
-
|
|
6388
|
-
|
|
6385
|
+
opts: {
|
|
6386
|
+
callerParams: params,
|
|
6387
|
+
activityName: activity.name
|
|
6389
6388
|
}
|
|
6390
|
-
}),
|
|
6389
|
+
}), action.spawn !== void 0) {
|
|
6390
|
+
if (!isEngineContext(ctx)) throw new ContractViolationError(`Action "${action.name}" declares spawn, which requires a lake-capable engine context`);
|
|
6391
|
+
await spawnSubworkflows({
|
|
6392
|
+
ctx: ctx,
|
|
6393
|
+
mutation: mutation,
|
|
6394
|
+
activity: activity,
|
|
6395
|
+
action: action,
|
|
6396
|
+
sub: action.spawn,
|
|
6397
|
+
actor: actor
|
|
6398
|
+
});
|
|
6399
|
+
}
|
|
6400
|
+
return {
|
|
6401
|
+
ranOps: ranOps
|
|
6402
|
+
};
|
|
6403
|
+
}
|
|
6404
|
+
|
|
6405
|
+
async function commitAction({ctx: ctx, activityName: activityName, actionName: actionName, callerParams: callerParams, requestRecord: requestRecord, options: options}) {
|
|
6406
|
+
assertRequestUnprocessed({
|
|
6407
|
+
instance: ctx.instance,
|
|
6408
|
+
record: requestRecord,
|
|
6409
|
+
now: ctx.now
|
|
6410
|
+
});
|
|
6411
|
+
const actor = options?.actor, {stage: stage, activity: activity, action: action, params: params} = await resolveActionCommit({
|
|
6412
|
+
ctx: ctx,
|
|
6391
6413
|
activityName: activityName,
|
|
6392
|
-
|
|
6393
|
-
|
|
6394
|
-
|
|
6395
|
-
|
|
6396
|
-
|
|
6397
|
-
|
|
6398
|
-
|
|
6399
|
-
|
|
6400
|
-
actor: actor,
|
|
6401
|
-
guards: args.guards
|
|
6402
|
-
}), subjectDenials = await forecastSubjectDenials({
|
|
6403
|
-
instance: instance,
|
|
6404
|
-
actor: actor,
|
|
6405
|
-
snapshot: snapshot,
|
|
6406
|
-
resourceGrants: args.resourceGrants
|
|
6407
|
-
}), subjectPermissionReason = subjectDenials.length > 0 ? {
|
|
6408
|
-
kind: "subject-permission-denied",
|
|
6409
|
-
denied: subjectDenials
|
|
6410
|
-
} : void 0, sites = [], activityEvaluations = [];
|
|
6411
|
-
for (const activity of stage.activities ?? []) activityEvaluations.push(await evaluateActivity({
|
|
6412
|
-
activity: activity,
|
|
6413
|
-
statusEntry: currentActivityEntries.find(t => t.name === activity.name),
|
|
6414
|
-
instance: instance,
|
|
6415
|
-
snapshot: snapshot,
|
|
6416
|
-
activityScope: await scopeForActivity(activity.name),
|
|
6417
|
-
cascadeActivityScope: () => cascadeScopeForActivity(activity.name),
|
|
6418
|
-
stageHasExits: !isTerminalStage(stage),
|
|
6419
|
-
guardDenial: guardDenial,
|
|
6420
|
-
subjectPermissionReason: subjectPermissionReason,
|
|
6421
|
-
sites: sites,
|
|
6422
|
-
autonomy: activityAutonomyOf(stageAutonomy2, activity.name)
|
|
6423
|
-
}));
|
|
6424
|
-
const cascadeParams = await renderConditionScope(scopeSource), transitionEvaluations = [];
|
|
6425
|
-
for (const transition of stage.transitions ?? []) {
|
|
6426
|
-
const insight = await explainSite({
|
|
6427
|
-
site: {
|
|
6428
|
-
kind: "transition",
|
|
6429
|
-
transition: transition.name
|
|
6430
|
-
},
|
|
6431
|
-
condition: transition.when,
|
|
6432
|
-
params: cascadeParams,
|
|
6433
|
-
snapshot: snapshot,
|
|
6434
|
-
sites: sites
|
|
6435
|
-
});
|
|
6436
|
-
transitionEvaluations.push({
|
|
6437
|
-
transition: transition,
|
|
6438
|
-
whenSatisfied: insight.outcome === "satisfied",
|
|
6439
|
-
unevaluable: insight.outcome === "unevaluable",
|
|
6440
|
-
insight: insight
|
|
6441
|
-
});
|
|
6442
|
-
}
|
|
6443
|
-
const currentStage = {
|
|
6444
|
-
stage: stage,
|
|
6445
|
-
activities: activityEvaluations,
|
|
6446
|
-
transitions: transitionEvaluations,
|
|
6447
|
-
autonomy: stageAutonomy2
|
|
6448
|
-
}, 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({
|
|
6449
|
-
instance: instance,
|
|
6450
|
-
definition: definition,
|
|
6451
|
-
stage: stage,
|
|
6452
|
-
snapshot: snapshot,
|
|
6453
|
-
scope: scope,
|
|
6454
|
-
scopeForActivity: scopeForActivity,
|
|
6455
|
-
guardDenial: editGuardDenial,
|
|
6456
|
-
sites: sites
|
|
6414
|
+
actionName: actionName,
|
|
6415
|
+
callerParams: callerParams,
|
|
6416
|
+
options: options
|
|
6417
|
+
}), mutation = startMutation(ctx.instance);
|
|
6418
|
+
recordProcessedRequest({
|
|
6419
|
+
mutation: mutation,
|
|
6420
|
+
record: requestRecord,
|
|
6421
|
+
now: ctx.now
|
|
6457
6422
|
});
|
|
6458
|
-
|
|
6459
|
-
|
|
6460
|
-
|
|
6423
|
+
const mutEntry = requireMutationActivityEntry(mutation, activityName), statusBefore = mutEntry.status, {ranOps: ranOps} = await applyActionFire({
|
|
6424
|
+
ctx: ctx,
|
|
6425
|
+
mutation: mutation,
|
|
6426
|
+
activity: activity,
|
|
6427
|
+
action: action,
|
|
6428
|
+
params: params,
|
|
6461
6429
|
actor: actor,
|
|
6462
|
-
|
|
6463
|
-
|
|
6464
|
-
|
|
6465
|
-
|
|
6466
|
-
|
|
6467
|
-
|
|
6468
|
-
|
|
6469
|
-
|
|
6470
|
-
|
|
6430
|
+
triggered: !1
|
|
6431
|
+
}), newStatus = mutEntry.status !== statusBefore ? mutEntry.status : void 0;
|
|
6432
|
+
return await persistThenMaybeRefresh({
|
|
6433
|
+
ctx: ctx,
|
|
6434
|
+
mutation: mutation,
|
|
6435
|
+
stageName: stage.name,
|
|
6436
|
+
didChangeState: ranOps.some(isFieldOp)
|
|
6437
|
+
}), {
|
|
6438
|
+
fired: !0,
|
|
6439
|
+
activity: activityName,
|
|
6440
|
+
action: actionName,
|
|
6441
|
+
...newStatus !== void 0 ? {
|
|
6442
|
+
newStatus: newStatus
|
|
6443
|
+
} : {},
|
|
6444
|
+
...ranOps.length > 0 ? {
|
|
6445
|
+
ranOps: ranOps
|
|
6446
|
+
} : {}
|
|
6471
6447
|
};
|
|
6472
6448
|
}
|
|
6473
6449
|
|
|
6474
|
-
|
|
6475
|
-
|
|
6476
|
-
|
|
6477
|
-
|
|
6478
|
-
|
|
6479
|
-
|
|
6480
|
-
|
|
6450
|
+
async function runTriggeredActions({ctx: ctx, mutation: mutation, stage: stage}) {
|
|
6451
|
+
let fires = 0, ranFieldOps = !1;
|
|
6452
|
+
const liveCtx = liveViewContext(ctx, mutation);
|
|
6453
|
+
let firedThisPass = !0;
|
|
6454
|
+
for (;firedThisPass; ) {
|
|
6455
|
+
firedThisPass = !1;
|
|
6456
|
+
for (const activity of stage.activities ?? []) {
|
|
6457
|
+
const fired = await runActivityTriggers({
|
|
6458
|
+
ctx: liveCtx,
|
|
6459
|
+
mutation: mutation,
|
|
6460
|
+
activity: activity
|
|
6461
|
+
});
|
|
6462
|
+
fires += fired.fires, ranFieldOps = ranFieldOps || fired.ranFieldOps, fired.fires > 0 && (firedThisPass = !0);
|
|
6463
|
+
}
|
|
6464
|
+
}
|
|
6465
|
+
return {
|
|
6466
|
+
fires: fires,
|
|
6467
|
+
ranFieldOps: ranFieldOps
|
|
6468
|
+
};
|
|
6481
6469
|
}
|
|
6482
6470
|
|
|
6483
|
-
async function
|
|
6484
|
-
|
|
6485
|
-
for (const
|
|
6486
|
-
|
|
6487
|
-
|
|
6488
|
-
|
|
6489
|
-
|
|
6490
|
-
|
|
6491
|
-
|
|
6492
|
-
|
|
6493
|
-
|
|
6494
|
-
|
|
6495
|
-
|
|
6496
|
-
|
|
6497
|
-
|
|
6498
|
-
|
|
6499
|
-
|
|
6500
|
-
scope: site.scope,
|
|
6501
|
-
...site.activity !== void 0 ? {
|
|
6502
|
-
activity: site.activity
|
|
6503
|
-
} : {},
|
|
6504
|
-
name: site.name,
|
|
6505
|
-
type: site.type,
|
|
6506
|
-
...site.title !== void 0 ? {
|
|
6507
|
-
title: site.title
|
|
6508
|
-
} : {},
|
|
6509
|
-
...site.validation !== void 0 ? {
|
|
6510
|
-
validation: site.validation
|
|
6511
|
-
} : {},
|
|
6512
|
-
value: value,
|
|
6513
|
-
editable: reason === void 0,
|
|
6514
|
-
...reason !== void 0 ? {
|
|
6515
|
-
disabledReason: reason
|
|
6516
|
-
} : {},
|
|
6517
|
-
...fieldProvenance(instance, site.ref),
|
|
6518
|
-
...insight !== void 0 ? {
|
|
6519
|
-
insight: insight
|
|
6520
|
-
} : {}
|
|
6471
|
+
async function runActivityTriggers({ctx: ctx, mutation: mutation, activity: activity}) {
|
|
6472
|
+
let fires = 0, ranFieldOps = !1;
|
|
6473
|
+
for (const action of activity.actions ?? []) {
|
|
6474
|
+
if (!isCascadeFired(action)) continue;
|
|
6475
|
+
const entry = currentActivities(mutation).find(e => e.name === activity.name);
|
|
6476
|
+
if (entry === void 0 || entry.status !== "active") break;
|
|
6477
|
+
if ((entry.firedActions ?? []).includes(action.name) || !await triggerIsLive({
|
|
6478
|
+
ctx: ctx,
|
|
6479
|
+
activity: activity,
|
|
6480
|
+
action: action
|
|
6481
|
+
})) continue;
|
|
6482
|
+
const result = await fireTriggeredAction({
|
|
6483
|
+
ctx: ctx,
|
|
6484
|
+
mutation: mutation,
|
|
6485
|
+
activity: activity,
|
|
6486
|
+
action: action,
|
|
6487
|
+
entry: entry
|
|
6521
6488
|
});
|
|
6489
|
+
if (fires++, ranFieldOps = ranFieldOps || result.ranFieldOps, isTerminalActivityStatus(entry.status)) break;
|
|
6522
6490
|
}
|
|
6523
|
-
return
|
|
6491
|
+
return {
|
|
6492
|
+
fires: fires,
|
|
6493
|
+
ranFieldOps: ranFieldOps
|
|
6494
|
+
};
|
|
6524
6495
|
}
|
|
6525
6496
|
|
|
6526
|
-
async function
|
|
6527
|
-
|
|
6528
|
-
|
|
6529
|
-
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
|
|
6543
|
-
});
|
|
6497
|
+
async function triggerIsLive({ctx: ctx, activity: activity, action: action}) {
|
|
6498
|
+
if (!tokenMayExecute({
|
|
6499
|
+
actor: ctx.actor,
|
|
6500
|
+
action: action,
|
|
6501
|
+
ctx: ctx
|
|
6502
|
+
})) return !1;
|
|
6503
|
+
const scope = {
|
|
6504
|
+
activityName: activity.name
|
|
6505
|
+
};
|
|
6506
|
+
return action.filter !== void 0 && await ctxEvaluateConditionOutcome({
|
|
6507
|
+
ctx: ctx,
|
|
6508
|
+
condition: action.filter,
|
|
6509
|
+
opts: scope
|
|
6510
|
+
}) !== "satisfied" ? !1 : await ctxEvaluateConditionOutcome({
|
|
6511
|
+
ctx: ctx,
|
|
6512
|
+
condition: action.when,
|
|
6513
|
+
opts: scope
|
|
6514
|
+
}) === "satisfied";
|
|
6544
6515
|
}
|
|
6545
6516
|
|
|
6546
|
-
|
|
6547
|
-
|
|
6548
|
-
|
|
6549
|
-
|
|
6550
|
-
|
|
6551
|
-
|
|
6552
|
-
|
|
6553
|
-
|
|
6517
|
+
function tokenMayExecute({actor: actor, action: action, ctx: ctx}) {
|
|
6518
|
+
const roles = action.roles;
|
|
6519
|
+
return roles === void 0 || roles.length === 0 ? !0 : actor === void 0 ? !1 : roles.some(required => actorFulfillsRole({
|
|
6520
|
+
actorRoles: actor.roles,
|
|
6521
|
+
required: required,
|
|
6522
|
+
aliases: ctx.definition.roleAliases
|
|
6523
|
+
}));
|
|
6524
|
+
}
|
|
6525
|
+
|
|
6526
|
+
async function fireTriggeredAction({ctx: ctx, mutation: mutation, activity: activity, action: action, entry: entry}) {
|
|
6527
|
+
entry.firedActions = [ ...entry.firedActions ?? [], action.name ];
|
|
6528
|
+
const {ranOps: ranOps} = await applyActionFire({
|
|
6529
|
+
ctx: ctx,
|
|
6530
|
+
mutation: mutation,
|
|
6531
|
+
activity: activity,
|
|
6532
|
+
action: action,
|
|
6533
|
+
params: {},
|
|
6534
|
+
actor: ctx.actor,
|
|
6535
|
+
triggered: !0
|
|
6554
6536
|
});
|
|
6555
|
-
return
|
|
6537
|
+
return {
|
|
6538
|
+
ranFieldOps: ranOps.some(isFieldOp)
|
|
6539
|
+
};
|
|
6556
6540
|
}
|
|
6557
6541
|
|
|
6558
|
-
async function
|
|
6559
|
-
const
|
|
6560
|
-
if (
|
|
6561
|
-
const
|
|
6562
|
-
|
|
6563
|
-
|
|
6564
|
-
|
|
6565
|
-
|
|
6566
|
-
|
|
6567
|
-
|
|
6568
|
-
|
|
6569
|
-
|
|
6570
|
-
|
|
6571
|
-
|
|
6572
|
-
|
|
6573
|
-
|
|
6574
|
-
|
|
6575
|
-
|
|
6576
|
-
|
|
6542
|
+
async function primeInitialStage({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry}) {
|
|
6543
|
+
const instance = await getInstanceDocument(client, instanceId);
|
|
6544
|
+
if (!instance || instance.stages.length > 0) return;
|
|
6545
|
+
const definition = parseDefinitionSnapshot(instance), stage = definition.stages.find(s => s.name === instance.currentStage);
|
|
6546
|
+
if (stage === void 0) return;
|
|
6547
|
+
const ctx = await buildEngineContext({
|
|
6548
|
+
client: client,
|
|
6549
|
+
clientForGdr: clientForGdr,
|
|
6550
|
+
refSurface: refSurface,
|
|
6551
|
+
instance: instance,
|
|
6552
|
+
definition: definition,
|
|
6553
|
+
...clock ? {
|
|
6554
|
+
clock: clock
|
|
6555
|
+
} : {},
|
|
6556
|
+
...actor ? {
|
|
6557
|
+
actor: actor
|
|
6558
|
+
} : {},
|
|
6559
|
+
...executionContext ? {
|
|
6560
|
+
executionContext: executionContext
|
|
6561
|
+
} : {},
|
|
6562
|
+
...telemetry ? {
|
|
6563
|
+
telemetry: telemetry
|
|
6564
|
+
} : {}
|
|
6565
|
+
}), now = ctx.now, discards = [], initialStageEntry = {
|
|
6566
|
+
_key: randomKey(),
|
|
6567
|
+
name: stage.name,
|
|
6568
|
+
enteredAt: now,
|
|
6569
|
+
fields: await resolveStageFieldEntries({
|
|
6570
|
+
client: client,
|
|
6571
|
+
instance: instance,
|
|
6572
|
+
stage: stage,
|
|
6573
|
+
now: now,
|
|
6574
|
+
refSurface: refSurface,
|
|
6575
|
+
recordDiscard: recordFieldDiscards({
|
|
6576
|
+
target: discards,
|
|
6577
|
+
scope: "stage",
|
|
6578
|
+
at: now
|
|
6579
|
+
})
|
|
6580
|
+
}),
|
|
6581
|
+
activities: []
|
|
6582
|
+
}, primedCtx = {
|
|
6583
|
+
...ctx,
|
|
6584
|
+
instance: {
|
|
6585
|
+
...instance,
|
|
6586
|
+
stages: [ initialStageEntry ]
|
|
6587
|
+
}
|
|
6588
|
+
};
|
|
6589
|
+
initialStageEntry.activities = await buildStageActivities({
|
|
6590
|
+
ctx: primedCtx,
|
|
6591
|
+
stage: stage,
|
|
6592
|
+
recordDiscard: recordFieldDiscards({
|
|
6593
|
+
target: discards,
|
|
6594
|
+
scope: "activity",
|
|
6595
|
+
at: now
|
|
6596
|
+
})
|
|
6597
|
+
});
|
|
6598
|
+
const terminal = isTerminalStage(stage), committed = await client.patch(instance._id).set({
|
|
6599
|
+
stages: [ initialStageEntry ],
|
|
6600
|
+
lastChangedAt: now,
|
|
6601
|
+
...terminal ? {
|
|
6602
|
+
completedAt: now
|
|
6603
|
+
} : {},
|
|
6604
|
+
...discards.length > 0 ? {
|
|
6605
|
+
history: [ ...instance.history, ...stampHistoryEntries(discards, ctx.executionContext) ]
|
|
6606
|
+
} : {}
|
|
6607
|
+
}).ifRevisionId(instance._rev).commit(SYNC_COMMIT);
|
|
6608
|
+
await deployOrRollback({
|
|
6609
|
+
client: client,
|
|
6610
|
+
instanceId: instance._id,
|
|
6611
|
+
committedRev: committed._rev,
|
|
6612
|
+
restore: {
|
|
6613
|
+
stages: instance.stages,
|
|
6614
|
+
history: instance.history
|
|
6615
|
+
},
|
|
6616
|
+
...terminal ? {
|
|
6617
|
+
unset: [ "completedAt" ]
|
|
6618
|
+
} : {},
|
|
6619
|
+
reversible: !0,
|
|
6620
|
+
deploy: () => deployStageGuards({
|
|
6621
|
+
client: client,
|
|
6622
|
+
clientForGdr: clientForGdr,
|
|
6623
|
+
instance: instance,
|
|
6624
|
+
definition: definition,
|
|
6625
|
+
stageName: stage.name,
|
|
6626
|
+
now: now,
|
|
6627
|
+
snapshot: ctx.snapshot
|
|
6628
|
+
})
|
|
6629
|
+
});
|
|
6630
|
+
}
|
|
6631
|
+
|
|
6632
|
+
const CASCADE_LIMIT = 100;
|
|
6633
|
+
|
|
6634
|
+
async function runCascadeHop({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry, overlay: overlay}) {
|
|
6635
|
+
const ctx = await loadContext({
|
|
6636
|
+
client: client,
|
|
6637
|
+
instanceId: instanceId,
|
|
6638
|
+
options: {
|
|
6639
|
+
clientForGdr: clientForGdr,
|
|
6640
|
+
refSurface: refSurface,
|
|
6641
|
+
...actor ? {
|
|
6642
|
+
actor: actor
|
|
6643
|
+
} : {},
|
|
6644
|
+
...clock ? {
|
|
6645
|
+
clock: clock
|
|
6646
|
+
} : {},
|
|
6647
|
+
...executionContext ? {
|
|
6648
|
+
executionContext: executionContext
|
|
6649
|
+
} : {},
|
|
6650
|
+
...telemetry ? {
|
|
6651
|
+
telemetry: telemetry
|
|
6652
|
+
} : {},
|
|
6653
|
+
...overlay ? {
|
|
6654
|
+
overlay: overlay
|
|
6655
|
+
} : {}
|
|
6656
|
+
}
|
|
6657
|
+
});
|
|
6658
|
+
if (isTerminal(ctx)) return {
|
|
6659
|
+
moved: !1
|
|
6660
|
+
};
|
|
6661
|
+
const stage = findStage(ctx.definition, ctx.instance.currentStage), mutation = startMutation(ctx.instance), fired = await runTriggeredActions({
|
|
6662
|
+
ctx: ctx,
|
|
6663
|
+
mutation: mutation,
|
|
6664
|
+
stage: stage
|
|
6665
|
+
}), hopCtx = liveViewContext(ctx, mutation), transition = await pickTransition(hopCtx, stage);
|
|
6666
|
+
return transition === void 0 ? (fired.fires > 0 && await persistThenMaybeRefresh({
|
|
6667
|
+
ctx: ctx,
|
|
6668
|
+
mutation: mutation,
|
|
6669
|
+
stageName: stage.name,
|
|
6670
|
+
didChangeState: fired.ranFieldOps
|
|
6671
|
+
}), {
|
|
6672
|
+
moved: !1
|
|
6673
|
+
}) : (await commitStageMove({
|
|
6674
|
+
ctx: ctx,
|
|
6675
|
+
mutation: mutation,
|
|
6676
|
+
fromStage: stage,
|
|
6677
|
+
toStage: findStage(ctx.definition, transition.to),
|
|
6678
|
+
transition: transition.name,
|
|
6679
|
+
via: "transition",
|
|
6680
|
+
actor: actor
|
|
6681
|
+
}), {
|
|
6682
|
+
moved: !0
|
|
6683
|
+
});
|
|
6684
|
+
}
|
|
6685
|
+
|
|
6686
|
+
async function cascadeAutoTransitions({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry, overlay: overlay}) {
|
|
6687
|
+
let count = 0;
|
|
6688
|
+
for (;;) {
|
|
6689
|
+
if (await drainCondemnedChildren({
|
|
6690
|
+
client: client,
|
|
6691
|
+
instanceId: instanceId,
|
|
6692
|
+
actor: actor,
|
|
6693
|
+
clientForGdr: clientForGdr,
|
|
6694
|
+
refSurface: refSurface,
|
|
6695
|
+
clock: clock,
|
|
6696
|
+
executionContext: executionContext,
|
|
6697
|
+
telemetry: telemetry
|
|
6698
|
+
}), !(await runCascadeHop({
|
|
6699
|
+
client: client,
|
|
6700
|
+
instanceId: instanceId,
|
|
6701
|
+
actor: actor,
|
|
6702
|
+
clientForGdr: clientForGdr,
|
|
6703
|
+
refSurface: refSurface,
|
|
6704
|
+
clock: clock,
|
|
6705
|
+
executionContext: executionContext,
|
|
6706
|
+
telemetry: telemetry,
|
|
6707
|
+
overlay: overlay
|
|
6708
|
+
})).moved) return count;
|
|
6709
|
+
if (count++, count >= CASCADE_LIMIT) throw new CascadeLimitError({
|
|
6710
|
+
instanceId: instanceId,
|
|
6711
|
+
limit: CASCADE_LIMIT
|
|
6577
6712
|
});
|
|
6578
6713
|
}
|
|
6579
|
-
return denials;
|
|
6580
6714
|
}
|
|
6581
6715
|
|
|
6582
|
-
async function
|
|
6583
|
-
|
|
6716
|
+
async function drainCondemnedChildren({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry, draining: draining = /* @__PURE__ */ new Set}) {
|
|
6717
|
+
if (draining.has(instanceId)) return;
|
|
6718
|
+
draining.add(instanceId);
|
|
6719
|
+
const instance = await getInstanceDocument(client, instanceId);
|
|
6720
|
+
if (!instance) return;
|
|
6721
|
+
const condemned = condemnedSubworkflows(instance);
|
|
6722
|
+
if (condemned.length !== 0) {
|
|
6723
|
+
for (const row of condemned) await settleCondemnedRow({
|
|
6724
|
+
client: client,
|
|
6725
|
+
ownerId: instanceId,
|
|
6726
|
+
row: row,
|
|
6727
|
+
actor: actor,
|
|
6728
|
+
clientForGdr: clientForGdr,
|
|
6729
|
+
refSurface: refSurface,
|
|
6730
|
+
clock: clock,
|
|
6731
|
+
executionContext: executionContext,
|
|
6732
|
+
telemetry: telemetry,
|
|
6733
|
+
draining: draining
|
|
6734
|
+
});
|
|
6735
|
+
await stampDrainedRows({
|
|
6736
|
+
client: client,
|
|
6737
|
+
instance: instance,
|
|
6738
|
+
condemned: condemned,
|
|
6739
|
+
clock: clock,
|
|
6740
|
+
executionContext: executionContext
|
|
6741
|
+
});
|
|
6742
|
+
}
|
|
6743
|
+
}
|
|
6744
|
+
|
|
6745
|
+
async function settleCondemnedRow({client: client, ownerId: ownerId, row: row, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry, draining: draining}) {
|
|
6746
|
+
const childId = toBareId(row.ref.id);
|
|
6584
6747
|
try {
|
|
6585
|
-
|
|
6586
|
-
|
|
6587
|
-
|
|
6588
|
-
|
|
6589
|
-
|
|
6590
|
-
|
|
6591
|
-
|
|
6592
|
-
|
|
6748
|
+
await abortInstance({
|
|
6749
|
+
client: client,
|
|
6750
|
+
instanceId: childId,
|
|
6751
|
+
reason: row.abortPending?.reason ?? "condemned by parent",
|
|
6752
|
+
options: {
|
|
6753
|
+
...actor ? {
|
|
6754
|
+
actor: actor
|
|
6755
|
+
} : {},
|
|
6756
|
+
clientForGdr: clientForGdr,
|
|
6757
|
+
refSurface: refSurface,
|
|
6758
|
+
...clock ? {
|
|
6759
|
+
clock: clock
|
|
6760
|
+
} : {},
|
|
6761
|
+
...executionContext ? {
|
|
6762
|
+
executionContext: executionContext
|
|
6763
|
+
} : {},
|
|
6764
|
+
...telemetry ? {
|
|
6765
|
+
telemetry: telemetry
|
|
6766
|
+
} : {}
|
|
6767
|
+
}
|
|
6768
|
+
}), await drainCondemnedChildren({
|
|
6769
|
+
client: client,
|
|
6770
|
+
instanceId: childId,
|
|
6771
|
+
actor: actor,
|
|
6772
|
+
clientForGdr: clientForGdr,
|
|
6773
|
+
refSurface: refSurface,
|
|
6774
|
+
clock: clock,
|
|
6775
|
+
executionContext: executionContext,
|
|
6776
|
+
telemetry: telemetry,
|
|
6777
|
+
draining: draining
|
|
6778
|
+
});
|
|
6779
|
+
} catch (cause) {
|
|
6780
|
+
if (cause instanceof InstanceNotFoundError) return;
|
|
6781
|
+
throw cause instanceof WorkflowStateDivergedError ? cause : new WorkflowStateDivergedError({
|
|
6782
|
+
instanceId: ownerId,
|
|
6783
|
+
guardError: cause,
|
|
6784
|
+
reason: `owed child abort of "${childId}" failed while draining condemned rows`
|
|
6593
6785
|
});
|
|
6594
|
-
} catch (err) {
|
|
6595
|
-
console.warn(`workflow: subject-write forecast skipped for "${doc._id}" — evaluating that resource's grants failed (the lake still enforces the write). Original error: ${errorMessage(err)}`);
|
|
6596
|
-
return;
|
|
6597
6786
|
}
|
|
6598
6787
|
}
|
|
6599
6788
|
|
|
6600
|
-
async function
|
|
6601
|
-
const
|
|
6602
|
-
|
|
6603
|
-
|
|
6604
|
-
|
|
6605
|
-
|
|
6606
|
-
|
|
6607
|
-
|
|
6608
|
-
|
|
6609
|
-
|
|
6610
|
-
|
|
6611
|
-
|
|
6612
|
-
|
|
6613
|
-
|
|
6614
|
-
|
|
6615
|
-
|
|
6616
|
-
|
|
6617
|
-
|
|
6618
|
-
|
|
6619
|
-
|
|
6620
|
-
|
|
6621
|
-
|
|
6622
|
-
|
|
6623
|
-
sites: sites
|
|
6624
|
-
}));
|
|
6625
|
-
return {
|
|
6626
|
-
activity: activity,
|
|
6627
|
-
status: status,
|
|
6628
|
-
kind: deriveActivityKind(activity),
|
|
6629
|
-
classification: deriveExecutorClassification(activity),
|
|
6630
|
-
autonomy: autonomy,
|
|
6631
|
-
pendingOnActor: status === "active" && assigned,
|
|
6632
|
-
scopedOut: isFilterScopedOut({
|
|
6633
|
-
status: status,
|
|
6634
|
-
startedAt: statusEntry?.startedAt
|
|
6635
|
-
}),
|
|
6636
|
-
...unmetRequirements.length > 0 ? {
|
|
6637
|
-
unmetRequirements: unmetRequirements
|
|
6638
|
-
} : {},
|
|
6639
|
-
...conditionInsights,
|
|
6640
|
-
actions: actions
|
|
6641
|
-
};
|
|
6789
|
+
async function stampDrainedRows({client: client, instance: instance, condemned: condemned, clock: clock, executionContext: executionContext}) {
|
|
6790
|
+
const now = (clock ?? wallClock)(), stamp = resolveExecutionContext(executionContext), ids = condemned.map(row => toBareId(row.ref.id)), children = await client.fetch("*[_id in $ids]{_id, currentStage, completedAt, abortedAt, modelVersion, minReaderModel}", {
|
|
6791
|
+
ids: ids
|
|
6792
|
+
}), 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 => {
|
|
6793
|
+
if (row.resolved !== void 0 || !condemnedKeys.has(row._key)) return row;
|
|
6794
|
+
const child = byId.get(toBareId(row.ref.id)), resolved = child !== void 0 ? terminalResolution(child) : {
|
|
6795
|
+
at: now,
|
|
6796
|
+
aborted: !0
|
|
6797
|
+
};
|
|
6798
|
+
return resolved === void 0 ? row : (history.push(...stampHistoryEntries([ subworkflowResolvedEntry({
|
|
6799
|
+
row: row,
|
|
6800
|
+
at: now,
|
|
6801
|
+
status: resolved.aborted === !0 ? "aborted" : "done"
|
|
6802
|
+
}) ], stamp)), {
|
|
6803
|
+
...row,
|
|
6804
|
+
resolved: resolved
|
|
6805
|
+
});
|
|
6806
|
+
});
|
|
6807
|
+
await client.patch(instance._id).set({
|
|
6808
|
+
subworkflows: subworkflows,
|
|
6809
|
+
history: history,
|
|
6810
|
+
lastChangedAt: now
|
|
6811
|
+
}).ifRevisionId(instance._rev).commit(SYNC_COMMIT);
|
|
6642
6812
|
}
|
|
6643
6813
|
|
|
6644
|
-
|
|
6645
|
-
const
|
|
6646
|
-
|
|
6647
|
-
|
|
6648
|
-
|
|
6649
|
-
|
|
6650
|
-
|
|
6651
|
-
}), requirementEntries = [];
|
|
6652
|
-
for (const [name, condition] of Object.entries(activity.requirements ?? {})) requirementEntries.push([ name, await explainAt({
|
|
6653
|
-
kind: "requirement",
|
|
6654
|
-
activity: activity.name,
|
|
6655
|
-
requirement: name
|
|
6656
|
-
}, condition) ]);
|
|
6657
|
-
const requirementInsights = Object.fromEntries(requirementEntries), unmetRequirements = requirementEntries.filter(([, insight]) => insight.outcome !== "satisfied").map(([name]) => name), filterInsight = activity.filter !== void 0 ? await explainSite({
|
|
6658
|
-
site: {
|
|
6659
|
-
kind: "activity-filter",
|
|
6660
|
-
activity: activity.name
|
|
6661
|
-
},
|
|
6662
|
-
condition: activity.filter,
|
|
6663
|
-
params: await cascadeActivityScope(),
|
|
6664
|
-
snapshot: snapshot,
|
|
6665
|
-
sites: sites
|
|
6666
|
-
}) : void 0;
|
|
6667
|
-
return {
|
|
6668
|
-
unmetRequirements: unmetRequirements,
|
|
6669
|
-
...activity.requirements !== void 0 ? {
|
|
6670
|
-
requirementInsights: requirementInsights
|
|
6671
|
-
} : {},
|
|
6672
|
-
...filterInsight !== void 0 ? {
|
|
6673
|
-
filterInsight: filterInsight
|
|
6814
|
+
function terminalResolution(child) {
|
|
6815
|
+
const status = resolvedChildStatus(child);
|
|
6816
|
+
if (!(status === void 0 || child.completedAt === void 0 || child.completedAt === null)) return {
|
|
6817
|
+
at: child.completedAt,
|
|
6818
|
+
stage: child.currentStage,
|
|
6819
|
+
...status === "aborted" ? {
|
|
6820
|
+
aborted: !0
|
|
6674
6821
|
} : {}
|
|
6675
6822
|
};
|
|
6676
6823
|
}
|
|
6677
6824
|
|
|
6678
|
-
|
|
6679
|
-
|
|
6680
|
-
|
|
6681
|
-
|
|
6682
|
-
|
|
6683
|
-
|
|
6684
|
-
|
|
6685
|
-
|
|
6686
|
-
|
|
6687
|
-
action: action,
|
|
6688
|
-
when: action.when,
|
|
6689
|
-
insights: insights
|
|
6690
|
-
}) : fireableActionVerdict({
|
|
6691
|
-
args: args,
|
|
6692
|
-
insights: insights
|
|
6693
|
-
});
|
|
6825
|
+
function subworkflowResolvedEntry({row: row, at: at, status: status}) {
|
|
6826
|
+
return {
|
|
6827
|
+
_key: randomKey(),
|
|
6828
|
+
_type: "subworkflowResolved",
|
|
6829
|
+
at: at,
|
|
6830
|
+
activity: row.activity,
|
|
6831
|
+
instanceRef: row.ref,
|
|
6832
|
+
status: status
|
|
6833
|
+
};
|
|
6694
6834
|
}
|
|
6695
6835
|
|
|
6696
|
-
async function
|
|
6697
|
-
const
|
|
6698
|
-
|
|
6699
|
-
|
|
6700
|
-
|
|
6701
|
-
|
|
6702
|
-
|
|
6703
|
-
|
|
6704
|
-
|
|
6705
|
-
|
|
6706
|
-
|
|
6707
|
-
}) : void 0, whenInsight = action.when !== void 0 ? await explainSite({
|
|
6708
|
-
site: {
|
|
6709
|
-
kind: "action-when",
|
|
6710
|
-
activity: activityName,
|
|
6711
|
-
action: action.name
|
|
6712
|
-
},
|
|
6713
|
-
condition: action.when,
|
|
6714
|
-
params: conditionScope,
|
|
6715
|
-
snapshot: snapshot,
|
|
6716
|
-
sites: sites
|
|
6717
|
-
}) : void 0;
|
|
6718
|
-
return {
|
|
6719
|
-
...insight !== void 0 ? {
|
|
6720
|
-
insight: insight
|
|
6836
|
+
async function propagateToAncestors({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, clock: clock, executionContext: executionContext, telemetry: telemetry}) {
|
|
6837
|
+
const loaded = await loadPropagationPair(client, instanceId);
|
|
6838
|
+
if (loaded === void 0) return;
|
|
6839
|
+
const {child: child, parent: parent} = loaded, definition = parseDefinitionSnapshot(parent), ctx = await buildEngineContext({
|
|
6840
|
+
client: client,
|
|
6841
|
+
clientForGdr: clientForGdr,
|
|
6842
|
+
refSurface: refSurface,
|
|
6843
|
+
instance: parent,
|
|
6844
|
+
definition: definition,
|
|
6845
|
+
...clock ? {
|
|
6846
|
+
clock: clock
|
|
6721
6847
|
} : {},
|
|
6722
|
-
...
|
|
6723
|
-
|
|
6848
|
+
...actor ? {
|
|
6849
|
+
actor: actor
|
|
6850
|
+
} : {},
|
|
6851
|
+
...executionContext ? {
|
|
6852
|
+
executionContext: executionContext
|
|
6853
|
+
} : {},
|
|
6854
|
+
...telemetry ? {
|
|
6855
|
+
telemetry: telemetry
|
|
6724
6856
|
} : {}
|
|
6725
|
-
};
|
|
6857
|
+
}), mutation = startMutation(parent), row = mutation.subworkflows.find(r => toBareId(r.ref.id) === child._id);
|
|
6858
|
+
if (row === void 0) {
|
|
6859
|
+
await recordOrphanedPropagation({
|
|
6860
|
+
ctx: ctx,
|
|
6861
|
+
mutation: mutation,
|
|
6862
|
+
child: child
|
|
6863
|
+
});
|
|
6864
|
+
return;
|
|
6865
|
+
}
|
|
6866
|
+
const changed = stampResolvedChild({
|
|
6867
|
+
mutation: mutation,
|
|
6868
|
+
row: row,
|
|
6869
|
+
child: child,
|
|
6870
|
+
now: ctx.now
|
|
6871
|
+
}), parentTerminal = parent.completedAt !== void 0;
|
|
6872
|
+
changed && await persist(ctx, mutation), !parentTerminal && (await cascadeAutoTransitions({
|
|
6873
|
+
client: client,
|
|
6874
|
+
instanceId: parent._id,
|
|
6875
|
+
actor: actor,
|
|
6876
|
+
clientForGdr: clientForGdr,
|
|
6877
|
+
refSurface: refSurface,
|
|
6878
|
+
clock: clock,
|
|
6879
|
+
executionContext: executionContext,
|
|
6880
|
+
telemetry: telemetry
|
|
6881
|
+
}), await propagateToAncestors({
|
|
6882
|
+
client: client,
|
|
6883
|
+
instanceId: parent._id,
|
|
6884
|
+
actor: actor,
|
|
6885
|
+
clientForGdr: clientForGdr,
|
|
6886
|
+
refSurface: refSurface,
|
|
6887
|
+
clock: clock,
|
|
6888
|
+
executionContext: executionContext,
|
|
6889
|
+
telemetry: telemetry
|
|
6890
|
+
}));
|
|
6726
6891
|
}
|
|
6727
6892
|
|
|
6728
|
-
function
|
|
6729
|
-
|
|
6730
|
-
|
|
6731
|
-
|
|
6732
|
-
|
|
6733
|
-
|
|
6734
|
-
|
|
6735
|
-
|
|
6736
|
-
|
|
6737
|
-
...actionEvaluationIdentity(action),
|
|
6738
|
-
allowed: !1,
|
|
6739
|
-
triggered: !0,
|
|
6740
|
-
disabledReason: {
|
|
6741
|
-
kind: "cascade-fired",
|
|
6742
|
-
when: when
|
|
6743
|
-
},
|
|
6744
|
-
...insights
|
|
6893
|
+
async function loadPropagationPair(client, instanceId) {
|
|
6894
|
+
const child = await getInstanceDocument(client, instanceId);
|
|
6895
|
+
if (!child) return;
|
|
6896
|
+
const parentGdr = parentRef(child);
|
|
6897
|
+
if (parentGdr === void 0) return;
|
|
6898
|
+
const parentDoc = await client.getDocument(toBareId(parentGdr.id));
|
|
6899
|
+
if (!(!parentDoc || parentDoc._type !== WORKFLOW_INSTANCE_TYPE)) return {
|
|
6900
|
+
child: child,
|
|
6901
|
+
parent: readInstanceDoc(parentDoc)
|
|
6745
6902
|
};
|
|
6746
6903
|
}
|
|
6747
6904
|
|
|
6748
|
-
function
|
|
6749
|
-
|
|
6750
|
-
|
|
6751
|
-
|
|
6752
|
-
|
|
6753
|
-
|
|
6754
|
-
|
|
6755
|
-
|
|
6756
|
-
|
|
6757
|
-
|
|
6758
|
-
|
|
6759
|
-
|
|
6760
|
-
|
|
6761
|
-
|
|
6762
|
-
|
|
6763
|
-
|
|
6764
|
-
|
|
6765
|
-
|
|
6766
|
-
|
|
6767
|
-
...insights
|
|
6768
|
-
});
|
|
6769
|
-
if (args.requirementsReason !== void 0) return disabled({
|
|
6770
|
-
action: action,
|
|
6771
|
-
reason: args.requirementsReason,
|
|
6772
|
-
...insights
|
|
6773
|
-
});
|
|
6774
|
-
const {insight: insight} = insights;
|
|
6775
|
-
return action.filter !== void 0 && insight !== void 0 && insight.outcome !== "satisfied" ? disabled({
|
|
6776
|
-
action: action,
|
|
6777
|
-
reason: {
|
|
6778
|
-
kind: "filter-failed",
|
|
6779
|
-
filter: action.filter
|
|
6905
|
+
function stampResolvedChild({mutation: mutation, row: row, child: child, now: now}) {
|
|
6906
|
+
if (row.resolved !== void 0) return !1;
|
|
6907
|
+
const resolved = terminalResolution(child);
|
|
6908
|
+
return resolved === void 0 ? !1 : (row.resolved = resolved, mutation.history.push(subworkflowResolvedEntry({
|
|
6909
|
+
row: row,
|
|
6910
|
+
at: now,
|
|
6911
|
+
status: resolved.aborted === !0 ? "aborted" : "done"
|
|
6912
|
+
})), !0);
|
|
6913
|
+
}
|
|
6914
|
+
|
|
6915
|
+
async function recordOrphanedPropagation({ctx: ctx, mutation: mutation, child: child}) {
|
|
6916
|
+
const childUri = selfGdr(child);
|
|
6917
|
+
mutation.history.some(h => h._type === "subworkflowOrphaned" && h.instanceRef.id === childUri) || (mutation.history.push({
|
|
6918
|
+
_key: randomKey(),
|
|
6919
|
+
_type: "subworkflowOrphaned",
|
|
6920
|
+
at: ctx.now,
|
|
6921
|
+
instanceRef: {
|
|
6922
|
+
id: childUri,
|
|
6923
|
+
type: WORKFLOW_INSTANCE_TYPE
|
|
6780
6924
|
},
|
|
6781
|
-
|
|
6782
|
-
})
|
|
6783
|
-
...actionEvaluationIdentity(action),
|
|
6784
|
-
allowed: !0,
|
|
6785
|
-
...insights
|
|
6786
|
-
};
|
|
6925
|
+
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.`
|
|
6926
|
+
}), await persist(ctx, mutation));
|
|
6787
6927
|
}
|
|
6788
6928
|
|
|
6789
|
-
function
|
|
6929
|
+
function startMutation(instance) {
|
|
6790
6930
|
return {
|
|
6791
|
-
|
|
6792
|
-
|
|
6793
|
-
|
|
6794
|
-
|
|
6931
|
+
minReaderModel: minReaderModelOf(instance),
|
|
6932
|
+
currentStage: instance.currentStage,
|
|
6933
|
+
fields: (instance.fields ?? []).map(s => ({
|
|
6934
|
+
...s
|
|
6935
|
+
})),
|
|
6936
|
+
stages: instance.stages.map(s => ({
|
|
6937
|
+
...s,
|
|
6938
|
+
fields: (s.fields ?? []).map(entry => ({
|
|
6939
|
+
...entry
|
|
6940
|
+
})),
|
|
6941
|
+
activities: s.activities.map(t => ({
|
|
6942
|
+
...t,
|
|
6943
|
+
...t.fields !== void 0 ? {
|
|
6944
|
+
fields: t.fields.map(entry => ({
|
|
6945
|
+
...entry
|
|
6946
|
+
}))
|
|
6947
|
+
} : {}
|
|
6948
|
+
}))
|
|
6949
|
+
})),
|
|
6950
|
+
subworkflows: (instance.subworkflows ?? []).map(row => ({
|
|
6951
|
+
...row,
|
|
6952
|
+
...row.abortPending !== void 0 ? {
|
|
6953
|
+
abortPending: {
|
|
6954
|
+
...row.abortPending
|
|
6955
|
+
}
|
|
6956
|
+
} : {},
|
|
6957
|
+
...row.resolved !== void 0 ? {
|
|
6958
|
+
resolved: {
|
|
6959
|
+
...row.resolved
|
|
6960
|
+
}
|
|
6961
|
+
} : {}
|
|
6962
|
+
})),
|
|
6963
|
+
pendingEffects: [ ...instance.pendingEffects ],
|
|
6964
|
+
effectHistory: [ ...instance.effectHistory ],
|
|
6965
|
+
context: [ ...instance.context ],
|
|
6966
|
+
history: [ ...instance.history ],
|
|
6967
|
+
processedRequests: [ ...instance.processedRequests ?? [] ],
|
|
6968
|
+
lastChangedAt: instance.lastChangedAt,
|
|
6969
|
+
...instance.completedAt !== void 0 ? {
|
|
6970
|
+
completedAt: instance.completedAt
|
|
6971
|
+
} : {},
|
|
6972
|
+
...instance.abortedAt !== void 0 ? {
|
|
6973
|
+
abortedAt: instance.abortedAt
|
|
6974
|
+
} : {},
|
|
6975
|
+
pendingCreates: []
|
|
6795
6976
|
};
|
|
6796
6977
|
}
|
|
6797
6978
|
|
|
6798
|
-
|
|
6799
|
-
|
|
6800
|
-
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
|
|
6804
|
-
|
|
6805
|
-
|
|
6806
|
-
|
|
6807
|
-
|
|
6979
|
+
function instanceStateFields(src) {
|
|
6980
|
+
const state = {
|
|
6981
|
+
currentStage: src.currentStage,
|
|
6982
|
+
fields: src.fields,
|
|
6983
|
+
stages: src.stages,
|
|
6984
|
+
subworkflows: src.subworkflows ?? [],
|
|
6985
|
+
pendingEffects: src.pendingEffects,
|
|
6986
|
+
effectHistory: src.effectHistory,
|
|
6987
|
+
context: src.context,
|
|
6988
|
+
history: src.history,
|
|
6989
|
+
processedRequests: src.processedRequests ?? [],
|
|
6990
|
+
...src.completedAt !== void 0 ? {
|
|
6991
|
+
completedAt: src.completedAt
|
|
6992
|
+
} : {},
|
|
6993
|
+
...src.abortedAt !== void 0 ? {
|
|
6994
|
+
abortedAt: src.abortedAt
|
|
6995
|
+
} : {}
|
|
6996
|
+
};
|
|
6997
|
+
return {
|
|
6998
|
+
...modelStampFor({
|
|
6999
|
+
documentType: "instance",
|
|
7000
|
+
document: state,
|
|
7001
|
+
storedMinReaderModel: src.minReaderModel
|
|
7002
|
+
}),
|
|
7003
|
+
...state
|
|
6808
7004
|
};
|
|
6809
7005
|
}
|
|
6810
7006
|
|
|
6811
|
-
function
|
|
6812
|
-
const
|
|
6813
|
-
|
|
6814
|
-
|
|
6815
|
-
|
|
6816
|
-
|
|
6817
|
-
};
|
|
6818
|
-
if (isTerminalActivityStatus(status)) return {
|
|
6819
|
-
kind: "activity-not-active",
|
|
6820
|
-
status: status
|
|
7007
|
+
function liveViewContext(ctx, mutation) {
|
|
7008
|
+
const materialized = materializeInstance(ctx.instance, mutation);
|
|
7009
|
+
return {
|
|
7010
|
+
...ctx,
|
|
7011
|
+
instance: materialized,
|
|
7012
|
+
snapshot: overlayInstanceInSnapshot(ctx.snapshot, materialized)
|
|
6821
7013
|
};
|
|
6822
7014
|
}
|
|
6823
7015
|
|
|
6824
|
-
function
|
|
7016
|
+
function materializeInstance(base, mutation) {
|
|
6825
7017
|
return {
|
|
6826
|
-
...
|
|
6827
|
-
|
|
6828
|
-
|
|
6829
|
-
|
|
6830
|
-
|
|
6831
|
-
|
|
6832
|
-
|
|
6833
|
-
|
|
6834
|
-
} : {}
|
|
7018
|
+
...base,
|
|
7019
|
+
currentStage: mutation.currentStage,
|
|
7020
|
+
fields: mutation.fields,
|
|
7021
|
+
stages: mutation.stages,
|
|
7022
|
+
subworkflows: mutation.subworkflows,
|
|
7023
|
+
context: mutation.context,
|
|
7024
|
+
pendingEffects: mutation.pendingEffects,
|
|
7025
|
+
effectHistory: mutation.effectHistory
|
|
6835
7026
|
};
|
|
6836
7027
|
}
|
|
6837
7028
|
|
|
6838
|
-
function
|
|
6839
|
-
|
|
7029
|
+
async function persist(ctx, mutation) {
|
|
7030
|
+
stampExecutionContext(ctx, mutation);
|
|
7031
|
+
const set = {
|
|
7032
|
+
...instanceStateFields(mutation),
|
|
7033
|
+
lastChangedAt: ctx.now
|
|
7034
|
+
}, pendingCreates = mutation.pendingCreates;
|
|
7035
|
+
if (pendingCreates.length === 0) return ctx.client.patch(ctx.instance._id).set(set).ifRevisionId(ctx.instance._rev).commit(SYNC_COMMIT);
|
|
7036
|
+
const tx = ctx.client.transaction();
|
|
7037
|
+
for (const {body: body} of pendingCreates) tx.create(body);
|
|
7038
|
+
tx.patch(ctx.client.patch(ctx.instance._id).set(set).ifRevisionId(ctx.instance._rev)),
|
|
7039
|
+
await tx.commit(), mutation.pendingCreates = [];
|
|
7040
|
+
const actorForPriming = ctx.actor;
|
|
7041
|
+
for (const {body: body, started: started} of pendingCreates) try {
|
|
7042
|
+
await primeInitialStage({
|
|
7043
|
+
client: ctx.client,
|
|
7044
|
+
instanceId: body._id,
|
|
7045
|
+
actor: actorForPriming,
|
|
7046
|
+
clientForGdr: ctx.clientForGdr,
|
|
7047
|
+
refSurface: ctx.refSurface,
|
|
7048
|
+
clock: ctx.clock,
|
|
7049
|
+
executionContext: ctx.executionContext,
|
|
7050
|
+
telemetry: ctx.telemetry
|
|
7051
|
+
}), await cascadeAutoTransitions({
|
|
7052
|
+
client: ctx.client,
|
|
7053
|
+
instanceId: body._id,
|
|
7054
|
+
actor: actorForPriming,
|
|
7055
|
+
clientForGdr: ctx.clientForGdr,
|
|
7056
|
+
refSurface: ctx.refSurface,
|
|
7057
|
+
clock: ctx.clock,
|
|
7058
|
+
executionContext: ctx.executionContext,
|
|
7059
|
+
telemetry: ctx.telemetry
|
|
7060
|
+
}), await propagateToAncestors({
|
|
7061
|
+
client: ctx.client,
|
|
7062
|
+
instanceId: body._id,
|
|
7063
|
+
actor: actorForPriming,
|
|
7064
|
+
clientForGdr: ctx.clientForGdr,
|
|
7065
|
+
refSurface: ctx.refSurface,
|
|
7066
|
+
clock: ctx.clock,
|
|
7067
|
+
executionContext: ctx.executionContext,
|
|
7068
|
+
telemetry: ctx.telemetry
|
|
7069
|
+
}), ctx.telemetry.log(WorkflowInstanceStarted, started);
|
|
7070
|
+
} catch (cause) {
|
|
7071
|
+
throw cause instanceof WorkflowStateDivergedError ? cause : new WorkflowStateDivergedError({
|
|
7072
|
+
instanceId: ctx.instance._id,
|
|
7073
|
+
guardError: cause,
|
|
7074
|
+
reason: `spawned child "${body._id}" failed to settle after the spawn transaction committed`
|
|
7075
|
+
});
|
|
7076
|
+
}
|
|
7077
|
+
const reloaded = await getInstanceDocument(ctx.client, ctx.instance._id);
|
|
7078
|
+
if (!reloaded) throw new Error(`Instance ${ctx.instance._id} disappeared after transaction commit`);
|
|
7079
|
+
return reloaded;
|
|
6840
7080
|
}
|
|
6841
7081
|
|
|
6842
|
-
|
|
6843
|
-
|
|
6844
|
-
|
|
6845
|
-
|
|
6846
|
-
constructor(args) {
|
|
6847
|
-
super("action-disabled", formatDisabledReason({
|
|
6848
|
-
activity: args.activity,
|
|
6849
|
-
action: args.action,
|
|
6850
|
-
reason: args.reason
|
|
6851
|
-
})), this.name = "ActionDisabledError", this.reason = args.reason, this.activity = args.activity,
|
|
6852
|
-
this.action = args.action;
|
|
6853
|
-
}
|
|
7082
|
+
function stampExecutionContext(ctx, mutation) {
|
|
7083
|
+
const stamp = ctx.executionContext, appended = mutation.history.slice(ctx.instance.history.length);
|
|
7084
|
+
mutation.history = [ ...mutation.history.slice(0, ctx.instance.history.length), ...stampHistoryEntries(appended, stamp) ];
|
|
7085
|
+
for (const {body: body} of mutation.pendingCreates) body.history = stampHistoryEntries(body.history, stamp);
|
|
6854
7086
|
}
|
|
6855
7087
|
|
|
6856
|
-
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
super("start-not-allowed", `startInstance refused: start.allowed on definition "${args.definition}" evaluated ` + (args.insight.outcome === "unevaluable" ? `GROQ null ("can't decide" — fail-closed)` : "false") + " for the supplied initialFields. Pre-flight the verdict with evaluateStart."),
|
|
6861
|
-
this.name = "StartNotAllowedError", this.definition = args.definition, this.insight = args.insight;
|
|
6862
|
-
}
|
|
7088
|
+
function currentStageEntry(mutation) {
|
|
7089
|
+
const entry = findOpenStageEntry(mutation);
|
|
7090
|
+
if (entry === void 0) throw new Error(`Mutation invariant broken: no current (un-exited) StageEntry for currentStage "${mutation.currentStage}"`);
|
|
7091
|
+
return entry;
|
|
6863
7092
|
}
|
|
6864
7093
|
|
|
6865
|
-
function
|
|
6866
|
-
|
|
6867
|
-
return kind === "filter-failed" ? "absent" : action.triggered === !0 || kind === "cascade-fired" ? "automation" : "button";
|
|
7094
|
+
function currentActivities(mutation) {
|
|
7095
|
+
return currentStageEntry(mutation).activities;
|
|
6868
7096
|
}
|
|
6869
7097
|
|
|
6870
|
-
|
|
6871
|
-
|
|
6872
|
-
|
|
6873
|
-
|
|
6874
|
-
|
|
6875
|
-
|
|
6876
|
-
|
|
6877
|
-
"requirements-unmet": r => `unmet requirement(s): ${r.unmetRequirements.join(", ")}`,
|
|
6878
|
-
"subject-permission-denied": r => `missing subject permission(s): ${subjectDenialLabels(r.denied).join(", ")}`
|
|
6879
|
-
};
|
|
6880
|
-
|
|
6881
|
-
function actionDisabledDetail(reason) {
|
|
6882
|
-
return disabledReasonDetail[reason.kind](reason);
|
|
7098
|
+
function findActivityInCurrentStage(ctx, activityName) {
|
|
7099
|
+
const stage = findStage(ctx.definition, ctx.instance.currentStage), activity = (stage.activities ?? []).find(t => t.name === activityName);
|
|
7100
|
+
if (activity === void 0) throw new ContractViolationError(`Activity "${activityName}" not found in current stage "${stage.name}" of ${ctx.definition.name}`);
|
|
7101
|
+
return {
|
|
7102
|
+
stage: stage,
|
|
7103
|
+
activity: activity
|
|
7104
|
+
};
|
|
6883
7105
|
}
|
|
6884
7106
|
|
|
6885
|
-
function
|
|
6886
|
-
|
|
7107
|
+
function requireMutationActivityEntry(mutation, activity) {
|
|
7108
|
+
const mutEntry = currentActivities(mutation).find(t => t.name === activity);
|
|
7109
|
+
if (mutEntry === void 0) throw new Error(`Activity "${activity}" disappeared from mutation copy — invariant broken`);
|
|
7110
|
+
return mutEntry;
|
|
6887
7111
|
}
|
|
6888
7112
|
|
|
6889
|
-
|
|
6890
|
-
|
|
6891
|
-
target;
|
|
6892
|
-
constructor(args) {
|
|
6893
|
-
super("edit-field-denied", formatEditDisabledReason(args.target, args.reason)),
|
|
6894
|
-
this.name = "EditFieldDeniedError", this.reason = args.reason, this.target = args.target;
|
|
6895
|
-
}
|
|
7113
|
+
function findCurrentStageEntry(instance) {
|
|
7114
|
+
return findOpenStageEntry(instance);
|
|
6896
7115
|
}
|
|
6897
7116
|
|
|
6898
|
-
|
|
6899
|
-
|
|
6900
|
-
"instance-completed": r => `instance completed at ${r.completedAt}`,
|
|
6901
|
-
"instance-aborted": r => `instance aborted at ${r.abortedAt}`,
|
|
6902
|
-
"edit-window-closed": r => `edit window closed (${r.detail})`,
|
|
6903
|
-
"editor-not-permitted": r => `editor not permitted (${r.predicate})`
|
|
6904
|
-
};
|
|
6905
|
-
|
|
6906
|
-
function formatEditDisabledReason(target, reason) {
|
|
6907
|
-
const detail = editDisabledReasonDetail[reason.kind](reason), where = target.activity !== void 0 ? `${target.activity}.${target.field}` : target.field;
|
|
6908
|
-
return `Field "${target.scope}:${where}" is not editable: ${detail}`;
|
|
7117
|
+
function findCurrentActivities(instance) {
|
|
7118
|
+
return findCurrentStageEntry(instance)?.activities ?? [];
|
|
6909
7119
|
}
|
|
6910
7120
|
|
|
6911
|
-
async function
|
|
6912
|
-
const {client: client, instanceId: instanceId,
|
|
7121
|
+
async function completeEffect(args) {
|
|
7122
|
+
const {client: client, instanceId: instanceId, effectKey: effectKey, status: status, outputs: outputs, ops: ops, detail: detail, error: error, durationMs: durationMs, requestRecord: requestRecord, options: options} = args;
|
|
7123
|
+
if (status !== "done" && status !== "failed") throw new ContractViolationError(`completeEffect: status must be "done" or "failed", got ${JSON.stringify(status)} — "cancelled" is engine-stamped by abort, never reportable`);
|
|
6913
7124
|
return retryOnRevisionConflict({
|
|
6914
7125
|
client: client,
|
|
6915
7126
|
instanceId: instanceId,
|
|
6916
7127
|
options: options,
|
|
6917
|
-
commit: ctx =>
|
|
7128
|
+
commit: ctx => commitCompleteEffect({
|
|
6918
7129
|
ctx: ctx,
|
|
6919
|
-
|
|
6920
|
-
|
|
6921
|
-
|
|
7130
|
+
effectKey: effectKey,
|
|
7131
|
+
status: status,
|
|
7132
|
+
outputs: outputs,
|
|
7133
|
+
ops: ops,
|
|
7134
|
+
detail: detail,
|
|
7135
|
+
error: error,
|
|
7136
|
+
durationMs: durationMs,
|
|
6922
7137
|
requestRecord: requestRecord,
|
|
6923
|
-
|
|
7138
|
+
actor: options.actor
|
|
6924
7139
|
}),
|
|
6925
|
-
onExhausted: () => new
|
|
7140
|
+
onExhausted: () => new ConcurrentCompleteEffectError({
|
|
6926
7141
|
instanceId: instanceId,
|
|
6927
|
-
|
|
6928
|
-
action: action,
|
|
7142
|
+
effectKey: effectKey,
|
|
6929
7143
|
attempts: CONCURRENT_COMMIT_MAX_ATTEMPTS
|
|
6930
7144
|
})
|
|
6931
7145
|
});
|
|
6932
7146
|
}
|
|
6933
7147
|
|
|
6934
|
-
|
|
6935
|
-
const
|
|
6936
|
-
if (action === void 0) throw new ContractViolationError(`Action "${actionName}" not declared on activity "${activityName}"`);
|
|
6937
|
-
if (action.when !== void 0) throw new ActionDisabledError({
|
|
6938
|
-
activity: activityName,
|
|
6939
|
-
action: actionName,
|
|
6940
|
-
reason: {
|
|
6941
|
-
kind: "cascade-fired",
|
|
6942
|
-
when: action.when
|
|
6943
|
-
}
|
|
6944
|
-
});
|
|
6945
|
-
if (action.filter !== void 0) {
|
|
6946
|
-
const can = options?.grants !== void 0 && actor !== void 0 ? await advisoryCan({
|
|
6947
|
-
instance: ctx.instance,
|
|
6948
|
-
actor: actor,
|
|
6949
|
-
grants: options.grants
|
|
6950
|
-
}) : void 0;
|
|
6951
|
-
if (!await ctxEvaluateCondition({
|
|
6952
|
-
ctx: ctx,
|
|
6953
|
-
condition: action.filter,
|
|
6954
|
-
opts: {
|
|
6955
|
-
activityName: activityName,
|
|
6956
|
-
...actor !== void 0 ? {
|
|
6957
|
-
actor: actor
|
|
6958
|
-
} : {},
|
|
6959
|
-
...can !== void 0 ? {
|
|
6960
|
-
vars: {
|
|
6961
|
-
can: can
|
|
6962
|
-
}
|
|
6963
|
-
} : {}
|
|
6964
|
-
}
|
|
6965
|
-
})) throw new ActionDisabledError({
|
|
6966
|
-
activity: activityName,
|
|
6967
|
-
action: actionName,
|
|
6968
|
-
reason: {
|
|
6969
|
-
kind: "filter-failed",
|
|
6970
|
-
filter: action.filter,
|
|
6971
|
-
detail: "commit re-check"
|
|
6972
|
-
}
|
|
6973
|
-
});
|
|
6974
|
-
}
|
|
6975
|
-
const entry = findCurrentActivities(ctx.instance).find(t => t.name === activityName);
|
|
6976
|
-
if (entry === void 0 || entry.status !== "active") throw notActiveAtCommit({
|
|
6977
|
-
activityName: activityName,
|
|
6978
|
-
actionName: actionName,
|
|
6979
|
-
status: entry?.status
|
|
6980
|
-
});
|
|
6981
|
-
const params = validateActionParams({
|
|
6982
|
-
action: action,
|
|
6983
|
-
activityName: activityName,
|
|
6984
|
-
callerParams: callerParams
|
|
6985
|
-
});
|
|
7148
|
+
function buildEffectHistoryEntry(pending, outcome) {
|
|
7149
|
+
const {status: status, ranAt: ranAt, actor: actor, detail: detail, error: error, durationMs: durationMs, outputs: outputs} = outcome, resolvedActor = actor ?? pending.actor;
|
|
6986
7150
|
return {
|
|
6987
|
-
|
|
6988
|
-
|
|
6989
|
-
|
|
6990
|
-
|
|
7151
|
+
_key: pending._key,
|
|
7152
|
+
name: pending.name,
|
|
7153
|
+
...pending.title !== void 0 ? {
|
|
7154
|
+
title: pending.title
|
|
7155
|
+
} : {},
|
|
7156
|
+
...pending.description !== void 0 ? {
|
|
7157
|
+
description: pending.description
|
|
7158
|
+
} : {},
|
|
7159
|
+
params: pending.params,
|
|
7160
|
+
origin: pending.origin,
|
|
7161
|
+
...resolvedActor !== void 0 ? {
|
|
7162
|
+
actor: resolvedActor
|
|
7163
|
+
} : {},
|
|
7164
|
+
...pending.stageEntryKey !== void 0 ? {
|
|
7165
|
+
stageEntryKey: pending.stageEntryKey
|
|
7166
|
+
} : {},
|
|
7167
|
+
ranAt: ranAt,
|
|
7168
|
+
...durationMs !== void 0 ? {
|
|
7169
|
+
durationMs: durationMs
|
|
7170
|
+
} : {},
|
|
7171
|
+
status: status,
|
|
7172
|
+
...detail !== void 0 ? {
|
|
7173
|
+
detail: detail
|
|
7174
|
+
} : {},
|
|
7175
|
+
...error !== void 0 ? {
|
|
7176
|
+
error: error
|
|
7177
|
+
} : {},
|
|
7178
|
+
...outputs !== void 0 ? {
|
|
7179
|
+
outputs: outputs
|
|
7180
|
+
} : {}
|
|
6991
7181
|
};
|
|
6992
7182
|
}
|
|
6993
7183
|
|
|
6994
|
-
function
|
|
6995
|
-
const {
|
|
6996
|
-
return
|
|
6997
|
-
|
|
6998
|
-
|
|
6999
|
-
|
|
7000
|
-
|
|
7001
|
-
|
|
7184
|
+
function buildEffectSettlement(pending, outcome) {
|
|
7185
|
+
const {status: status, ranAt: ranAt, actor: actor, detail: detail, outputs: outputs} = outcome;
|
|
7186
|
+
return {
|
|
7187
|
+
run: buildEffectHistoryEntry(pending, outcome),
|
|
7188
|
+
event: {
|
|
7189
|
+
_key: randomKey(),
|
|
7190
|
+
_type: "effectCompleted",
|
|
7191
|
+
at: ranAt,
|
|
7192
|
+
effectKey: pending._key,
|
|
7193
|
+
effect: pending.name,
|
|
7194
|
+
status: status,
|
|
7195
|
+
...outputs !== void 0 ? {
|
|
7196
|
+
outputs: outputs
|
|
7197
|
+
} : {},
|
|
7198
|
+
...detail !== void 0 ? {
|
|
7199
|
+
detail: detail
|
|
7200
|
+
} : {},
|
|
7201
|
+
...actor !== void 0 ? {
|
|
7202
|
+
actor: actor
|
|
7203
|
+
} : {}
|
|
7002
7204
|
}
|
|
7003
|
-
}
|
|
7205
|
+
};
|
|
7004
7206
|
}
|
|
7005
7207
|
|
|
7006
|
-
|
|
7007
|
-
|
|
7008
|
-
|
|
7009
|
-
|
|
7010
|
-
|
|
7011
|
-
|
|
7012
|
-
|
|
7013
|
-
|
|
7014
|
-
|
|
7015
|
-
|
|
7016
|
-
|
|
7208
|
+
function validateEffectOutputs(args) {
|
|
7209
|
+
const {outputs: outputs, declared: declared, effectName: effectName} = args, byName = new Map(declared.map(shape => [ shape.name, shape ])), issues = [];
|
|
7210
|
+
for (const [key, value] of Object.entries(outputs)) {
|
|
7211
|
+
const shape = byName.get(key);
|
|
7212
|
+
if (shape === void 0) {
|
|
7213
|
+
issues.push(`"${key}" is not a declared output`);
|
|
7214
|
+
continue;
|
|
7215
|
+
}
|
|
7216
|
+
const shapeIssues = effectOutputIssues(shape, value);
|
|
7217
|
+
shapeIssues !== void 0 && issues.push(...shapeIssues.map(i => `"${key}": ${i}`));
|
|
7218
|
+
}
|
|
7219
|
+
if (issues.length > 0) throw new EffectOutputsInvalidError({
|
|
7220
|
+
effect: effectName,
|
|
7221
|
+
issues: issues
|
|
7222
|
+
});
|
|
7223
|
+
}
|
|
7224
|
+
|
|
7225
|
+
function effectOutputIssues(shape, value) {
|
|
7226
|
+
return checkFieldValue({
|
|
7227
|
+
entryType: shape.type,
|
|
7228
|
+
value: value,
|
|
7229
|
+
...shape.fields !== void 0 ? {
|
|
7230
|
+
fields: shape.fields
|
|
7017
7231
|
} : {},
|
|
7018
|
-
...
|
|
7019
|
-
|
|
7232
|
+
...shape.of !== void 0 ? {
|
|
7233
|
+
of: shape.of
|
|
7234
|
+
} : {},
|
|
7235
|
+
...shape.options !== void 0 ? {
|
|
7236
|
+
options: shape.options
|
|
7237
|
+
} : {},
|
|
7238
|
+
...shape.validation !== void 0 ? {
|
|
7239
|
+
validation: shape.validation
|
|
7240
|
+
} : {}
|
|
7241
|
+
});
|
|
7242
|
+
}
|
|
7243
|
+
|
|
7244
|
+
function requirePendingEffect(instance, effectKey) {
|
|
7245
|
+
const pending = instance.pendingEffects.find(e => e._key === effectKey);
|
|
7246
|
+
if (pending !== void 0) return pending;
|
|
7247
|
+
const run = instance.effectHistory.find(e => e._key === effectKey), settled = run !== void 0 ? {
|
|
7248
|
+
status: run.status,
|
|
7249
|
+
ranAt: run.ranAt,
|
|
7250
|
+
...run.detail !== void 0 ? {
|
|
7251
|
+
detail: run.detail
|
|
7252
|
+
} : {}
|
|
7253
|
+
} : void 0;
|
|
7254
|
+
throw new EffectNotFoundError({
|
|
7255
|
+
instanceId: instance._id,
|
|
7256
|
+
effectKey: effectKey,
|
|
7257
|
+
...settled !== void 0 ? {
|
|
7258
|
+
settled: settled
|
|
7020
7259
|
} : {}
|
|
7021
7260
|
});
|
|
7022
|
-
|
|
7023
|
-
|
|
7024
|
-
|
|
7025
|
-
|
|
7026
|
-
|
|
7027
|
-
|
|
7028
|
-
action: action.name
|
|
7029
|
-
},
|
|
7030
|
-
params: params,
|
|
7031
|
-
actor: actor,
|
|
7032
|
-
self: selfGdr(ctx.instance),
|
|
7033
|
-
now: ctx.now,
|
|
7034
|
-
snapshot: ctx.snapshot,
|
|
7035
|
-
refSurface: ctx.refSurface
|
|
7261
|
+
}
|
|
7262
|
+
|
|
7263
|
+
function validateCompletionInput({pending: pending, definition: definition, status: status, ops: ops, outputs: outputs}) {
|
|
7264
|
+
if (status === "failed" && ops !== void 0 && ops.length > 0) throw new EffectOpsInvalidError({
|
|
7265
|
+
effect: pending.name,
|
|
7266
|
+
issues: [ "ops cannot accompany a failed completion — field.set the outcome on a done completion instead" ]
|
|
7036
7267
|
});
|
|
7037
|
-
|
|
7038
|
-
|
|
7039
|
-
|
|
7040
|
-
|
|
7041
|
-
|
|
7042
|
-
|
|
7043
|
-
|
|
7044
|
-
|
|
7045
|
-
|
|
7046
|
-
|
|
7047
|
-
|
|
7048
|
-
|
|
7049
|
-
|
|
7050
|
-
}), action.spawn !== void 0 && await spawnSubworkflows({
|
|
7051
|
-
ctx: ctx,
|
|
7052
|
-
mutation: mutation,
|
|
7053
|
-
activity: activity,
|
|
7054
|
-
action: action,
|
|
7055
|
-
sub: action.spawn,
|
|
7056
|
-
actor: actor
|
|
7057
|
-
}), {
|
|
7058
|
-
ranOps: ranOps
|
|
7059
|
-
};
|
|
7268
|
+
if (status === "failed" && outputs !== void 0) throw new EffectOutputsInvalidError({
|
|
7269
|
+
effect: pending.name,
|
|
7270
|
+
issues: [ "outputs cannot accompany a failed completion — report outputs on a done completion instead" ]
|
|
7271
|
+
});
|
|
7272
|
+
if (outputs !== void 0) {
|
|
7273
|
+
const declared = findEffect(definition, pending.name)?.outputs ?? [];
|
|
7274
|
+
validateEffectOutputs({
|
|
7275
|
+
outputs: outputs,
|
|
7276
|
+
declared: declared,
|
|
7277
|
+
effectName: pending.name
|
|
7278
|
+
});
|
|
7279
|
+
}
|
|
7280
|
+
return ops !== void 0 ? validateEffectOps(ops, pending.name) : [];
|
|
7060
7281
|
}
|
|
7061
7282
|
|
|
7062
|
-
async function
|
|
7283
|
+
async function commitCompleteEffect({ctx: ctx, effectKey: effectKey, status: status, outputs: outputs, ops: ops, detail: detail, error: error, durationMs: durationMs, requestRecord: requestRecord, actor: actor}) {
|
|
7063
7284
|
assertRequestUnprocessed({
|
|
7064
7285
|
instance: ctx.instance,
|
|
7065
7286
|
record: requestRecord,
|
|
7066
7287
|
now: ctx.now
|
|
7067
7288
|
});
|
|
7068
|
-
const
|
|
7069
|
-
|
|
7070
|
-
|
|
7071
|
-
|
|
7072
|
-
|
|
7073
|
-
|
|
7289
|
+
const pending = requirePendingEffect(ctx.instance, effectKey), validatedOps = validateCompletionInput({
|
|
7290
|
+
pending: pending,
|
|
7291
|
+
definition: ctx.definition,
|
|
7292
|
+
status: status,
|
|
7293
|
+
ops: ops,
|
|
7294
|
+
outputs: outputs
|
|
7074
7295
|
}), mutation = startMutation(ctx.instance);
|
|
7075
7296
|
recordProcessedRequest({
|
|
7076
7297
|
mutation: mutation,
|
|
7077
7298
|
record: requestRecord,
|
|
7078
7299
|
now: ctx.now
|
|
7300
|
+
}), mutation.pendingEffects = mutation.pendingEffects.filter(e => e._key !== effectKey);
|
|
7301
|
+
const ranAt = ctx.now, settlement = buildEffectSettlement(pending, {
|
|
7302
|
+
status: status,
|
|
7303
|
+
ranAt: ranAt,
|
|
7304
|
+
actor: actor,
|
|
7305
|
+
detail: detail,
|
|
7306
|
+
error: error,
|
|
7307
|
+
durationMs: durationMs,
|
|
7308
|
+
outputs: outputs
|
|
7079
7309
|
});
|
|
7080
|
-
|
|
7081
|
-
|
|
7310
|
+
mutation.effectHistory.push(settlement.run);
|
|
7311
|
+
const wroteEffectOutputs = status === "done" && outputs !== void 0;
|
|
7312
|
+
mutation.history.push(settlement.event);
|
|
7313
|
+
const ranOps = await runOps({
|
|
7314
|
+
ops: validatedOps,
|
|
7082
7315
|
mutation: mutation,
|
|
7083
|
-
|
|
7084
|
-
|
|
7085
|
-
|
|
7316
|
+
stage: ctx.instance.currentStage,
|
|
7317
|
+
origin: {
|
|
7318
|
+
effect: pending.name
|
|
7319
|
+
},
|
|
7320
|
+
params: pending.params,
|
|
7086
7321
|
actor: actor,
|
|
7087
|
-
|
|
7088
|
-
|
|
7322
|
+
self: selfGdr(ctx.instance),
|
|
7323
|
+
now: ranAt,
|
|
7324
|
+
snapshot: ctx.snapshot,
|
|
7325
|
+
refSurface: ctx.refSurface
|
|
7326
|
+
}), needsGuardRefresh = wroteEffectOutputs || ranOps.some(isFieldOp);
|
|
7089
7327
|
return await persistThenMaybeRefresh({
|
|
7090
7328
|
ctx: ctx,
|
|
7091
7329
|
mutation: mutation,
|
|
7092
|
-
stageName:
|
|
7093
|
-
didChangeState:
|
|
7330
|
+
stageName: ctx.instance.currentStage,
|
|
7331
|
+
didChangeState: needsGuardRefresh
|
|
7094
7332
|
}), {
|
|
7095
|
-
|
|
7096
|
-
|
|
7097
|
-
|
|
7098
|
-
|
|
7099
|
-
newStatus: newStatus
|
|
7100
|
-
} : {},
|
|
7101
|
-
...ranOps.length > 0 ? {
|
|
7102
|
-
ranOps: ranOps
|
|
7103
|
-
} : {}
|
|
7104
|
-
};
|
|
7105
|
-
}
|
|
7106
|
-
|
|
7107
|
-
async function runTriggeredActions({ctx: ctx, mutation: mutation, stage: stage}) {
|
|
7108
|
-
let fires = 0, ranFieldOps = !1;
|
|
7109
|
-
const liveCtx = liveViewContext(ctx, mutation);
|
|
7110
|
-
let firedThisPass = !0;
|
|
7111
|
-
for (;firedThisPass; ) {
|
|
7112
|
-
firedThisPass = !1;
|
|
7113
|
-
for (const activity of stage.activities ?? []) {
|
|
7114
|
-
const fired = await runActivityTriggers({
|
|
7115
|
-
ctx: liveCtx,
|
|
7116
|
-
mutation: mutation,
|
|
7117
|
-
activity: activity
|
|
7118
|
-
});
|
|
7119
|
-
fires += fired.fires, ranFieldOps = ranFieldOps || fired.ranFieldOps, fired.fires > 0 && (firedThisPass = !0);
|
|
7120
|
-
}
|
|
7121
|
-
}
|
|
7122
|
-
return {
|
|
7123
|
-
fires: fires,
|
|
7124
|
-
ranFieldOps: ranFieldOps
|
|
7125
|
-
};
|
|
7126
|
-
}
|
|
7127
|
-
|
|
7128
|
-
async function runActivityTriggers({ctx: ctx, mutation: mutation, activity: activity}) {
|
|
7129
|
-
let fires = 0, ranFieldOps = !1;
|
|
7130
|
-
for (const action of activity.actions ?? []) {
|
|
7131
|
-
if (!isCascadeFired(action)) continue;
|
|
7132
|
-
const entry = currentActivities(mutation).find(e => e.name === activity.name);
|
|
7133
|
-
if (entry === void 0 || entry.status !== "active") break;
|
|
7134
|
-
if ((entry.firedActions ?? []).includes(action.name) || !await triggerIsLive({
|
|
7135
|
-
ctx: ctx,
|
|
7136
|
-
activity: activity,
|
|
7137
|
-
action: action
|
|
7138
|
-
})) continue;
|
|
7139
|
-
const result = await fireTriggeredAction({
|
|
7140
|
-
ctx: ctx,
|
|
7141
|
-
mutation: mutation,
|
|
7142
|
-
activity: activity,
|
|
7143
|
-
action: action,
|
|
7144
|
-
entry: entry
|
|
7145
|
-
});
|
|
7146
|
-
if (fires++, ranFieldOps = ranFieldOps || result.ranFieldOps, isTerminalActivityStatus(entry.status)) break;
|
|
7147
|
-
}
|
|
7148
|
-
return {
|
|
7149
|
-
fires: fires,
|
|
7150
|
-
ranFieldOps: ranFieldOps
|
|
7333
|
+
effectKey: effectKey,
|
|
7334
|
+
effect: pending.name,
|
|
7335
|
+
status: status,
|
|
7336
|
+
origin: pending.origin.kind
|
|
7151
7337
|
};
|
|
7152
7338
|
}
|
|
7153
7339
|
|
|
7154
|
-
|
|
7155
|
-
|
|
7156
|
-
|
|
7157
|
-
|
|
7158
|
-
|
|
7159
|
-
|
|
7160
|
-
|
|
7161
|
-
|
|
7340
|
+
function buildQueuedEffect({effect: effect, origin: origin, params: params, actor: actor, now: now, stageEntryKey: stageEntryKey}) {
|
|
7341
|
+
const key = randomKey(), pending = {
|
|
7342
|
+
_key: key,
|
|
7343
|
+
_type: "pendingEffect",
|
|
7344
|
+
name: effect.name,
|
|
7345
|
+
...effect.title !== void 0 ? {
|
|
7346
|
+
title: effect.title
|
|
7347
|
+
} : {},
|
|
7348
|
+
...effect.description !== void 0 ? {
|
|
7349
|
+
description: effect.description
|
|
7350
|
+
} : {},
|
|
7351
|
+
...effect.bindings !== void 0 ? {
|
|
7352
|
+
bindings: effect.bindings
|
|
7353
|
+
} : {},
|
|
7354
|
+
params: params,
|
|
7355
|
+
origin: origin,
|
|
7356
|
+
...actor !== void 0 ? {
|
|
7357
|
+
actor: actor
|
|
7358
|
+
} : {},
|
|
7359
|
+
queuedAt: now,
|
|
7360
|
+
...stageEntryKey !== void 0 ? {
|
|
7361
|
+
stageEntryKey: stageEntryKey
|
|
7362
|
+
} : {}
|
|
7363
|
+
}, history = {
|
|
7364
|
+
_key: randomKey(),
|
|
7365
|
+
_type: "effectQueued",
|
|
7366
|
+
at: now,
|
|
7367
|
+
effectKey: key,
|
|
7368
|
+
effect: effect.name,
|
|
7369
|
+
origin: origin
|
|
7162
7370
|
};
|
|
7163
|
-
return action.filter !== void 0 && await ctxEvaluateConditionOutcome({
|
|
7164
|
-
ctx: ctx,
|
|
7165
|
-
condition: action.filter,
|
|
7166
|
-
opts: scope
|
|
7167
|
-
}) !== "satisfied" ? !1 : await ctxEvaluateConditionOutcome({
|
|
7168
|
-
ctx: ctx,
|
|
7169
|
-
condition: action.when,
|
|
7170
|
-
opts: scope
|
|
7171
|
-
}) === "satisfied";
|
|
7172
|
-
}
|
|
7173
|
-
|
|
7174
|
-
function tokenMayExecute({actor: actor, action: action, ctx: ctx}) {
|
|
7175
|
-
const roles = action.roles;
|
|
7176
|
-
return roles === void 0 || roles.length === 0 ? !0 : actor === void 0 ? !1 : roles.some(required => actorFulfillsRole({
|
|
7177
|
-
actorRoles: actor.roles,
|
|
7178
|
-
required: required,
|
|
7179
|
-
aliases: ctx.definition.roleAliases
|
|
7180
|
-
}));
|
|
7181
|
-
}
|
|
7182
|
-
|
|
7183
|
-
async function fireTriggeredAction({ctx: ctx, mutation: mutation, activity: activity, action: action, entry: entry}) {
|
|
7184
|
-
entry.firedActions = [ ...entry.firedActions ?? [], action.name ];
|
|
7185
|
-
const {ranOps: ranOps} = await applyActionFire({
|
|
7186
|
-
ctx: ctx,
|
|
7187
|
-
mutation: mutation,
|
|
7188
|
-
activity: activity,
|
|
7189
|
-
action: action,
|
|
7190
|
-
params: {},
|
|
7191
|
-
actor: ctx.actor,
|
|
7192
|
-
triggered: !0
|
|
7193
|
-
});
|
|
7194
7371
|
return {
|
|
7195
|
-
|
|
7372
|
+
pending: pending,
|
|
7373
|
+
history: history
|
|
7196
7374
|
};
|
|
7197
7375
|
}
|
|
7198
7376
|
|
|
7199
|
-
async function
|
|
7200
|
-
|
|
7201
|
-
|
|
7202
|
-
|
|
7203
|
-
|
|
7204
|
-
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
refSurface: refSurface,
|
|
7208
|
-
instance: instance,
|
|
7209
|
-
definition: definition,
|
|
7210
|
-
...clock ? {
|
|
7211
|
-
clock: clock
|
|
7377
|
+
async function queueEffects({ctx: ctx, mutation: mutation, effects: effects, origin: origin, actor: actor, opts: opts}) {
|
|
7378
|
+
if (!effects || effects.length === 0) return;
|
|
7379
|
+
const now = ctx.now, liveCtx = {
|
|
7380
|
+
...ctx,
|
|
7381
|
+
instance: materializeInstance(ctx.instance, mutation)
|
|
7382
|
+
}, stageEntryKey = opts?.stageEntryKey ?? findOpenStageEntry(liveCtx.instance)?._key, params = await ctxConditionParams(liveCtx, {
|
|
7383
|
+
...opts?.activityName !== void 0 ? {
|
|
7384
|
+
activityName: opts.activityName
|
|
7212
7385
|
} : {},
|
|
7213
|
-
...actor ? {
|
|
7386
|
+
...actor !== void 0 ? {
|
|
7214
7387
|
actor: actor
|
|
7215
7388
|
} : {},
|
|
7216
|
-
|
|
7217
|
-
|
|
7218
|
-
} : {},
|
|
7219
|
-
...telemetry ? {
|
|
7220
|
-
telemetry: telemetry
|
|
7221
|
-
} : {}
|
|
7222
|
-
}), now = ctx.now, discards = [], initialStageEntry = {
|
|
7223
|
-
_key: randomKey(),
|
|
7224
|
-
name: stage.name,
|
|
7225
|
-
enteredAt: now,
|
|
7226
|
-
fields: await resolveStageFieldEntries({
|
|
7227
|
-
client: client,
|
|
7228
|
-
instance: instance,
|
|
7229
|
-
stage: stage,
|
|
7230
|
-
now: now,
|
|
7231
|
-
refSurface: refSurface,
|
|
7232
|
-
recordDiscard: recordFieldDiscards({
|
|
7233
|
-
target: discards,
|
|
7234
|
-
scope: "stage",
|
|
7235
|
-
at: now
|
|
7236
|
-
})
|
|
7237
|
-
}),
|
|
7238
|
-
activities: []
|
|
7239
|
-
}, primedCtx = {
|
|
7240
|
-
...ctx,
|
|
7241
|
-
instance: {
|
|
7242
|
-
...instance,
|
|
7243
|
-
stages: [ initialStageEntry ]
|
|
7389
|
+
vars: {
|
|
7390
|
+
params: opts?.callerParams ?? {}
|
|
7244
7391
|
}
|
|
7245
|
-
};
|
|
7246
|
-
initialStageEntry.activities = await buildStageActivities({
|
|
7247
|
-
ctx: primedCtx,
|
|
7248
|
-
stage: stage,
|
|
7249
|
-
recordDiscard: recordFieldDiscards({
|
|
7250
|
-
target: discards,
|
|
7251
|
-
scope: "activity",
|
|
7252
|
-
at: now
|
|
7253
|
-
})
|
|
7254
7392
|
});
|
|
7255
|
-
const
|
|
7256
|
-
|
|
7257
|
-
|
|
7258
|
-
|
|
7259
|
-
|
|
7260
|
-
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
client: client,
|
|
7267
|
-
instanceId: instance._id,
|
|
7268
|
-
committedRev: committed._rev,
|
|
7269
|
-
restore: {
|
|
7270
|
-
stages: instance.stages,
|
|
7271
|
-
history: instance.history
|
|
7272
|
-
},
|
|
7273
|
-
...terminal ? {
|
|
7274
|
-
unset: [ "completedAt" ]
|
|
7275
|
-
} : {},
|
|
7276
|
-
reversible: !0,
|
|
7277
|
-
deploy: () => deployStageGuards({
|
|
7278
|
-
client: client,
|
|
7279
|
-
clientForGdr: clientForGdr,
|
|
7280
|
-
instance: instance,
|
|
7281
|
-
definition: definition,
|
|
7282
|
-
stageName: stage.name,
|
|
7393
|
+
for (const effect of effects) {
|
|
7394
|
+
const resolved = await resolveBindings({
|
|
7395
|
+
bindings: effect.bindings,
|
|
7396
|
+
staticInput: effect.input,
|
|
7397
|
+
snapshot: ctx.snapshot,
|
|
7398
|
+
params: params
|
|
7399
|
+
}), {pending: pending, history: history} = buildQueuedEffect({
|
|
7400
|
+
effect: effect,
|
|
7401
|
+
origin: origin,
|
|
7402
|
+
params: resolved,
|
|
7403
|
+
actor: actor,
|
|
7283
7404
|
now: now,
|
|
7284
|
-
|
|
7405
|
+
stageEntryKey: stageEntryKey
|
|
7406
|
+
});
|
|
7407
|
+
mutation.pendingEffects.push(pending), mutation.history.push(history);
|
|
7408
|
+
}
|
|
7409
|
+
}
|
|
7410
|
+
|
|
7411
|
+
async function commitEffectOps(args) {
|
|
7412
|
+
const {client: client, instanceId: instanceId, effectKey: effectKey, claimToken: claimToken, ops: ops, requestRecord: requestRecord, leaseMs: leaseMs, options: options} = args;
|
|
7413
|
+
return retryOnRevisionConflict({
|
|
7414
|
+
client: client,
|
|
7415
|
+
instanceId: instanceId,
|
|
7416
|
+
options: options,
|
|
7417
|
+
commit: ctx => commitReport({
|
|
7418
|
+
ctx: ctx,
|
|
7419
|
+
effectKey: effectKey,
|
|
7420
|
+
claimToken: claimToken,
|
|
7421
|
+
ops: ops,
|
|
7422
|
+
requestRecord: requestRecord,
|
|
7423
|
+
leaseMs: leaseMs
|
|
7424
|
+
}),
|
|
7425
|
+
onExhausted: () => new ConcurrentCommitEffectOpsError({
|
|
7426
|
+
instanceId: instanceId,
|
|
7427
|
+
effectKey: effectKey,
|
|
7428
|
+
attempts: CONCURRENT_COMMIT_MAX_ATTEMPTS
|
|
7285
7429
|
})
|
|
7286
7430
|
});
|
|
7287
7431
|
}
|
|
7288
7432
|
|
|
7289
|
-
|
|
7433
|
+
function staleClaimReason(args) {
|
|
7434
|
+
const claim = args.pending.claim;
|
|
7435
|
+
if (claim === void 0) return "unclaimed";
|
|
7436
|
+
if (claim.claimToken !== args.claimToken) return "claim-superseded";
|
|
7437
|
+
if (isClaimExpired(claim, args.now)) return "lease-expired";
|
|
7438
|
+
}
|
|
7290
7439
|
|
|
7291
|
-
|
|
7292
|
-
const
|
|
7293
|
-
|
|
7294
|
-
|
|
7295
|
-
|
|
7296
|
-
|
|
7297
|
-
|
|
7298
|
-
...actor ? {
|
|
7299
|
-
actor: actor
|
|
7300
|
-
} : {},
|
|
7301
|
-
...clock ? {
|
|
7302
|
-
clock: clock
|
|
7303
|
-
} : {},
|
|
7304
|
-
...executionContext ? {
|
|
7305
|
-
executionContext: executionContext
|
|
7306
|
-
} : {},
|
|
7307
|
-
...telemetry ? {
|
|
7308
|
-
telemetry: telemetry
|
|
7309
|
-
} : {},
|
|
7310
|
-
...overlay ? {
|
|
7311
|
-
overlay: overlay
|
|
7312
|
-
} : {}
|
|
7440
|
+
function renewClaimLease(args) {
|
|
7441
|
+
const {mutation: mutation, effectKey: effectKey, now: now, leaseMs: leaseMs} = args;
|
|
7442
|
+
mutation.pendingEffects = mutation.pendingEffects.map(entry => entry._key === effectKey && entry.claim !== void 0 ? {
|
|
7443
|
+
...entry,
|
|
7444
|
+
claim: {
|
|
7445
|
+
...entry.claim,
|
|
7446
|
+
leaseExpiresAt: addMs(now, leaseMs)
|
|
7313
7447
|
}
|
|
7448
|
+
} : entry);
|
|
7449
|
+
}
|
|
7450
|
+
|
|
7451
|
+
async function commitReport({ctx: ctx, effectKey: effectKey, claimToken: claimToken, ops: ops, requestRecord: requestRecord, leaseMs: leaseMs}) {
|
|
7452
|
+
assertRequestUnprocessed({
|
|
7453
|
+
instance: ctx.instance,
|
|
7454
|
+
record: requestRecord,
|
|
7455
|
+
now: ctx.now
|
|
7314
7456
|
});
|
|
7315
|
-
|
|
7316
|
-
|
|
7317
|
-
|
|
7318
|
-
|
|
7319
|
-
|
|
7457
|
+
const pending = requirePendingEffect(ctx.instance, effectKey), reason = staleClaimReason({
|
|
7458
|
+
pending: pending,
|
|
7459
|
+
claimToken: claimToken,
|
|
7460
|
+
now: ctx.now
|
|
7461
|
+
});
|
|
7462
|
+
if (reason !== void 0) throw new StaleEffectClaimError({
|
|
7463
|
+
instanceId: ctx.instance._id,
|
|
7464
|
+
effectKey: effectKey,
|
|
7465
|
+
reason: reason
|
|
7466
|
+
});
|
|
7467
|
+
const validatedOps = validateEffectOps(ops, pending.name);
|
|
7468
|
+
if (validatedOps.length === 0) throw new EffectOpsInvalidError({
|
|
7469
|
+
effect: pending.name,
|
|
7470
|
+
issues: [ "a mid-dispatch report must carry at least one field op — there is nothing to commit" ]
|
|
7471
|
+
});
|
|
7472
|
+
const mutation = startMutation(ctx.instance);
|
|
7473
|
+
return recordProcessedRequest({
|
|
7320
7474
|
mutation: mutation,
|
|
7321
|
-
|
|
7322
|
-
|
|
7323
|
-
|
|
7324
|
-
ctx: ctx,
|
|
7475
|
+
record: requestRecord,
|
|
7476
|
+
now: ctx.now
|
|
7477
|
+
}), renewClaimLease({
|
|
7325
7478
|
mutation: mutation,
|
|
7326
|
-
|
|
7327
|
-
|
|
7328
|
-
|
|
7329
|
-
|
|
7330
|
-
|
|
7479
|
+
effectKey: effectKey,
|
|
7480
|
+
now: ctx.now,
|
|
7481
|
+
leaseMs: leaseMs
|
|
7482
|
+
}), await runOps({
|
|
7483
|
+
ops: validatedOps,
|
|
7484
|
+
mutation: mutation,
|
|
7485
|
+
stage: ctx.instance.currentStage,
|
|
7486
|
+
origin: {
|
|
7487
|
+
effect: pending.name
|
|
7488
|
+
},
|
|
7489
|
+
params: pending.params,
|
|
7490
|
+
actor: ctx.actor,
|
|
7491
|
+
self: selfGdr(ctx.instance),
|
|
7492
|
+
now: ctx.now,
|
|
7493
|
+
snapshot: ctx.snapshot,
|
|
7494
|
+
refSurface: ctx.refSurface
|
|
7495
|
+
}), await persistThenMaybeRefresh({
|
|
7331
7496
|
ctx: ctx,
|
|
7332
7497
|
mutation: mutation,
|
|
7333
|
-
|
|
7334
|
-
|
|
7335
|
-
transition: transition.name,
|
|
7336
|
-
via: "transition",
|
|
7337
|
-
actor: actor
|
|
7498
|
+
stageName: ctx.instance.currentStage,
|
|
7499
|
+
didChangeState: !0
|
|
7338
7500
|
}), {
|
|
7339
|
-
|
|
7501
|
+
effectKey: effectKey,
|
|
7502
|
+
effect: pending.name
|
|
7503
|
+
};
|
|
7504
|
+
}
|
|
7505
|
+
|
|
7506
|
+
const actorCache = /* @__PURE__ */ new WeakMap, grantsCache = /* @__PURE__ */ new WeakMap;
|
|
7507
|
+
|
|
7508
|
+
async function resolveAccess(taggedClient, args = {}) {
|
|
7509
|
+
const client = unwrapRequestTag(taggedClient), requestFn = lazyRequest(client);
|
|
7510
|
+
if (requestFn === void 0) throw new 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).");
|
|
7511
|
+
const grantsPromise = args.grantsFromPath !== void 0 ? cachedGrants({
|
|
7512
|
+
client: client,
|
|
7513
|
+
requestFn: requestFn,
|
|
7514
|
+
resourcePath: args.grantsFromPath
|
|
7515
|
+
}) : Promise.resolve(void 0), [actor, grants] = await Promise.all([ cachedActor(client, requestFn), grantsPromise ]);
|
|
7516
|
+
if (actor === void 0) throw new ContractViolationError("workflow: failed to resolve actor from `/users/me`. The client is configured but the endpoint returned no usable identity — check the token.");
|
|
7517
|
+
return {
|
|
7518
|
+
actor: actor,
|
|
7519
|
+
...grants !== void 0 ? {
|
|
7520
|
+
grants: grants
|
|
7521
|
+
} : {}
|
|
7522
|
+
};
|
|
7523
|
+
}
|
|
7524
|
+
|
|
7525
|
+
function cachedActor(client, requestFn) {
|
|
7526
|
+
const cached = actorCache.get(client);
|
|
7527
|
+
if (cached !== void 0) return cached;
|
|
7528
|
+
const pending = fetchActor(requestFn).catch(err => {
|
|
7529
|
+
throw actorCache.get(client) === pending && actorCache.delete(client), err;
|
|
7340
7530
|
});
|
|
7531
|
+
return actorCache.set(client, pending), pending;
|
|
7341
7532
|
}
|
|
7342
7533
|
|
|
7343
|
-
|
|
7344
|
-
|
|
7345
|
-
|
|
7346
|
-
|
|
7347
|
-
|
|
7348
|
-
|
|
7349
|
-
|
|
7350
|
-
|
|
7351
|
-
|
|
7352
|
-
|
|
7353
|
-
|
|
7354
|
-
|
|
7355
|
-
|
|
7356
|
-
|
|
7357
|
-
|
|
7358
|
-
|
|
7359
|
-
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
|
|
7364
|
-
|
|
7365
|
-
|
|
7366
|
-
|
|
7367
|
-
|
|
7368
|
-
|
|
7534
|
+
function grantsForClientPath(taggedClient, resourcePath) {
|
|
7535
|
+
const client = unwrapRequestTag(taggedClient), requestFn = lazyRequest(client);
|
|
7536
|
+
return requestFn === void 0 ? Promise.resolve(void 0) : cachedGrants({
|
|
7537
|
+
client: client,
|
|
7538
|
+
requestFn: requestFn,
|
|
7539
|
+
resourcePath: resourcePath
|
|
7540
|
+
});
|
|
7541
|
+
}
|
|
7542
|
+
|
|
7543
|
+
function lazyRequest(client) {
|
|
7544
|
+
return client.request === void 0 ? void 0 : opts => client.request(opts);
|
|
7545
|
+
}
|
|
7546
|
+
|
|
7547
|
+
function cachedGrants({client: client, requestFn: requestFn, resourcePath: resourcePath}) {
|
|
7548
|
+
let byPath = grantsCache.get(client);
|
|
7549
|
+
byPath === void 0 && (byPath = /* @__PURE__ */ new Map, grantsCache.set(client, byPath));
|
|
7550
|
+
let cached = byPath.get(resourcePath);
|
|
7551
|
+
return cached === void 0 && (cached = fetchGrantsCached(requestFn, resourcePath),
|
|
7552
|
+
byPath.set(resourcePath, cached)), cached;
|
|
7553
|
+
}
|
|
7554
|
+
|
|
7555
|
+
async function fetchActor(requestFn) {
|
|
7556
|
+
let user;
|
|
7557
|
+
try {
|
|
7558
|
+
user = await requestFn({
|
|
7559
|
+
uri: "/users/me",
|
|
7560
|
+
tag: REQUEST_TAG.accessResolveActor
|
|
7561
|
+
});
|
|
7562
|
+
} catch (err) {
|
|
7563
|
+
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.', {
|
|
7564
|
+
cause: err
|
|
7369
7565
|
});
|
|
7370
7566
|
}
|
|
7567
|
+
if (!user || typeof user.id != "string" || user.id.length === 0) return;
|
|
7568
|
+
const roleNames = user.roles?.map(r => r.name).filter(n => !!n) ?? [];
|
|
7569
|
+
return {
|
|
7570
|
+
kind: "person",
|
|
7571
|
+
id: user.id,
|
|
7572
|
+
...roleNames.length > 0 ? {
|
|
7573
|
+
roles: roleNames
|
|
7574
|
+
} : {}
|
|
7575
|
+
};
|
|
7371
7576
|
}
|
|
7372
7577
|
|
|
7373
|
-
async function
|
|
7374
|
-
|
|
7375
|
-
|
|
7376
|
-
|
|
7377
|
-
|
|
7378
|
-
|
|
7379
|
-
|
|
7380
|
-
for (const row of condemned) await settleCondemnedRow({
|
|
7381
|
-
client: client,
|
|
7382
|
-
ownerId: instanceId,
|
|
7383
|
-
row: row,
|
|
7384
|
-
actor: actor,
|
|
7385
|
-
clientForGdr: clientForGdr,
|
|
7386
|
-
refSurface: refSurface,
|
|
7387
|
-
clock: clock,
|
|
7388
|
-
executionContext: executionContext,
|
|
7389
|
-
telemetry: telemetry,
|
|
7390
|
-
draining: draining
|
|
7391
|
-
});
|
|
7392
|
-
await stampDrainedRows({
|
|
7393
|
-
client: client,
|
|
7394
|
-
instance: instance,
|
|
7395
|
-
condemned: condemned,
|
|
7396
|
-
clock: clock,
|
|
7397
|
-
executionContext: executionContext
|
|
7578
|
+
async function fetchGrantsCached(requestFn, resourcePath) {
|
|
7579
|
+
try {
|
|
7580
|
+
return await fetchGrants({
|
|
7581
|
+
client: {
|
|
7582
|
+
request: requestFn
|
|
7583
|
+
},
|
|
7584
|
+
resourcePath: resourcePath
|
|
7398
7585
|
});
|
|
7586
|
+
} catch (err) {
|
|
7587
|
+
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: ${errorMessage(err)}`);
|
|
7588
|
+
return;
|
|
7399
7589
|
}
|
|
7400
7590
|
}
|
|
7401
7591
|
|
|
7402
|
-
|
|
7403
|
-
|
|
7592
|
+
const REPLAY_SURFACE = {
|
|
7593
|
+
allows: () => !0,
|
|
7594
|
+
description: "any resource (in-memory replay; the committing surface enforces)"
|
|
7595
|
+
};
|
|
7596
|
+
|
|
7597
|
+
async function whatIfFireAction(args) {
|
|
7598
|
+
const {instance: instance, definition: definition, snapshot: snapshot, now: now, actor: actor, stage: stage, activity: activity, action: action} = args;
|
|
7599
|
+
if (!replayable({
|
|
7600
|
+
instance: instance,
|
|
7601
|
+
stage: stage,
|
|
7602
|
+
action: action
|
|
7603
|
+
})) return;
|
|
7604
|
+
const ctx = {
|
|
7605
|
+
now: now,
|
|
7606
|
+
instance: instance,
|
|
7607
|
+
definition: definition,
|
|
7608
|
+
snapshot: snapshot,
|
|
7609
|
+
refSurface: REPLAY_SURFACE,
|
|
7610
|
+
actor: actor
|
|
7611
|
+
}, mutation = startMutation(instance);
|
|
7404
7612
|
try {
|
|
7405
|
-
await
|
|
7406
|
-
|
|
7407
|
-
|
|
7408
|
-
|
|
7409
|
-
|
|
7410
|
-
|
|
7411
|
-
actor: actor
|
|
7412
|
-
} : {},
|
|
7413
|
-
clientForGdr: clientForGdr,
|
|
7414
|
-
refSurface: refSurface,
|
|
7415
|
-
...clock ? {
|
|
7416
|
-
clock: clock
|
|
7417
|
-
} : {},
|
|
7418
|
-
...executionContext ? {
|
|
7419
|
-
executionContext: executionContext
|
|
7420
|
-
} : {},
|
|
7421
|
-
...telemetry ? {
|
|
7422
|
-
telemetry: telemetry
|
|
7423
|
-
} : {}
|
|
7424
|
-
}
|
|
7425
|
-
}), await drainCondemnedChildren({
|
|
7426
|
-
client: client,
|
|
7427
|
-
instanceId: childId,
|
|
7613
|
+
await applyActionFire({
|
|
7614
|
+
ctx: ctx,
|
|
7615
|
+
mutation: mutation,
|
|
7616
|
+
activity: activity,
|
|
7617
|
+
action: action,
|
|
7618
|
+
params: {},
|
|
7428
7619
|
actor: actor,
|
|
7429
|
-
|
|
7430
|
-
|
|
7431
|
-
|
|
7432
|
-
|
|
7433
|
-
|
|
7434
|
-
draining: draining
|
|
7435
|
-
});
|
|
7436
|
-
} catch (cause) {
|
|
7437
|
-
if (cause instanceof InstanceNotFoundError) return;
|
|
7438
|
-
throw cause instanceof WorkflowStateDivergedError ? cause : new WorkflowStateDivergedError({
|
|
7439
|
-
instanceId: ownerId,
|
|
7440
|
-
guardError: cause,
|
|
7441
|
-
reason: `owed child abort of "${childId}" failed while draining condemned rows`
|
|
7620
|
+
triggered: !1
|
|
7621
|
+
}), await runTriggeredActions({
|
|
7622
|
+
ctx: ctx,
|
|
7623
|
+
mutation: mutation,
|
|
7624
|
+
stage: stage
|
|
7442
7625
|
});
|
|
7626
|
+
} catch (err) {
|
|
7627
|
+
if (err instanceof FieldValueShapeError) return;
|
|
7628
|
+
throw err;
|
|
7443
7629
|
}
|
|
7630
|
+
const transition = await pickTransition(liveViewContext(ctx, mutation), stage);
|
|
7631
|
+
return transition === void 0 ? {
|
|
7632
|
+
exitsStage: !1
|
|
7633
|
+
} : {
|
|
7634
|
+
exitsStage: !0,
|
|
7635
|
+
transition: transition.name
|
|
7636
|
+
};
|
|
7444
7637
|
}
|
|
7445
7638
|
|
|
7446
|
-
|
|
7447
|
-
const
|
|
7448
|
-
|
|
7449
|
-
|
|
7450
|
-
|
|
7451
|
-
|
|
7452
|
-
|
|
7453
|
-
|
|
7454
|
-
|
|
7455
|
-
|
|
7456
|
-
|
|
7457
|
-
|
|
7458
|
-
|
|
7459
|
-
|
|
7460
|
-
|
|
7461
|
-
|
|
7639
|
+
function replayable(args) {
|
|
7640
|
+
const {instance: instance, stage: stage, action: action} = args;
|
|
7641
|
+
if (terminalState(instance) !== "in-flight" || (action.params ?? []).length > 0 || action.spawn !== void 0) return !1;
|
|
7642
|
+
const primed = new Set((findOpenStageEntry(instance)?.activities ?? []).map(e => e.name));
|
|
7643
|
+
return (stage.activities ?? []).every(declared => primed.has(declared.name)) ? !(stage.activities ?? []).flatMap(declared => declared.actions ?? []).some(sibling => isCascadeFired(sibling) && sibling.spawn !== void 0) : !1;
|
|
7644
|
+
}
|
|
7645
|
+
|
|
7646
|
+
async function buildFieldInsights({sites: sites, snapshot: snapshot}) {
|
|
7647
|
+
const insights = [];
|
|
7648
|
+
for (const field of fieldsReadAcross(sites)) {
|
|
7649
|
+
const involved = sites.filter(entry => readsField(entry.insight, field));
|
|
7650
|
+
insights.push({
|
|
7651
|
+
field: field,
|
|
7652
|
+
reads: dedupeReads(involved.flatMap(entry => fieldReads(entry.insight, field))),
|
|
7653
|
+
involvedIn: involved.map(entry => entry.site),
|
|
7654
|
+
proposals: await verifyProposals({
|
|
7655
|
+
field: field,
|
|
7656
|
+
involved: involved,
|
|
7657
|
+
snapshot: snapshot
|
|
7658
|
+
})
|
|
7462
7659
|
});
|
|
7463
|
-
}
|
|
7464
|
-
|
|
7465
|
-
subworkflows: subworkflows,
|
|
7466
|
-
history: history,
|
|
7467
|
-
lastChangedAt: now
|
|
7468
|
-
}).ifRevisionId(instance._rev).commit(SYNC_COMMIT);
|
|
7660
|
+
}
|
|
7661
|
+
return insights;
|
|
7469
7662
|
}
|
|
7470
7663
|
|
|
7471
|
-
function
|
|
7472
|
-
const
|
|
7473
|
-
|
|
7474
|
-
at: child.completedAt,
|
|
7475
|
-
stage: child.currentStage,
|
|
7476
|
-
...status === "aborted" ? {
|
|
7477
|
-
aborted: !0
|
|
7478
|
-
} : {}
|
|
7479
|
-
};
|
|
7664
|
+
function fieldsReadAcross(sites) {
|
|
7665
|
+
const names = sites.flatMap(entry => entry.insight.analysis.reads).filter(read => read.variable === "fields").map(read => read.path[0]).filter(head => typeof head == "string");
|
|
7666
|
+
return [ ...new Set(names) ];
|
|
7480
7667
|
}
|
|
7481
7668
|
|
|
7482
|
-
function
|
|
7483
|
-
return
|
|
7484
|
-
_key: randomKey(),
|
|
7485
|
-
_type: "subworkflowResolved",
|
|
7486
|
-
at: at,
|
|
7487
|
-
activity: row.activity,
|
|
7488
|
-
instanceRef: row.ref,
|
|
7489
|
-
status: status
|
|
7490
|
-
};
|
|
7669
|
+
function fieldReads(insight, field) {
|
|
7670
|
+
return insight.analysis.reads.filter(read => read.variable === "fields" && read.path[0] === field);
|
|
7491
7671
|
}
|
|
7492
7672
|
|
|
7493
|
-
|
|
7494
|
-
|
|
7495
|
-
if (loaded === void 0) return;
|
|
7496
|
-
const {child: child, parent: parent} = loaded, definition = parseDefinitionSnapshot(parent), ctx = await buildEngineContext({
|
|
7497
|
-
client: client,
|
|
7498
|
-
clientForGdr: clientForGdr,
|
|
7499
|
-
refSurface: refSurface,
|
|
7500
|
-
instance: parent,
|
|
7501
|
-
definition: definition,
|
|
7502
|
-
...clock ? {
|
|
7503
|
-
clock: clock
|
|
7504
|
-
} : {},
|
|
7505
|
-
...actor ? {
|
|
7506
|
-
actor: actor
|
|
7507
|
-
} : {},
|
|
7508
|
-
...executionContext ? {
|
|
7509
|
-
executionContext: executionContext
|
|
7510
|
-
} : {},
|
|
7511
|
-
...telemetry ? {
|
|
7512
|
-
telemetry: telemetry
|
|
7513
|
-
} : {}
|
|
7514
|
-
}), mutation = startMutation(parent), row = mutation.subworkflows.find(r => toBareId(r.ref.id) === child._id);
|
|
7515
|
-
if (row === void 0) {
|
|
7516
|
-
await recordOrphanedPropagation({
|
|
7517
|
-
ctx: ctx,
|
|
7518
|
-
mutation: mutation,
|
|
7519
|
-
child: child
|
|
7520
|
-
});
|
|
7521
|
-
return;
|
|
7522
|
-
}
|
|
7523
|
-
const changed = stampResolvedChild({
|
|
7524
|
-
mutation: mutation,
|
|
7525
|
-
row: row,
|
|
7526
|
-
child: child,
|
|
7527
|
-
now: ctx.now
|
|
7528
|
-
}), parentTerminal = parent.completedAt !== void 0;
|
|
7529
|
-
changed && await persist(ctx, mutation), !parentTerminal && (await cascadeAutoTransitions({
|
|
7530
|
-
client: client,
|
|
7531
|
-
instanceId: parent._id,
|
|
7532
|
-
actor: actor,
|
|
7533
|
-
clientForGdr: clientForGdr,
|
|
7534
|
-
refSurface: refSurface,
|
|
7535
|
-
clock: clock,
|
|
7536
|
-
executionContext: executionContext,
|
|
7537
|
-
telemetry: telemetry
|
|
7538
|
-
}), await propagateToAncestors({
|
|
7539
|
-
client: client,
|
|
7540
|
-
instanceId: parent._id,
|
|
7541
|
-
actor: actor,
|
|
7542
|
-
clientForGdr: clientForGdr,
|
|
7543
|
-
refSurface: refSurface,
|
|
7544
|
-
clock: clock,
|
|
7545
|
-
executionContext: executionContext,
|
|
7546
|
-
telemetry: telemetry
|
|
7547
|
-
}));
|
|
7673
|
+
function readsField(insight, field) {
|
|
7674
|
+
return fieldReads(insight, field).length > 0;
|
|
7548
7675
|
}
|
|
7549
7676
|
|
|
7550
|
-
async function
|
|
7551
|
-
const
|
|
7552
|
-
|
|
7553
|
-
|
|
7554
|
-
|
|
7555
|
-
|
|
7556
|
-
|
|
7557
|
-
|
|
7558
|
-
|
|
7559
|
-
|
|
7677
|
+
async function verifyProposals({field: field, involved: involved, snapshot: snapshot}) {
|
|
7678
|
+
const proposals = [];
|
|
7679
|
+
for (const assign of candidateAssignments(involved, field)) {
|
|
7680
|
+
const consequences = [];
|
|
7681
|
+
for (const entry of involved) {
|
|
7682
|
+
const result = await whatIfCondition({
|
|
7683
|
+
condition: entry.condition,
|
|
7684
|
+
dataset: snapshot.docs,
|
|
7685
|
+
params: entry.params,
|
|
7686
|
+
assign: assign
|
|
7687
|
+
});
|
|
7688
|
+
result.changed && consequences.push({
|
|
7689
|
+
site: entry.site,
|
|
7690
|
+
before: result.before,
|
|
7691
|
+
after: result.after
|
|
7692
|
+
});
|
|
7693
|
+
}
|
|
7694
|
+
consequences.length > 0 && proposals.push({
|
|
7695
|
+
assign: assign,
|
|
7696
|
+
consequences: consequences
|
|
7697
|
+
});
|
|
7698
|
+
}
|
|
7699
|
+
return proposals;
|
|
7560
7700
|
}
|
|
7561
7701
|
|
|
7562
|
-
function
|
|
7563
|
-
|
|
7564
|
-
|
|
7565
|
-
return resolved === void 0 ? !1 : (row.resolved = resolved, mutation.history.push(subworkflowResolvedEntry({
|
|
7566
|
-
row: row,
|
|
7567
|
-
at: now,
|
|
7568
|
-
status: resolved.aborted === !0 ? "aborted" : "done"
|
|
7569
|
-
})), !0);
|
|
7702
|
+
function candidateAssignments(involved, field) {
|
|
7703
|
+
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);
|
|
7704
|
+
return dedupeBy(candidates, assignment => JSON.stringify([ assignment.target.path, assignment.value ]));
|
|
7570
7705
|
}
|
|
7571
7706
|
|
|
7572
|
-
|
|
7573
|
-
|
|
7574
|
-
|
|
7575
|
-
|
|
7576
|
-
|
|
7577
|
-
|
|
7578
|
-
|
|
7579
|
-
id: childUri,
|
|
7580
|
-
type: WORKFLOW_INSTANCE_TYPE
|
|
7581
|
-
},
|
|
7582
|
-
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.`
|
|
7583
|
-
}), await persist(ctx, mutation));
|
|
7584
|
-
}
|
|
7707
|
+
function assignmentFor(requirement) {
|
|
7708
|
+
switch (requirement.kind) {
|
|
7709
|
+
case "equals":
|
|
7710
|
+
return [ {
|
|
7711
|
+
target: requirement.target,
|
|
7712
|
+
value: requirement.value
|
|
7713
|
+
} ];
|
|
7585
7714
|
|
|
7586
|
-
|
|
7587
|
-
|
|
7588
|
-
|
|
7589
|
-
|
|
7590
|
-
|
|
7591
|
-
...s
|
|
7592
|
-
})),
|
|
7593
|
-
stages: instance.stages.map(s => ({
|
|
7594
|
-
...s,
|
|
7595
|
-
fields: (s.fields ?? []).map(entry => ({
|
|
7596
|
-
...entry
|
|
7597
|
-
})),
|
|
7598
|
-
activities: s.activities.map(t => ({
|
|
7599
|
-
...t,
|
|
7600
|
-
...t.fields !== void 0 ? {
|
|
7601
|
-
fields: t.fields.map(entry => ({
|
|
7602
|
-
...entry
|
|
7603
|
-
}))
|
|
7604
|
-
} : {}
|
|
7605
|
-
}))
|
|
7606
|
-
})),
|
|
7607
|
-
subworkflows: (instance.subworkflows ?? []).map(row => ({
|
|
7608
|
-
...row,
|
|
7609
|
-
...row.abortPending !== void 0 ? {
|
|
7610
|
-
abortPending: {
|
|
7611
|
-
...row.abortPending
|
|
7612
|
-
}
|
|
7613
|
-
} : {},
|
|
7614
|
-
...row.resolved !== void 0 ? {
|
|
7615
|
-
resolved: {
|
|
7616
|
-
...row.resolved
|
|
7617
|
-
}
|
|
7618
|
-
} : {}
|
|
7619
|
-
})),
|
|
7620
|
-
pendingEffects: [ ...instance.pendingEffects ],
|
|
7621
|
-
effectHistory: [ ...instance.effectHistory ],
|
|
7622
|
-
context: [ ...instance.context ],
|
|
7623
|
-
history: [ ...instance.history ],
|
|
7624
|
-
processedRequests: [ ...instance.processedRequests ?? [] ],
|
|
7625
|
-
lastChangedAt: instance.lastChangedAt,
|
|
7626
|
-
...instance.completedAt !== void 0 ? {
|
|
7627
|
-
completedAt: instance.completedAt
|
|
7628
|
-
} : {},
|
|
7629
|
-
...instance.abortedAt !== void 0 ? {
|
|
7630
|
-
abortedAt: instance.abortedAt
|
|
7631
|
-
} : {},
|
|
7632
|
-
pendingCreates: []
|
|
7633
|
-
};
|
|
7634
|
-
}
|
|
7715
|
+
case "truthy":
|
|
7716
|
+
return [ {
|
|
7717
|
+
target: requirement.target,
|
|
7718
|
+
value: !0
|
|
7719
|
+
} ];
|
|
7635
7720
|
|
|
7636
|
-
|
|
7637
|
-
|
|
7638
|
-
|
|
7639
|
-
|
|
7640
|
-
|
|
7641
|
-
subworkflows: src.subworkflows ?? [],
|
|
7642
|
-
pendingEffects: src.pendingEffects,
|
|
7643
|
-
effectHistory: src.effectHistory,
|
|
7644
|
-
context: src.context,
|
|
7645
|
-
history: src.history,
|
|
7646
|
-
processedRequests: src.processedRequests ?? [],
|
|
7647
|
-
...src.completedAt !== void 0 ? {
|
|
7648
|
-
completedAt: src.completedAt
|
|
7649
|
-
} : {},
|
|
7650
|
-
...src.abortedAt !== void 0 ? {
|
|
7651
|
-
abortedAt: src.abortedAt
|
|
7652
|
-
} : {}
|
|
7653
|
-
};
|
|
7654
|
-
return {
|
|
7655
|
-
...modelStampFor({
|
|
7656
|
-
documentType: "instance",
|
|
7657
|
-
document: state,
|
|
7658
|
-
storedMinReaderModel: src.minReaderModel
|
|
7659
|
-
}),
|
|
7660
|
-
...state
|
|
7661
|
-
};
|
|
7662
|
-
}
|
|
7721
|
+
case "falsy":
|
|
7722
|
+
return [ {
|
|
7723
|
+
target: requirement.target,
|
|
7724
|
+
value: !1
|
|
7725
|
+
} ];
|
|
7663
7726
|
|
|
7664
|
-
|
|
7665
|
-
|
|
7666
|
-
|
|
7667
|
-
|
|
7668
|
-
|
|
7669
|
-
|
|
7670
|
-
};
|
|
7727
|
+
case "differs":
|
|
7728
|
+
case "defined":
|
|
7729
|
+
case "undefined":
|
|
7730
|
+
case "compares":
|
|
7731
|
+
return [];
|
|
7732
|
+
}
|
|
7671
7733
|
}
|
|
7672
7734
|
|
|
7673
|
-
function
|
|
7674
|
-
return
|
|
7675
|
-
...base,
|
|
7676
|
-
currentStage: mutation.currentStage,
|
|
7677
|
-
fields: mutation.fields,
|
|
7678
|
-
stages: mutation.stages,
|
|
7679
|
-
subworkflows: mutation.subworkflows,
|
|
7680
|
-
context: mutation.context,
|
|
7681
|
-
pendingEffects: mutation.pendingEffects,
|
|
7682
|
-
effectHistory: mutation.effectHistory
|
|
7683
|
-
};
|
|
7735
|
+
function fabricatable(assignment) {
|
|
7736
|
+
return assignment.target.path.every(segment => typeof segment != "number" || segment <= MAX_COUNTERFACTUAL_INDEX);
|
|
7684
7737
|
}
|
|
7685
7738
|
|
|
7686
|
-
async function
|
|
7687
|
-
|
|
7688
|
-
const
|
|
7689
|
-
|
|
7690
|
-
|
|
7691
|
-
|
|
7692
|
-
|
|
7693
|
-
const tx = ctx.client.transaction();
|
|
7694
|
-
for (const {body: body} of pendingCreates) tx.create(body);
|
|
7695
|
-
tx.patch(ctx.client.patch(ctx.instance._id).set(set).ifRevisionId(ctx.instance._rev)),
|
|
7696
|
-
await tx.commit(), mutation.pendingCreates = [];
|
|
7697
|
-
const actorForPriming = ctx.actor;
|
|
7698
|
-
for (const {body: body, started: started} of pendingCreates) try {
|
|
7699
|
-
await primeInitialStage({
|
|
7700
|
-
client: ctx.client,
|
|
7701
|
-
instanceId: body._id,
|
|
7702
|
-
actor: actorForPriming,
|
|
7703
|
-
clientForGdr: ctx.clientForGdr,
|
|
7704
|
-
refSurface: ctx.refSurface,
|
|
7705
|
-
clock: ctx.clock,
|
|
7706
|
-
executionContext: ctx.executionContext,
|
|
7707
|
-
telemetry: ctx.telemetry
|
|
7708
|
-
}), await cascadeAutoTransitions({
|
|
7709
|
-
client: ctx.client,
|
|
7710
|
-
instanceId: body._id,
|
|
7711
|
-
actor: actorForPriming,
|
|
7712
|
-
clientForGdr: ctx.clientForGdr,
|
|
7713
|
-
refSurface: ctx.refSurface,
|
|
7714
|
-
clock: ctx.clock,
|
|
7715
|
-
executionContext: ctx.executionContext,
|
|
7716
|
-
telemetry: ctx.telemetry
|
|
7717
|
-
}), await propagateToAncestors({
|
|
7718
|
-
client: ctx.client,
|
|
7719
|
-
instanceId: body._id,
|
|
7720
|
-
actor: actorForPriming,
|
|
7721
|
-
clientForGdr: ctx.clientForGdr,
|
|
7722
|
-
refSurface: ctx.refSurface,
|
|
7723
|
-
clock: ctx.clock,
|
|
7724
|
-
executionContext: ctx.executionContext,
|
|
7725
|
-
telemetry: ctx.telemetry
|
|
7726
|
-
}), ctx.telemetry.log(WorkflowInstanceStarted, started);
|
|
7727
|
-
} catch (cause) {
|
|
7728
|
-
throw cause instanceof WorkflowStateDivergedError ? cause : new WorkflowStateDivergedError({
|
|
7729
|
-
instanceId: ctx.instance._id,
|
|
7730
|
-
guardError: cause,
|
|
7731
|
-
reason: `spawned child "${body._id}" failed to settle after the spawn transaction committed`
|
|
7739
|
+
async function subjectResourceGrants(args) {
|
|
7740
|
+
const {clientForGdr: clientForGdr, instance: instance} = args, clients = /* @__PURE__ */ new Map;
|
|
7741
|
+
for (const {parsed: parsed, resource: resource} of foreignSubjectRefs(instance)) {
|
|
7742
|
+
const key = resourceGdr(resource);
|
|
7743
|
+
clients.has(key) || clients.set(key, {
|
|
7744
|
+
resource: resource,
|
|
7745
|
+
client: clientForGdr(parsed)
|
|
7732
7746
|
});
|
|
7733
7747
|
}
|
|
7734
|
-
const
|
|
7735
|
-
|
|
7736
|
-
|
|
7748
|
+
const resolved = await Promise.all([ ...clients.entries() ].map(async ([key, entry]) => {
|
|
7749
|
+
const path = aclPathForResource(entry.resource);
|
|
7750
|
+
if (path === void 0) return;
|
|
7751
|
+
const grants = await grantsForClientPath(entry.client, path);
|
|
7752
|
+
return grants === void 0 ? void 0 : [ key, grants ];
|
|
7753
|
+
}));
|
|
7754
|
+
return new Map(resolved.filter(entry => entry !== void 0));
|
|
7737
7755
|
}
|
|
7738
7756
|
|
|
7739
|
-
function
|
|
7740
|
-
const
|
|
7741
|
-
|
|
7742
|
-
|
|
7757
|
+
async function evaluateInstance(args) {
|
|
7758
|
+
const {client: client, tag: tag, workflowResource: workflowResource, instanceId: instanceId, resourceClients: resourceClients} = args, now = (args.clock ?? wallClock)();
|
|
7759
|
+
validateTag(tag);
|
|
7760
|
+
const {actor: actor, grants: grants} = await resolveAccess(client, {
|
|
7761
|
+
...args.grantsFromPath !== void 0 ? {
|
|
7762
|
+
grantsFromPath: args.grantsFromPath
|
|
7763
|
+
} : {}
|
|
7764
|
+
}), instance = await reload({
|
|
7765
|
+
client: client,
|
|
7766
|
+
instanceId: instanceId,
|
|
7767
|
+
tag: tag
|
|
7768
|
+
}), definition = parseDefinitionSnapshot(instance), clientForGdr = buildClientForGdr({
|
|
7769
|
+
client: client,
|
|
7770
|
+
workflowResource: workflowResource,
|
|
7771
|
+
resourceClients: resourceClients
|
|
7772
|
+
}), snapshot = await hydrateSnapshot({
|
|
7773
|
+
client: client,
|
|
7774
|
+
clientForGdr: clientForGdr,
|
|
7775
|
+
instance: instance
|
|
7776
|
+
}), guards = await verdictGuardsForInstance(client, instance._id), resourceGrants = await subjectResourceGrants({
|
|
7777
|
+
clientForGdr: clientForGdr,
|
|
7778
|
+
instance: instance
|
|
7779
|
+
});
|
|
7780
|
+
return evaluateFromSnapshot({
|
|
7781
|
+
instance: instance,
|
|
7782
|
+
definition: definition,
|
|
7783
|
+
actor: actor,
|
|
7784
|
+
snapshot: snapshot,
|
|
7785
|
+
guards: guards,
|
|
7786
|
+
now: now,
|
|
7787
|
+
resourceGrants: resourceGrants,
|
|
7788
|
+
...grants !== void 0 ? {
|
|
7789
|
+
grants: grants
|
|
7790
|
+
} : {}
|
|
7791
|
+
});
|
|
7792
|
+
}
|
|
7793
|
+
|
|
7794
|
+
function memoizedByName(render) {
|
|
7795
|
+
const rendered = /* @__PURE__ */ new Map;
|
|
7796
|
+
return activityName => {
|
|
7797
|
+
const hit = rendered.get(activityName);
|
|
7798
|
+
if (hit !== void 0) return hit;
|
|
7799
|
+
const scope = render(activityName);
|
|
7800
|
+
return rendered.set(activityName, scope), scope;
|
|
7801
|
+
};
|
|
7743
7802
|
}
|
|
7744
7803
|
|
|
7745
|
-
function
|
|
7746
|
-
|
|
7747
|
-
|
|
7748
|
-
|
|
7804
|
+
function currentStageOf(instance, definition) {
|
|
7805
|
+
try {
|
|
7806
|
+
return findStage(definition, instance.currentStage);
|
|
7807
|
+
} catch (err) {
|
|
7808
|
+
throw new Error(`Instance "${instance._id}" currentStage "${instance.currentStage}" not in definition`, {
|
|
7809
|
+
cause: err
|
|
7810
|
+
});
|
|
7811
|
+
}
|
|
7749
7812
|
}
|
|
7750
7813
|
|
|
7751
|
-
function
|
|
7752
|
-
|
|
7814
|
+
async function explainSite(args) {
|
|
7815
|
+
const {site: site, condition: condition, params: params, snapshot: snapshot, sites: sites} = args, insight = await explainCondition({
|
|
7816
|
+
condition: condition,
|
|
7817
|
+
dataset: snapshot.docs,
|
|
7818
|
+
params: params
|
|
7819
|
+
});
|
|
7820
|
+
return sites.push({
|
|
7821
|
+
site: site,
|
|
7822
|
+
condition: condition,
|
|
7823
|
+
params: params,
|
|
7824
|
+
insight: insight
|
|
7825
|
+
}), insight;
|
|
7753
7826
|
}
|
|
7754
7827
|
|
|
7755
|
-
function
|
|
7756
|
-
const stage =
|
|
7757
|
-
|
|
7758
|
-
|
|
7828
|
+
async function evaluateFromSnapshot(args) {
|
|
7829
|
+
const {instance: instance, definition: definition, actor: actor, grants: grants, snapshot: snapshot} = args, now = args.now ?? wallClock(), stage = currentStageOf(instance, definition), autonomy = autonomyOf(definition), stageAutonomy2 = stageAutonomyOf(autonomy, stage.name), fireConsequence = (activity, action) => whatIfFireAction({
|
|
7830
|
+
instance: instance,
|
|
7831
|
+
definition: definition,
|
|
7832
|
+
snapshot: snapshot,
|
|
7833
|
+
now: now,
|
|
7834
|
+
actor: actor,
|
|
7759
7835
|
stage: stage,
|
|
7760
|
-
activity: activity
|
|
7836
|
+
activity: activity,
|
|
7837
|
+
action: action
|
|
7838
|
+
}), scopeSource = {
|
|
7839
|
+
instance: instance,
|
|
7840
|
+
definition: definition,
|
|
7841
|
+
snapshot: snapshot,
|
|
7842
|
+
now: now
|
|
7843
|
+
}, can = await advisoryCan({
|
|
7844
|
+
instance: instance,
|
|
7845
|
+
actor: actor,
|
|
7846
|
+
grants: grants
|
|
7847
|
+
}), scope = await renderConditionScope(scopeSource, {
|
|
7848
|
+
actor: actor,
|
|
7849
|
+
vars: {
|
|
7850
|
+
can: can
|
|
7851
|
+
}
|
|
7852
|
+
}), scopeForActivity = memoizedByName(activityName => renderConditionScope(scopeSource, {
|
|
7853
|
+
activityName: activityName,
|
|
7854
|
+
actor: actor,
|
|
7855
|
+
vars: {
|
|
7856
|
+
can: can
|
|
7857
|
+
}
|
|
7858
|
+
})), cascadeScopeForActivity = memoizedByName(activityName => renderConditionScope(scopeSource, {
|
|
7859
|
+
activityName: activityName
|
|
7860
|
+
})), currentActivityEntries = findOpenStageEntry(instance)?.activities ?? [], guardDenial = await instanceGuardReason({
|
|
7861
|
+
instance: instance,
|
|
7862
|
+
actor: actor,
|
|
7863
|
+
guards: args.guards
|
|
7864
|
+
}), subjectDenials = await forecastSubjectDenials({
|
|
7865
|
+
instance: instance,
|
|
7866
|
+
actor: actor,
|
|
7867
|
+
snapshot: snapshot,
|
|
7868
|
+
resourceGrants: args.resourceGrants
|
|
7869
|
+
}), subjectPermissionReason = subjectDenials.length > 0 ? {
|
|
7870
|
+
kind: "subject-permission-denied",
|
|
7871
|
+
denied: subjectDenials
|
|
7872
|
+
} : void 0, sites = [], activityEvaluations = [];
|
|
7873
|
+
for (const activity of stage.activities ?? []) activityEvaluations.push(await evaluateActivity({
|
|
7874
|
+
activity: activity,
|
|
7875
|
+
statusEntry: currentActivityEntries.find(t => t.name === activity.name),
|
|
7876
|
+
instance: instance,
|
|
7877
|
+
snapshot: snapshot,
|
|
7878
|
+
activityScope: await scopeForActivity(activity.name),
|
|
7879
|
+
cascadeActivityScope: () => cascadeScopeForActivity(activity.name),
|
|
7880
|
+
stageHasExits: !isTerminalStage(stage),
|
|
7881
|
+
guardDenial: guardDenial,
|
|
7882
|
+
subjectPermissionReason: subjectPermissionReason,
|
|
7883
|
+
sites: sites,
|
|
7884
|
+
autonomy: activityAutonomyOf(stageAutonomy2, activity.name),
|
|
7885
|
+
fireConsequence: fireConsequence
|
|
7886
|
+
}));
|
|
7887
|
+
const cascadeParams = await renderConditionScope(scopeSource), transitionEvaluations = [];
|
|
7888
|
+
for (const transition of stage.transitions ?? []) {
|
|
7889
|
+
const insight = await explainSite({
|
|
7890
|
+
site: {
|
|
7891
|
+
kind: "transition",
|
|
7892
|
+
transition: transition.name
|
|
7893
|
+
},
|
|
7894
|
+
condition: transition.when,
|
|
7895
|
+
params: cascadeParams,
|
|
7896
|
+
snapshot: snapshot,
|
|
7897
|
+
sites: sites
|
|
7898
|
+
});
|
|
7899
|
+
transitionEvaluations.push({
|
|
7900
|
+
transition: transition,
|
|
7901
|
+
whenSatisfied: insight.outcome === "satisfied",
|
|
7902
|
+
unevaluable: insight.outcome === "unevaluable",
|
|
7903
|
+
insight: insight
|
|
7904
|
+
});
|
|
7905
|
+
}
|
|
7906
|
+
const currentStage = {
|
|
7907
|
+
stage: stage,
|
|
7908
|
+
activities: activityEvaluations,
|
|
7909
|
+
transitions: transitionEvaluations,
|
|
7910
|
+
autonomy: stageAutonomy2
|
|
7911
|
+
}, 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({
|
|
7912
|
+
instance: instance,
|
|
7913
|
+
definition: definition,
|
|
7914
|
+
stage: stage,
|
|
7915
|
+
snapshot: snapshot,
|
|
7916
|
+
scope: scope,
|
|
7917
|
+
scopeForActivity: scopeForActivity,
|
|
7918
|
+
guardDenial: editGuardDenial,
|
|
7919
|
+
sites: sites
|
|
7920
|
+
});
|
|
7921
|
+
return {
|
|
7922
|
+
instance: instance,
|
|
7923
|
+
definition: definition,
|
|
7924
|
+
actor: actor,
|
|
7925
|
+
currentStage: currentStage,
|
|
7926
|
+
pendingOnYou: pendingOnYou,
|
|
7927
|
+
canInteract: canInteract,
|
|
7928
|
+
editableFields: editableFields,
|
|
7929
|
+
fieldInsights: await buildFieldInsights({
|
|
7930
|
+
sites: sites,
|
|
7931
|
+
snapshot: snapshot
|
|
7932
|
+
}),
|
|
7933
|
+
autonomy: autonomy
|
|
7761
7934
|
};
|
|
7762
7935
|
}
|
|
7763
7936
|
|
|
7764
|
-
|
|
7765
|
-
const mutEntry = currentActivities(mutation).find(t => t.name === activity);
|
|
7766
|
-
if (mutEntry === void 0) throw new Error(`Activity "${activity}" disappeared from mutation copy — invariant broken`);
|
|
7767
|
-
return mutEntry;
|
|
7768
|
-
}
|
|
7937
|
+
const AUTONOMY_CACHE = /* @__PURE__ */ new WeakMap;
|
|
7769
7938
|
|
|
7770
|
-
function
|
|
7771
|
-
|
|
7939
|
+
function autonomyOf(definition) {
|
|
7940
|
+
const hit = AUTONOMY_CACHE.get(definition);
|
|
7941
|
+
if (hit !== void 0) return hit;
|
|
7942
|
+
const derived = deriveWorkflowAutonomy(definition);
|
|
7943
|
+
return AUTONOMY_CACHE.set(definition, derived), derived;
|
|
7772
7944
|
}
|
|
7773
7945
|
|
|
7774
|
-
function
|
|
7775
|
-
|
|
7946
|
+
async function evaluateEditableFields(args) {
|
|
7947
|
+
const {instance: instance, definition: definition, stage: stage, snapshot: snapshot, scope: scope, scopeForActivity: scopeForActivity, guardDenial: guardDenial, sites: sites} = args, fields = [];
|
|
7948
|
+
for (const site of editableFieldsInStage(definition, stage)) {
|
|
7949
|
+
const window = fieldWindowOpen(instance, site), insight = await editPredicateInsight({
|
|
7950
|
+
site: site,
|
|
7951
|
+
snapshot: snapshot,
|
|
7952
|
+
scope: scope,
|
|
7953
|
+
scopeForActivity: scopeForActivity,
|
|
7954
|
+
sites: sites
|
|
7955
|
+
}), predicateSatisfied = insight === void 0 || insight.outcome === "satisfied", reason = editDisabledReason({
|
|
7956
|
+
effective: site.effective,
|
|
7957
|
+
instance: instance,
|
|
7958
|
+
window: window,
|
|
7959
|
+
guardDenial: guardDenial,
|
|
7960
|
+
predicateSatisfied: predicateSatisfied
|
|
7961
|
+
}), value = readFieldValue(instance, site);
|
|
7962
|
+
fields.push({
|
|
7963
|
+
scope: site.scope,
|
|
7964
|
+
...site.activity !== void 0 ? {
|
|
7965
|
+
activity: site.activity
|
|
7966
|
+
} : {},
|
|
7967
|
+
name: site.name,
|
|
7968
|
+
type: site.type,
|
|
7969
|
+
...site.title !== void 0 ? {
|
|
7970
|
+
title: site.title
|
|
7971
|
+
} : {},
|
|
7972
|
+
...site.validation !== void 0 ? {
|
|
7973
|
+
validation: site.validation
|
|
7974
|
+
} : {},
|
|
7975
|
+
value: value,
|
|
7976
|
+
editable: reason === void 0,
|
|
7977
|
+
...reason !== void 0 ? {
|
|
7978
|
+
disabledReason: reason
|
|
7979
|
+
} : {},
|
|
7980
|
+
...fieldProvenance(instance, site.ref),
|
|
7981
|
+
...insight !== void 0 ? {
|
|
7982
|
+
insight: insight
|
|
7983
|
+
} : {}
|
|
7984
|
+
});
|
|
7985
|
+
}
|
|
7986
|
+
return fields;
|
|
7776
7987
|
}
|
|
7777
7988
|
|
|
7778
|
-
async function
|
|
7779
|
-
const {
|
|
7780
|
-
if (
|
|
7781
|
-
|
|
7782
|
-
|
|
7783
|
-
|
|
7784
|
-
|
|
7785
|
-
|
|
7786
|
-
|
|
7787
|
-
|
|
7788
|
-
|
|
7789
|
-
|
|
7790
|
-
|
|
7791
|
-
|
|
7792
|
-
|
|
7793
|
-
|
|
7794
|
-
|
|
7795
|
-
actor: options.actor
|
|
7796
|
-
}),
|
|
7797
|
-
onExhausted: () => new ConcurrentCompleteEffectError({
|
|
7798
|
-
instanceId: instanceId,
|
|
7799
|
-
effectKey: effectKey,
|
|
7800
|
-
attempts: CONCURRENT_COMMIT_MAX_ATTEMPTS
|
|
7801
|
-
})
|
|
7989
|
+
async function editPredicateInsight(args) {
|
|
7990
|
+
const {site: site, snapshot: snapshot, scope: scope, scopeForActivity: scopeForActivity, sites: sites} = args;
|
|
7991
|
+
if (typeof site.effective != "string") return;
|
|
7992
|
+
const params = site.scope === "activity" && site.activity !== void 0 ? await scopeForActivity(site.activity) : scope;
|
|
7993
|
+
return explainSite({
|
|
7994
|
+
site: {
|
|
7995
|
+
kind: "editable-field",
|
|
7996
|
+
scope: site.scope,
|
|
7997
|
+
name: site.name,
|
|
7998
|
+
...site.activity !== void 0 ? {
|
|
7999
|
+
activity: site.activity
|
|
8000
|
+
} : {}
|
|
8001
|
+
},
|
|
8002
|
+
condition: site.effective,
|
|
8003
|
+
params: params,
|
|
8004
|
+
snapshot: snapshot,
|
|
8005
|
+
sites: sites
|
|
7802
8006
|
});
|
|
7803
8007
|
}
|
|
7804
8008
|
|
|
7805
|
-
function
|
|
7806
|
-
const {
|
|
8009
|
+
async function forecastSubjectDenials(args) {
|
|
8010
|
+
const {instance: instance, actor: actor, snapshot: snapshot, resourceGrants: resourceGrants} = args;
|
|
8011
|
+
if (resourceGrants === void 0 || resourceGrants.size === 0) return [];
|
|
8012
|
+
const denials = [], seen = /* @__PURE__ */ new Set;
|
|
8013
|
+
for (const {ref: ref, parsed: parsed, resource: resource} of foreignSubjectRefs(instance)) {
|
|
8014
|
+
if (seen.has(ref.id)) continue;
|
|
8015
|
+
seen.add(ref.id);
|
|
8016
|
+
const grants = resourceGrants.get(resourceGdr(resource));
|
|
8017
|
+
if (grants === void 0) continue;
|
|
8018
|
+
const doc = snapshot.docs.find(d => d._id === ref.id);
|
|
8019
|
+
doc !== void 0 && await subjectUpdateAllowed({
|
|
8020
|
+
doc: doc,
|
|
8021
|
+
parsed: parsed,
|
|
8022
|
+
grants: grants,
|
|
8023
|
+
actorId: actor.id
|
|
8024
|
+
}) === !1 && denials.push({
|
|
8025
|
+
subject: ref.id,
|
|
8026
|
+
resource: resourceGdr(resource),
|
|
8027
|
+
permission: "update"
|
|
8028
|
+
});
|
|
8029
|
+
}
|
|
8030
|
+
return denials;
|
|
8031
|
+
}
|
|
8032
|
+
|
|
8033
|
+
async function subjectUpdateAllowed(args) {
|
|
8034
|
+
const {doc: doc, parsed: parsed, grants: grants, actorId: actorId} = args;
|
|
8035
|
+
try {
|
|
8036
|
+
return await grantsPermissionOn({
|
|
8037
|
+
document: {
|
|
8038
|
+
...doc,
|
|
8039
|
+
_id: parsed.documentId
|
|
8040
|
+
},
|
|
8041
|
+
grants: grants,
|
|
8042
|
+
permission: "update",
|
|
8043
|
+
userId: actorId
|
|
8044
|
+
});
|
|
8045
|
+
} catch (err) {
|
|
8046
|
+
console.warn(`workflow: subject-write forecast skipped for "${doc._id}" — evaluating that resource's grants failed (the lake still enforces the write). Original error: ${errorMessage(err)}`);
|
|
8047
|
+
return;
|
|
8048
|
+
}
|
|
8049
|
+
}
|
|
8050
|
+
|
|
8051
|
+
async function evaluateActivity(args) {
|
|
8052
|
+
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: unmetRequirements, ...conditionInsights} = await explainActivityConditions({
|
|
8053
|
+
activity: activity,
|
|
8054
|
+
activityScope: activityScope,
|
|
8055
|
+
cascadeActivityScope: cascadeActivityScope,
|
|
8056
|
+
snapshot: snapshot,
|
|
8057
|
+
sites: sites
|
|
8058
|
+
}), requirementsReason = unmetRequirements.length > 0 ? {
|
|
8059
|
+
kind: "requirements-unmet",
|
|
8060
|
+
unmetRequirements: unmetRequirements
|
|
8061
|
+
} : void 0, actions = [];
|
|
8062
|
+
for (const action of activity.actions ?? []) actions.push(await evaluateAction({
|
|
8063
|
+
action: action,
|
|
8064
|
+
activityName: activity.name,
|
|
8065
|
+
status: status,
|
|
8066
|
+
instance: instance,
|
|
8067
|
+
snapshot: snapshot,
|
|
8068
|
+
activityScope: activityScope,
|
|
8069
|
+
cascadeActivityScope: cascadeActivityScope,
|
|
8070
|
+
stageHasExits: stageHasExits,
|
|
8071
|
+
guardDenial: guardDenial,
|
|
8072
|
+
subjectPermissionReason: subjectPermissionReason,
|
|
8073
|
+
requirementsReason: requirementsReason,
|
|
8074
|
+
sites: sites,
|
|
8075
|
+
fireConsequence: a => fireConsequence(activity, a)
|
|
8076
|
+
}));
|
|
7807
8077
|
return {
|
|
7808
|
-
|
|
7809
|
-
name: pending.name,
|
|
7810
|
-
...pending.title !== void 0 ? {
|
|
7811
|
-
title: pending.title
|
|
7812
|
-
} : {},
|
|
7813
|
-
...pending.description !== void 0 ? {
|
|
7814
|
-
description: pending.description
|
|
7815
|
-
} : {},
|
|
7816
|
-
params: pending.params,
|
|
7817
|
-
origin: pending.origin,
|
|
7818
|
-
...resolvedActor !== void 0 ? {
|
|
7819
|
-
actor: resolvedActor
|
|
7820
|
-
} : {},
|
|
7821
|
-
...pending.stageEntryKey !== void 0 ? {
|
|
7822
|
-
stageEntryKey: pending.stageEntryKey
|
|
7823
|
-
} : {},
|
|
7824
|
-
ranAt: ranAt,
|
|
7825
|
-
...durationMs !== void 0 ? {
|
|
7826
|
-
durationMs: durationMs
|
|
7827
|
-
} : {},
|
|
8078
|
+
activity: activity,
|
|
7828
8079
|
status: status,
|
|
7829
|
-
|
|
7830
|
-
|
|
7831
|
-
|
|
7832
|
-
|
|
7833
|
-
|
|
8080
|
+
kind: deriveActivityKind(activity),
|
|
8081
|
+
classification: deriveExecutorClassification(activity),
|
|
8082
|
+
autonomy: autonomy,
|
|
8083
|
+
pendingOnActor: status === "active" && assigned,
|
|
8084
|
+
scopedOut: isFilterScopedOut({
|
|
8085
|
+
status: status,
|
|
8086
|
+
startedAt: statusEntry?.startedAt
|
|
8087
|
+
}),
|
|
8088
|
+
...unmetRequirements.length > 0 ? {
|
|
8089
|
+
unmetRequirements: unmetRequirements
|
|
7834
8090
|
} : {},
|
|
7835
|
-
...
|
|
7836
|
-
|
|
7837
|
-
} : {}
|
|
8091
|
+
...conditionInsights,
|
|
8092
|
+
actions: actions
|
|
7838
8093
|
};
|
|
7839
8094
|
}
|
|
7840
8095
|
|
|
7841
|
-
function
|
|
7842
|
-
const
|
|
8096
|
+
async function explainActivityConditions({activity: activity, activityScope: activityScope, cascadeActivityScope: cascadeActivityScope, snapshot: snapshot, sites: sites}) {
|
|
8097
|
+
const explainAt = (site, condition) => explainSite({
|
|
8098
|
+
site: site,
|
|
8099
|
+
condition: condition,
|
|
8100
|
+
params: activityScope,
|
|
8101
|
+
snapshot: snapshot,
|
|
8102
|
+
sites: sites
|
|
8103
|
+
}), requirementEntries = [];
|
|
8104
|
+
for (const [name, condition] of Object.entries(activity.requirements ?? {})) requirementEntries.push([ name, await explainAt({
|
|
8105
|
+
kind: "requirement",
|
|
8106
|
+
activity: activity.name,
|
|
8107
|
+
requirement: name
|
|
8108
|
+
}, condition) ]);
|
|
8109
|
+
const requirementInsights = Object.fromEntries(requirementEntries), unmetRequirements = requirementEntries.filter(([, insight]) => insight.outcome !== "satisfied").map(([name]) => name), filterInsight = activity.filter !== void 0 ? await explainSite({
|
|
8110
|
+
site: {
|
|
8111
|
+
kind: "activity-filter",
|
|
8112
|
+
activity: activity.name
|
|
8113
|
+
},
|
|
8114
|
+
condition: activity.filter,
|
|
8115
|
+
params: await cascadeActivityScope(),
|
|
8116
|
+
snapshot: snapshot,
|
|
8117
|
+
sites: sites
|
|
8118
|
+
}) : void 0;
|
|
7843
8119
|
return {
|
|
7844
|
-
|
|
7845
|
-
|
|
7846
|
-
|
|
7847
|
-
|
|
7848
|
-
|
|
7849
|
-
|
|
7850
|
-
|
|
7851
|
-
status: status,
|
|
7852
|
-
...outputs !== void 0 ? {
|
|
7853
|
-
outputs: outputs
|
|
7854
|
-
} : {},
|
|
7855
|
-
...detail !== void 0 ? {
|
|
7856
|
-
detail: detail
|
|
7857
|
-
} : {},
|
|
7858
|
-
...actor !== void 0 ? {
|
|
7859
|
-
actor: actor
|
|
7860
|
-
} : {}
|
|
7861
|
-
}
|
|
8120
|
+
unmetRequirements: unmetRequirements,
|
|
8121
|
+
...activity.requirements !== void 0 ? {
|
|
8122
|
+
requirementInsights: requirementInsights
|
|
8123
|
+
} : {},
|
|
8124
|
+
...filterInsight !== void 0 ? {
|
|
8125
|
+
filterInsight: filterInsight
|
|
8126
|
+
} : {}
|
|
7862
8127
|
};
|
|
7863
8128
|
}
|
|
7864
8129
|
|
|
7865
|
-
function
|
|
7866
|
-
const {
|
|
7867
|
-
|
|
7868
|
-
|
|
7869
|
-
|
|
7870
|
-
|
|
7871
|
-
|
|
7872
|
-
}
|
|
7873
|
-
const shapeIssues = effectOutputIssues(shape, value);
|
|
7874
|
-
shapeIssues !== void 0 && issues.push(...shapeIssues.map(i => `"${key}": ${i}`));
|
|
7875
|
-
}
|
|
7876
|
-
if (issues.length > 0) throw new EffectOutputsInvalidError({
|
|
7877
|
-
effect: effectName,
|
|
7878
|
-
issues: issues
|
|
8130
|
+
async function evaluateAction(args) {
|
|
8131
|
+
const {action: action, activityName: activityName, snapshot: snapshot, activityScope: activityScope, sites: sites} = args, conditionScope = isCascadeFired(action) ? await args.cascadeActivityScope() : activityScope, insights = await explainActionGates({
|
|
8132
|
+
action: action,
|
|
8133
|
+
activityName: activityName,
|
|
8134
|
+
conditionScope: conditionScope,
|
|
8135
|
+
snapshot: snapshot,
|
|
8136
|
+
sites: sites
|
|
7879
8137
|
});
|
|
7880
|
-
|
|
7881
|
-
|
|
7882
|
-
|
|
7883
|
-
|
|
7884
|
-
entryType: shape.type,
|
|
7885
|
-
value: value,
|
|
7886
|
-
...shape.fields !== void 0 ? {
|
|
7887
|
-
fields: shape.fields
|
|
7888
|
-
} : {},
|
|
7889
|
-
...shape.of !== void 0 ? {
|
|
7890
|
-
of: shape.of
|
|
7891
|
-
} : {},
|
|
7892
|
-
...shape.options !== void 0 ? {
|
|
7893
|
-
options: shape.options
|
|
7894
|
-
} : {},
|
|
7895
|
-
...shape.validation !== void 0 ? {
|
|
7896
|
-
validation: shape.validation
|
|
7897
|
-
} : {}
|
|
8138
|
+
if (action.when !== void 0) return triggeredActionVerdict({
|
|
8139
|
+
action: action,
|
|
8140
|
+
when: action.when,
|
|
8141
|
+
insights: insights
|
|
7898
8142
|
});
|
|
8143
|
+
const verdict = fireableActionVerdict({
|
|
8144
|
+
args: args,
|
|
8145
|
+
insights: insights
|
|
8146
|
+
}), firing = await args.fireConsequence(action);
|
|
8147
|
+
return firing !== void 0 ? {
|
|
8148
|
+
...verdict,
|
|
8149
|
+
firing: firing
|
|
8150
|
+
} : verdict;
|
|
7899
8151
|
}
|
|
7900
8152
|
|
|
7901
|
-
function
|
|
7902
|
-
const
|
|
7903
|
-
|
|
7904
|
-
|
|
7905
|
-
|
|
7906
|
-
|
|
7907
|
-
|
|
7908
|
-
|
|
7909
|
-
|
|
7910
|
-
|
|
7911
|
-
|
|
7912
|
-
|
|
7913
|
-
|
|
7914
|
-
|
|
7915
|
-
|
|
8153
|
+
async function explainActionGates({action: action, activityName: activityName, conditionScope: conditionScope, snapshot: snapshot, sites: sites}) {
|
|
8154
|
+
const insight = action.filter !== void 0 ? await explainSite({
|
|
8155
|
+
site: {
|
|
8156
|
+
kind: "action",
|
|
8157
|
+
activity: activityName,
|
|
8158
|
+
action: action.name
|
|
8159
|
+
},
|
|
8160
|
+
condition: action.filter,
|
|
8161
|
+
params: conditionScope,
|
|
8162
|
+
snapshot: snapshot,
|
|
8163
|
+
sites: sites
|
|
8164
|
+
}) : void 0, whenInsight = action.when !== void 0 ? await explainSite({
|
|
8165
|
+
site: {
|
|
8166
|
+
kind: "action-when",
|
|
8167
|
+
activity: activityName,
|
|
8168
|
+
action: action.name
|
|
8169
|
+
},
|
|
8170
|
+
condition: action.when,
|
|
8171
|
+
params: conditionScope,
|
|
8172
|
+
snapshot: snapshot,
|
|
8173
|
+
sites: sites
|
|
8174
|
+
}) : void 0;
|
|
8175
|
+
return {
|
|
8176
|
+
...insight !== void 0 ? {
|
|
8177
|
+
insight: insight
|
|
8178
|
+
} : {},
|
|
8179
|
+
...whenInsight !== void 0 ? {
|
|
8180
|
+
whenInsight: whenInsight
|
|
7916
8181
|
} : {}
|
|
7917
|
-
}
|
|
8182
|
+
};
|
|
7918
8183
|
}
|
|
7919
8184
|
|
|
7920
|
-
function
|
|
7921
|
-
|
|
7922
|
-
|
|
7923
|
-
|
|
7924
|
-
|
|
7925
|
-
|
|
7926
|
-
|
|
7927
|
-
|
|
7928
|
-
})
|
|
7929
|
-
|
|
7930
|
-
|
|
7931
|
-
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
}
|
|
7936
|
-
|
|
7937
|
-
|
|
8185
|
+
function triggeredActionVerdict({action: action, when: when, insights: insights}) {
|
|
8186
|
+
return insights.insight !== void 0 && insights.insight.outcome === "unsatisfied" ? disabled({
|
|
8187
|
+
action: action,
|
|
8188
|
+
reason: {
|
|
8189
|
+
kind: "filter-failed",
|
|
8190
|
+
filter: action.filter ?? ""
|
|
8191
|
+
},
|
|
8192
|
+
...insights
|
|
8193
|
+
}) : {
|
|
8194
|
+
...actionEvaluationIdentity(action),
|
|
8195
|
+
allowed: !1,
|
|
8196
|
+
triggered: !0,
|
|
8197
|
+
disabledReason: {
|
|
8198
|
+
kind: "cascade-fired",
|
|
8199
|
+
when: when
|
|
8200
|
+
},
|
|
8201
|
+
...insights
|
|
8202
|
+
};
|
|
7938
8203
|
}
|
|
7939
8204
|
|
|
7940
|
-
|
|
7941
|
-
|
|
7942
|
-
instance:
|
|
7943
|
-
record: requestRecord,
|
|
7944
|
-
now: ctx.now
|
|
7945
|
-
});
|
|
7946
|
-
const pending = requirePendingEffect(ctx.instance, effectKey), validatedOps = validateCompletionInput({
|
|
7947
|
-
pending: pending,
|
|
7948
|
-
definition: ctx.definition,
|
|
7949
|
-
status: status,
|
|
7950
|
-
ops: ops,
|
|
7951
|
-
outputs: outputs
|
|
7952
|
-
}), mutation = startMutation(ctx.instance);
|
|
7953
|
-
recordProcessedRequest({
|
|
7954
|
-
mutation: mutation,
|
|
7955
|
-
record: requestRecord,
|
|
7956
|
-
now: ctx.now
|
|
7957
|
-
}), mutation.pendingEffects = mutation.pendingEffects.filter(e => e._key !== effectKey);
|
|
7958
|
-
const ranAt = ctx.now, settlement = buildEffectSettlement(pending, {
|
|
8205
|
+
function fireableActionVerdict({args: args, insights: insights}) {
|
|
8206
|
+
const {action: action, status: status, instance: instance, stageHasExits: stageHasExits, guardDenial: guardDenial} = args, lifecycle = lifecycleReason({
|
|
8207
|
+
instance: instance,
|
|
7959
8208
|
status: status,
|
|
7960
|
-
|
|
7961
|
-
actor: actor,
|
|
7962
|
-
detail: detail,
|
|
7963
|
-
error: error,
|
|
7964
|
-
durationMs: durationMs,
|
|
7965
|
-
outputs: outputs
|
|
8209
|
+
stageHasExits: stageHasExits
|
|
7966
8210
|
});
|
|
7967
|
-
|
|
7968
|
-
|
|
7969
|
-
|
|
7970
|
-
|
|
7971
|
-
|
|
7972
|
-
|
|
7973
|
-
|
|
7974
|
-
|
|
7975
|
-
|
|
8211
|
+
if (lifecycle !== void 0) return disabled({
|
|
8212
|
+
action: action,
|
|
8213
|
+
reason: lifecycle,
|
|
8214
|
+
...insights
|
|
8215
|
+
});
|
|
8216
|
+
if (guardDenial !== void 0) return disabled({
|
|
8217
|
+
action: action,
|
|
8218
|
+
reason: guardDenial,
|
|
8219
|
+
...insights
|
|
8220
|
+
});
|
|
8221
|
+
if (args.subjectPermissionReason !== void 0 && (action.effects?.length ?? 0) > 0) return disabled({
|
|
8222
|
+
action: action,
|
|
8223
|
+
reason: args.subjectPermissionReason,
|
|
8224
|
+
...insights
|
|
8225
|
+
});
|
|
8226
|
+
if (args.requirementsReason !== void 0) return disabled({
|
|
8227
|
+
action: action,
|
|
8228
|
+
reason: args.requirementsReason,
|
|
8229
|
+
...insights
|
|
8230
|
+
});
|
|
8231
|
+
const {insight: insight} = insights;
|
|
8232
|
+
return action.filter !== void 0 && insight !== void 0 && insight.outcome !== "satisfied" ? disabled({
|
|
8233
|
+
action: action,
|
|
8234
|
+
reason: {
|
|
8235
|
+
kind: "filter-failed",
|
|
8236
|
+
filter: action.filter
|
|
7976
8237
|
},
|
|
7977
|
-
|
|
7978
|
-
|
|
7979
|
-
|
|
7980
|
-
|
|
7981
|
-
|
|
7982
|
-
refSurface: ctx.refSurface
|
|
7983
|
-
}), needsGuardRefresh = wroteEffectOutputs || ranOps.some(isFieldOp);
|
|
7984
|
-
return await persistThenMaybeRefresh({
|
|
7985
|
-
ctx: ctx,
|
|
7986
|
-
mutation: mutation,
|
|
7987
|
-
stageName: ctx.instance.currentStage,
|
|
7988
|
-
didChangeState: needsGuardRefresh
|
|
7989
|
-
}), {
|
|
7990
|
-
effectKey: effectKey,
|
|
7991
|
-
effect: pending.name,
|
|
7992
|
-
status: status,
|
|
7993
|
-
origin: pending.origin.kind
|
|
8238
|
+
...insights
|
|
8239
|
+
}) : {
|
|
8240
|
+
...actionEvaluationIdentity(action),
|
|
8241
|
+
allowed: !0,
|
|
8242
|
+
...insights
|
|
7994
8243
|
};
|
|
7995
8244
|
}
|
|
7996
8245
|
|
|
7997
|
-
function
|
|
7998
|
-
|
|
7999
|
-
|
|
8000
|
-
|
|
8001
|
-
|
|
8002
|
-
...effect.title !== void 0 ? {
|
|
8003
|
-
title: effect.title
|
|
8004
|
-
} : {},
|
|
8005
|
-
...effect.description !== void 0 ? {
|
|
8006
|
-
description: effect.description
|
|
8007
|
-
} : {},
|
|
8008
|
-
...effect.bindings !== void 0 ? {
|
|
8009
|
-
bindings: effect.bindings
|
|
8010
|
-
} : {},
|
|
8011
|
-
params: params,
|
|
8012
|
-
origin: origin,
|
|
8013
|
-
...actor !== void 0 ? {
|
|
8014
|
-
actor: actor
|
|
8015
|
-
} : {},
|
|
8016
|
-
queuedAt: now,
|
|
8017
|
-
...stageEntryKey !== void 0 ? {
|
|
8018
|
-
stageEntryKey: stageEntryKey
|
|
8246
|
+
function actionEvaluationIdentity(action) {
|
|
8247
|
+
return {
|
|
8248
|
+
action: action,
|
|
8249
|
+
...action.semantics !== void 0 ? {
|
|
8250
|
+
semantics: action.semantics
|
|
8019
8251
|
} : {}
|
|
8020
|
-
}, history = {
|
|
8021
|
-
_key: randomKey(),
|
|
8022
|
-
_type: "effectQueued",
|
|
8023
|
-
at: now,
|
|
8024
|
-
effectKey: key,
|
|
8025
|
-
effect: effect.name,
|
|
8026
|
-
origin: origin
|
|
8027
8252
|
};
|
|
8028
|
-
|
|
8029
|
-
|
|
8030
|
-
|
|
8253
|
+
}
|
|
8254
|
+
|
|
8255
|
+
async function instanceGuardReason({instance: instance, actor: actor, guards: guards}) {
|
|
8256
|
+
if (guards === void 0 || guards.length === 0) return;
|
|
8257
|
+
const denied = await instanceWriteDenials({
|
|
8258
|
+
instance: instance,
|
|
8259
|
+
guards: guards,
|
|
8260
|
+
identity: actor.id
|
|
8261
|
+
});
|
|
8262
|
+
if (denied.length !== 0) return {
|
|
8263
|
+
kind: "mutation-guard-denied",
|
|
8264
|
+
denied: deniedGuardRefs(denied)
|
|
8031
8265
|
};
|
|
8032
8266
|
}
|
|
8033
8267
|
|
|
8034
|
-
|
|
8035
|
-
|
|
8036
|
-
|
|
8037
|
-
|
|
8038
|
-
|
|
8039
|
-
|
|
8040
|
-
|
|
8041
|
-
|
|
8042
|
-
|
|
8043
|
-
|
|
8044
|
-
|
|
8268
|
+
function lifecycleReason({instance: instance, status: status, stageHasExits: stageHasExits}) {
|
|
8269
|
+
const terminalReason = instanceTerminalReason(instance);
|
|
8270
|
+
if (terminalReason !== void 0) return terminalReason;
|
|
8271
|
+
if (!stageHasExits) return {
|
|
8272
|
+
kind: "stage-terminal",
|
|
8273
|
+
stage: instance.currentStage
|
|
8274
|
+
};
|
|
8275
|
+
if (isTerminalActivityStatus(status)) return {
|
|
8276
|
+
kind: "activity-not-active",
|
|
8277
|
+
status: status
|
|
8278
|
+
};
|
|
8279
|
+
}
|
|
8280
|
+
|
|
8281
|
+
function disabled(args) {
|
|
8282
|
+
return {
|
|
8283
|
+
...actionEvaluationIdentity(args.action),
|
|
8284
|
+
allowed: !1,
|
|
8285
|
+
disabledReason: args.reason,
|
|
8286
|
+
...args.insight !== void 0 ? {
|
|
8287
|
+
insight: args.insight
|
|
8045
8288
|
} : {},
|
|
8046
|
-
|
|
8047
|
-
|
|
8048
|
-
}
|
|
8049
|
-
}
|
|
8050
|
-
for (const effect of effects) {
|
|
8051
|
-
const resolved = await resolveBindings({
|
|
8052
|
-
bindings: effect.bindings,
|
|
8053
|
-
staticInput: effect.input,
|
|
8054
|
-
snapshot: ctx.snapshot,
|
|
8055
|
-
params: params
|
|
8056
|
-
}), {pending: pending, history: history} = buildQueuedEffect({
|
|
8057
|
-
effect: effect,
|
|
8058
|
-
origin: origin,
|
|
8059
|
-
params: resolved,
|
|
8060
|
-
actor: actor,
|
|
8061
|
-
now: now,
|
|
8062
|
-
stageEntryKey: stageEntryKey
|
|
8063
|
-
});
|
|
8064
|
-
mutation.pendingEffects.push(pending), mutation.history.push(history);
|
|
8065
|
-
}
|
|
8289
|
+
...args.whenInsight !== void 0 ? {
|
|
8290
|
+
whenInsight: args.whenInsight
|
|
8291
|
+
} : {}
|
|
8292
|
+
};
|
|
8066
8293
|
}
|
|
8067
8294
|
|
|
8068
8295
|
function inFlightFilter() {
|
|
@@ -8090,25 +8317,81 @@ function idsArm(filter, params) {
|
|
|
8090
8317
|
}
|
|
8091
8318
|
}
|
|
8092
8319
|
|
|
8093
|
-
function instancesQuery(args) {
|
|
8094
|
-
const {tag: tag, filter: filter = {}} = args;
|
|
8095
|
-
if (validateTag(tag), filter.limit !== void 0 && (!Number.isInteger(filter.limit) || filter.limit <= 0)) throw new ContractViolationError(`instancesQuery: limit must be a positive integer; got ${JSON.stringify(filter.limit)}`);
|
|
8096
|
-
const conditions = [ `_type == "${WORKFLOW_INSTANCE_TYPE}"`, tagScopeFilter() ], params = {
|
|
8097
|
-
tag: tag
|
|
8098
|
-
};
|
|
8099
|
-
filter.includeCompleted !== !0 && conditions.push(inFlightFilter()), filter.definition !== void 0 && (conditions.push("definition == $definition"),
|
|
8100
|
-
params.definition = filter.definition), filter.stage !== void 0 && (conditions.push("currentStage == $stage"),
|
|
8101
|
-
params.stage = filter.stage);
|
|
8102
|
-
const arms = [ documentArm(filter, params), idsArm(filter, params) ].filter(arm => arm !== void 0);
|
|
8103
|
-
arms.length > 0 && conditions.push(`(${arms.join(" || ")})`);
|
|
8104
|
-
const body = `*[${conditions.join(" && ")}]`;
|
|
8105
|
-
return filter.limit !== void 0 ? {
|
|
8106
|
-
query: `${body} | order(startedAt desc) [0...${filter.limit}]`,
|
|
8107
|
-
params: params
|
|
8108
|
-
} : {
|
|
8109
|
-
query: `${body} | order(startedAt asc)`,
|
|
8110
|
-
params: params
|
|
8111
|
-
};
|
|
8320
|
+
function instancesQuery(args) {
|
|
8321
|
+
const {tag: tag, filter: filter = {}} = args;
|
|
8322
|
+
if (validateTag(tag), filter.limit !== void 0 && (!Number.isInteger(filter.limit) || filter.limit <= 0)) throw new ContractViolationError(`instancesQuery: limit must be a positive integer; got ${JSON.stringify(filter.limit)}`);
|
|
8323
|
+
const conditions = [ `_type == "${WORKFLOW_INSTANCE_TYPE}"`, tagScopeFilter() ], params = {
|
|
8324
|
+
tag: tag
|
|
8325
|
+
};
|
|
8326
|
+
filter.includeCompleted !== !0 && conditions.push(inFlightFilter()), filter.definition !== void 0 && (conditions.push("definition == $definition"),
|
|
8327
|
+
params.definition = filter.definition), filter.stage !== void 0 && (conditions.push("currentStage == $stage"),
|
|
8328
|
+
params.stage = filter.stage);
|
|
8329
|
+
const arms = [ documentArm(filter, params), idsArm(filter, params) ].filter(arm => arm !== void 0);
|
|
8330
|
+
arms.length > 0 && conditions.push(`(${arms.join(" || ")})`);
|
|
8331
|
+
const body = `*[${conditions.join(" && ")}]`;
|
|
8332
|
+
return filter.limit !== void 0 ? {
|
|
8333
|
+
query: `${body} | order(startedAt desc) [0...${filter.limit}]`,
|
|
8334
|
+
params: params
|
|
8335
|
+
} : {
|
|
8336
|
+
query: `${body} | order(startedAt asc)`,
|
|
8337
|
+
params: params
|
|
8338
|
+
};
|
|
8339
|
+
}
|
|
8340
|
+
|
|
8341
|
+
const DEFAULT_EFFECT_LEASE_MS = 300 * 1e3;
|
|
8342
|
+
|
|
8343
|
+
function claimReleasedEntry(args) {
|
|
8344
|
+
return {
|
|
8345
|
+
_key: randomKey(),
|
|
8346
|
+
_type: "effectClaimReleased",
|
|
8347
|
+
at: args.at,
|
|
8348
|
+
effectKey: args.pending._key,
|
|
8349
|
+
effect: args.pending.name,
|
|
8350
|
+
claim: args.claim,
|
|
8351
|
+
via: args.via,
|
|
8352
|
+
actor: args.actor
|
|
8353
|
+
};
|
|
8354
|
+
}
|
|
8355
|
+
|
|
8356
|
+
const SWEEP_MAX_ATTEMPTS = 5;
|
|
8357
|
+
|
|
8358
|
+
async function sweepStaleClaims(args) {
|
|
8359
|
+
const {tag: tag, instanceId: instanceId} = args, client = pinApiVersion(args.client);
|
|
8360
|
+
validateTag(tag);
|
|
8361
|
+
const clock = args.clock ?? wallClock, {actor: actor} = await resolveAccess(client), stamp = resolveExecutionContext(args.executionContext);
|
|
8362
|
+
for (let attempt = 1; attempt <= SWEEP_MAX_ATTEMPTS; attempt++) {
|
|
8363
|
+
const now = clock(), instance = await reload({
|
|
8364
|
+
client: client,
|
|
8365
|
+
instanceId: instanceId,
|
|
8366
|
+
tag: tag
|
|
8367
|
+
}), expired = instance.pendingEffects.filter(e => e.claim !== void 0 && isClaimExpired(e.claim, now));
|
|
8368
|
+
if (expired.length === 0) return {
|
|
8369
|
+
released: []
|
|
8370
|
+
};
|
|
8371
|
+
const expiredKeys = new Set(expired.map(e => e._key)), releasedQueue = instance.pendingEffects.map(e => expiredKeys.has(e._key) ? stripClaim(e) : e), auditRows = stampHistoryEntries(expired.map(pending => claimReleasedEntry({
|
|
8372
|
+
pending: pending,
|
|
8373
|
+
claim: pending.claim,
|
|
8374
|
+
at: now,
|
|
8375
|
+
via: "sweep",
|
|
8376
|
+
actor: actor
|
|
8377
|
+
})), stamp);
|
|
8378
|
+
try {
|
|
8379
|
+
return await client.patch(instance._id).ifRevisionId(instance._rev).set({
|
|
8380
|
+
pendingEffects: releasedQueue,
|
|
8381
|
+
history: [ ...instance.history, ...auditRows ]
|
|
8382
|
+
}).commit(SYNC_COMMIT), {
|
|
8383
|
+
released: expired
|
|
8384
|
+
};
|
|
8385
|
+
} catch (error) {
|
|
8386
|
+
if (!isRevisionConflict(error)) throw error;
|
|
8387
|
+
}
|
|
8388
|
+
}
|
|
8389
|
+
throw new Error(`sweepStaleClaims lost the optimistic-locking race ${SWEEP_MAX_ATTEMPTS} attempts running on ${instanceId} — a concurrent writer kept committing first. Retry later, or investigate a write storm on this instance.`);
|
|
8390
|
+
}
|
|
8391
|
+
|
|
8392
|
+
function stripClaim(entry) {
|
|
8393
|
+
const {claim: _claim, ...rest} = entry;
|
|
8394
|
+
return rest;
|
|
8112
8395
|
}
|
|
8113
8396
|
|
|
8114
8397
|
async function sortByDependencies({client: client, definitions: definitions, tag: tag}) {
|
|
@@ -9402,6 +9685,66 @@ const workflow = {
|
|
|
9402
9685
|
}
|
|
9403
9686
|
});
|
|
9404
9687
|
},
|
|
9688
|
+
commitEffectOps: async rawArgs => {
|
|
9689
|
+
const args = taggedScope(rawArgs, REQUEST_TAG.commitEffectOps), {client: client, tag: tag, instanceId: instanceId, effectKey: effectKey, claimToken: claimToken, ops: ops, executionContext: executionContext} = args, {actor: actor, clientForGdr: clientForGdr, refSurface: refSurface} = await resolveOperationContext(args), clock = args.clock ?? wallClock, record = requestRecordFor({
|
|
9690
|
+
idempotencyKey: args.idempotencyKey,
|
|
9691
|
+
op: "commitEffectOps",
|
|
9692
|
+
idempotencyTtlMs: args.idempotencyTtlMs
|
|
9693
|
+
});
|
|
9694
|
+
if (record === void 0) throw new ContractViolationError("commitEffectOps: idempotencyKey is required — the engine cannot assume a supplied field op is idempotent, so every mid-dispatch report must be retry-safe");
|
|
9695
|
+
return runDeduped({
|
|
9696
|
+
client: client,
|
|
9697
|
+
tag: tag,
|
|
9698
|
+
instanceId: instanceId,
|
|
9699
|
+
record: record,
|
|
9700
|
+
now: clock(),
|
|
9701
|
+
actor: actor,
|
|
9702
|
+
clientForGdr: clientForGdr,
|
|
9703
|
+
refSurface: refSurface,
|
|
9704
|
+
clock: clock,
|
|
9705
|
+
executionContext: executionContext,
|
|
9706
|
+
telemetry: args.telemetry,
|
|
9707
|
+
run: async () => {
|
|
9708
|
+
const report = await commitEffectOps({
|
|
9709
|
+
client: client,
|
|
9710
|
+
instanceId: instanceId,
|
|
9711
|
+
effectKey: effectKey,
|
|
9712
|
+
claimToken: claimToken,
|
|
9713
|
+
ops: ops,
|
|
9714
|
+
requestRecord: record,
|
|
9715
|
+
leaseMs: args.leaseMs ?? DEFAULT_EFFECT_LEASE_MS,
|
|
9716
|
+
options: engineOptionsForActor({
|
|
9717
|
+
actor: actor,
|
|
9718
|
+
clock: clock,
|
|
9719
|
+
clientForGdr: clientForGdr,
|
|
9720
|
+
refSurface: refSurface,
|
|
9721
|
+
executionContext: executionContext,
|
|
9722
|
+
telemetry: args.telemetry
|
|
9723
|
+
})
|
|
9724
|
+
}), {cascaded: cascaded, instance: instance} = await cascadeAndReload({
|
|
9725
|
+
client: client,
|
|
9726
|
+
tag: tag,
|
|
9727
|
+
instanceId: instanceId,
|
|
9728
|
+
actor: actor,
|
|
9729
|
+
clientForGdr: clientForGdr,
|
|
9730
|
+
refSurface: refSurface,
|
|
9731
|
+
clock: clock,
|
|
9732
|
+
executionContext: executionContext,
|
|
9733
|
+
telemetry: args.telemetry
|
|
9734
|
+
});
|
|
9735
|
+
return resolveTelemetry(args.telemetry).log(WorkflowEffectStateReported, {
|
|
9736
|
+
...definitionHashFragment(instance.pinnedContentHash),
|
|
9737
|
+
instanceId: instanceId,
|
|
9738
|
+
effect: report.effect,
|
|
9739
|
+
cascaded: cascaded
|
|
9740
|
+
}), {
|
|
9741
|
+
instance: instance,
|
|
9742
|
+
cascaded: cascaded,
|
|
9743
|
+
changed: !0
|
|
9744
|
+
};
|
|
9745
|
+
}
|
|
9746
|
+
});
|
|
9747
|
+
},
|
|
9405
9748
|
tick: async rawArgs => {
|
|
9406
9749
|
const args = taggedScope(rawArgs, REQUEST_TAG.tick), {client: client, tag: tag, instanceId: instanceId, executionContext: executionContext} = args, {actor: actor, clientForGdr: clientForGdr, refSurface: refSurface} = await resolveOperationContext(args), clock = args.clock ?? wallClock, current = await reload({
|
|
9407
9750
|
client: client,
|
|
@@ -10003,64 +10346,100 @@ async function resolveActor(directory, actor) {
|
|
|
10003
10346
|
};
|
|
10004
10347
|
}
|
|
10005
10348
|
|
|
10006
|
-
const
|
|
10349
|
+
const EFFECT_COMMIT_QUEUE_DEPTH = 32, EFFECT_COMMIT_DISPATCH_CAP = 200;
|
|
10007
10350
|
|
|
10008
|
-
|
|
10009
|
-
|
|
10351
|
+
class EffectCommitQueueOverflowError extends WorkflowError {
|
|
10352
|
+
effectKey;
|
|
10353
|
+
bound;
|
|
10354
|
+
limit;
|
|
10355
|
+
constructor(args) {
|
|
10356
|
+
super("effect-commit-queue-overflow", args.bound === "queue-depth" ? `Effect dispatch "${args.effectKey}" has ${args.limit} mid-dispatch commits queued — await your ctx.commitOps/ctx.setProgress calls (or report less often); the queue serializes real engine commits and cannot grow without bound` : `Effect dispatch "${args.effectKey}" already made ${args.limit} mid-dispatch commits — the per-dispatch cap bounds instance history growth; report more coarsely`),
|
|
10357
|
+
this.name = "EffectCommitQueueOverflowError", this.effectKey = args.effectKey, this.bound = args.bound,
|
|
10358
|
+
this.limit = args.limit;
|
|
10359
|
+
}
|
|
10010
10360
|
}
|
|
10011
10361
|
|
|
10012
|
-
function
|
|
10013
|
-
return
|
|
10014
|
-
_key: randomKey(),
|
|
10015
|
-
_type: "effectClaimReleased",
|
|
10016
|
-
at: args.at,
|
|
10017
|
-
effectKey: args.pending._key,
|
|
10018
|
-
effect: args.pending.name,
|
|
10019
|
-
claim: args.claim,
|
|
10020
|
-
via: args.via,
|
|
10021
|
-
actor: args.actor
|
|
10022
|
-
};
|
|
10362
|
+
function isClaimLoss(err) {
|
|
10363
|
+
return err instanceof StaleEffectClaimError || err instanceof EffectNotFoundError;
|
|
10023
10364
|
}
|
|
10024
10365
|
|
|
10025
|
-
|
|
10366
|
+
function pendingEntry(args) {
|
|
10367
|
+
let resolve, reject;
|
|
10368
|
+
const promise = new Promise((res, rej) => {
|
|
10369
|
+
resolve = res, reject = rej;
|
|
10370
|
+
});
|
|
10371
|
+
return promise.catch(() => {}), {
|
|
10372
|
+
...args,
|
|
10373
|
+
resolve: resolve,
|
|
10374
|
+
reject: reject,
|
|
10375
|
+
promise: promise
|
|
10376
|
+
};
|
|
10377
|
+
}
|
|
10026
10378
|
|
|
10027
|
-
|
|
10028
|
-
const {
|
|
10029
|
-
|
|
10030
|
-
|
|
10031
|
-
|
|
10032
|
-
|
|
10033
|
-
|
|
10034
|
-
|
|
10035
|
-
|
|
10036
|
-
|
|
10037
|
-
|
|
10038
|
-
|
|
10039
|
-
};
|
|
10040
|
-
|
|
10041
|
-
|
|
10042
|
-
|
|
10043
|
-
|
|
10044
|
-
|
|
10045
|
-
|
|
10046
|
-
|
|
10047
|
-
|
|
10048
|
-
|
|
10049
|
-
|
|
10050
|
-
|
|
10051
|
-
|
|
10052
|
-
|
|
10053
|
-
|
|
10054
|
-
|
|
10055
|
-
|
|
10379
|
+
function createEffectCommitQueue(args) {
|
|
10380
|
+
const {effectKey: effectKey, claimToken: claimToken, commit: commit} = args, pending = [];
|
|
10381
|
+
let admitted = 0, dead, closed = !1, tail = Promise.resolve();
|
|
10382
|
+
function assertOpen() {
|
|
10383
|
+
if (dead !== void 0) throw dead;
|
|
10384
|
+
if (closed) throw new Error(`Effect dispatch "${effectKey}" already settled — a mid-dispatch report after the handler returned cannot commit; await your reports inside the handler`);
|
|
10385
|
+
}
|
|
10386
|
+
function admit(entry) {
|
|
10387
|
+
if (assertOpen(), pending.length >= EFFECT_COMMIT_QUEUE_DEPTH) throw new EffectCommitQueueOverflowError({
|
|
10388
|
+
effectKey: effectKey,
|
|
10389
|
+
bound: "queue-depth",
|
|
10390
|
+
limit: EFFECT_COMMIT_QUEUE_DEPTH
|
|
10391
|
+
});
|
|
10392
|
+
if (admitted >= EFFECT_COMMIT_DISPATCH_CAP) throw new EffectCommitQueueOverflowError({
|
|
10393
|
+
effectKey: effectKey,
|
|
10394
|
+
bound: "dispatch-cap",
|
|
10395
|
+
limit: EFFECT_COMMIT_DISPATCH_CAP
|
|
10396
|
+
});
|
|
10397
|
+
return admitted += 1, pending.push(entry), tail = tail.then(runNext), entry.promise;
|
|
10398
|
+
}
|
|
10399
|
+
function die(err) {
|
|
10400
|
+
dead = err;
|
|
10401
|
+
for (const entry of pending.splice(0)) entry.reject(err);
|
|
10402
|
+
}
|
|
10403
|
+
async function runNext() {
|
|
10404
|
+
const entry = pending.shift();
|
|
10405
|
+
if (entry !== void 0) try {
|
|
10406
|
+
await commit({
|
|
10407
|
+
ops: entry.buildOps(),
|
|
10408
|
+
idempotencyKey: entry.idempotencyKey
|
|
10409
|
+
}), entry.resolve();
|
|
10410
|
+
} catch (err) {
|
|
10411
|
+
entry.reject(err), die(err);
|
|
10056
10412
|
}
|
|
10057
10413
|
}
|
|
10058
|
-
|
|
10059
|
-
|
|
10060
|
-
|
|
10061
|
-
|
|
10062
|
-
|
|
10063
|
-
|
|
10414
|
+
return {
|
|
10415
|
+
commitOps: req => admit(pendingEntry({
|
|
10416
|
+
buildOps: () => req.ops,
|
|
10417
|
+
idempotencyKey: req.idempotencyKey
|
|
10418
|
+
})),
|
|
10419
|
+
setProgress: (target, value) => {
|
|
10420
|
+
assertOpen();
|
|
10421
|
+
const ref = typeof target == "string" ? {
|
|
10422
|
+
scope: "workflow",
|
|
10423
|
+
field: target
|
|
10424
|
+
} : target, coalesceKey = `${ref.scope}:${ref.field}`, queued = pending.find(entry2 => entry2.coalesceKey === coalesceKey);
|
|
10425
|
+
if (queued !== void 0) return queued.latestValue = value, queued.promise;
|
|
10426
|
+
const entry = pendingEntry({
|
|
10427
|
+
buildOps: () => [ {
|
|
10428
|
+
type: "field.set",
|
|
10429
|
+
target: ref,
|
|
10430
|
+
value: {
|
|
10431
|
+
type: "literal",
|
|
10432
|
+
value: entry.latestValue
|
|
10433
|
+
}
|
|
10434
|
+
} ],
|
|
10435
|
+
idempotencyKey: `effect:${effectKey}:${claimToken}:progress:${coalesceKey}:${admitted}`,
|
|
10436
|
+
coalesceKey: coalesceKey,
|
|
10437
|
+
latestValue: value
|
|
10438
|
+
});
|
|
10439
|
+
return admit(entry);
|
|
10440
|
+
},
|
|
10441
|
+
settle: async () => (closed = !0, await tail, dead !== void 0 && !isClaimLoss(dead) ? dead : void 0)
|
|
10442
|
+
};
|
|
10064
10443
|
}
|
|
10065
10444
|
|
|
10066
10445
|
class MissingHandlerError extends WorkflowError {
|
|
@@ -10156,74 +10535,126 @@ async function drainEffectsInternal(args) {
|
|
|
10156
10535
|
failed: [],
|
|
10157
10536
|
skipped: [],
|
|
10158
10537
|
lost: []
|
|
10159
|
-
},
|
|
10160
|
-
|
|
10161
|
-
|
|
10162
|
-
|
|
10163
|
-
|
|
10164
|
-
|
|
10165
|
-
|
|
10166
|
-
|
|
10167
|
-
|
|
10168
|
-
|
|
10169
|
-
|
|
10170
|
-
|
|
10171
|
-
|
|
10172
|
-
|
|
10173
|
-
|
|
10174
|
-
|
|
10175
|
-
|
|
10176
|
-
|
|
10177
|
-
|
|
10178
|
-
|
|
10179
|
-
|
|
10180
|
-
|
|
10181
|
-
|
|
10182
|
-
|
|
10183
|
-
|
|
10184
|
-
client: client,
|
|
10185
|
-
instance: before,
|
|
10186
|
-
candidate: candidate,
|
|
10187
|
-
drainerActor: drainerActor,
|
|
10188
|
-
now: now,
|
|
10189
|
-
leaseMs: leaseMs,
|
|
10190
|
-
...executionContext !== void 0 ? {
|
|
10191
|
-
executionContext: executionContext
|
|
10192
|
-
} : {}
|
|
10193
|
-
})) continue;
|
|
10194
|
-
const outcome = await dispatchAndReport({
|
|
10195
|
-
handler: handler,
|
|
10196
|
-
candidate: candidate,
|
|
10197
|
-
client: client,
|
|
10198
|
-
handlerClient: handlerClient,
|
|
10199
|
-
tag: tag,
|
|
10200
|
-
workflowResource: workflowResource,
|
|
10201
|
-
...resourceClients !== void 0 ? {
|
|
10202
|
-
resourceClients: resourceClients
|
|
10203
|
-
} : {},
|
|
10204
|
-
instanceId: instanceId,
|
|
10205
|
-
clientFor: clientFor,
|
|
10206
|
-
logger: logger,
|
|
10207
|
-
log: log,
|
|
10208
|
-
...executionContext !== void 0 ? {
|
|
10209
|
-
executionContext: executionContext
|
|
10210
|
-
} : {},
|
|
10211
|
-
telemetry: cascadeTelemetry,
|
|
10212
|
-
clock: clock
|
|
10213
|
-
});
|
|
10214
|
-
buckets[outcome].push(candidate);
|
|
10215
|
-
}
|
|
10538
|
+
}, pass = {
|
|
10539
|
+
client: client,
|
|
10540
|
+
handlerClient: handlerClient,
|
|
10541
|
+
tag: tag,
|
|
10542
|
+
workflowResource: workflowResource,
|
|
10543
|
+
...resourceClients !== void 0 ? {
|
|
10544
|
+
resourceClients: resourceClients
|
|
10545
|
+
} : {},
|
|
10546
|
+
instanceId: instanceId,
|
|
10547
|
+
effectHandlers: effectHandlers,
|
|
10548
|
+
missingHandler: missingHandler,
|
|
10549
|
+
logger: logger,
|
|
10550
|
+
log: log,
|
|
10551
|
+
leaseMs: leaseMs,
|
|
10552
|
+
clock: clock,
|
|
10553
|
+
...executionContext !== void 0 ? {
|
|
10554
|
+
executionContext: executionContext
|
|
10555
|
+
} : {},
|
|
10556
|
+
telemetry: cascadeTelemetry,
|
|
10557
|
+
drainerActor: drainerActor,
|
|
10558
|
+
clientFor: clientFor,
|
|
10559
|
+
buckets: buckets,
|
|
10560
|
+
skippedKeys: /* @__PURE__ */ new Set
|
|
10561
|
+
};
|
|
10562
|
+
for (;await drainOneCandidate(pass); ) ;
|
|
10216
10563
|
return buckets;
|
|
10217
10564
|
}
|
|
10218
10565
|
|
|
10566
|
+
async function drainOneCandidate(pass) {
|
|
10567
|
+
const {client: client, tag: tag, instanceId: instanceId, buckets: buckets, skippedKeys: skippedKeys} = pass, now = pass.clock(), before = await reload({
|
|
10568
|
+
client: client,
|
|
10569
|
+
instanceId: instanceId,
|
|
10570
|
+
tag: tag
|
|
10571
|
+
});
|
|
10572
|
+
noteDrainedInstanceHash(buckets, before);
|
|
10573
|
+
const candidate = findClaimableCandidate({
|
|
10574
|
+
instance: before,
|
|
10575
|
+
now: now,
|
|
10576
|
+
skippedKeys: skippedKeys
|
|
10577
|
+
});
|
|
10578
|
+
if (candidate === void 0) return !1;
|
|
10579
|
+
const handler = pass.effectHandlers[candidate.name];
|
|
10580
|
+
if (handler === void 0) return await assertSkippableOrThrow({
|
|
10581
|
+
missingHandler: pass.missingHandler,
|
|
10582
|
+
candidate: candidate,
|
|
10583
|
+
instanceId: instanceId,
|
|
10584
|
+
log: pass.log
|
|
10585
|
+
}), buckets.skipped.push(candidate), skippedKeys.add(candidate._key), !0;
|
|
10586
|
+
const claimed = await claimPendingEffect({
|
|
10587
|
+
client: client,
|
|
10588
|
+
instance: before,
|
|
10589
|
+
candidate: candidate,
|
|
10590
|
+
drainerActor: pass.drainerActor,
|
|
10591
|
+
now: now,
|
|
10592
|
+
leaseMs: pass.leaseMs,
|
|
10593
|
+
...pass.executionContext !== void 0 ? {
|
|
10594
|
+
executionContext: pass.executionContext
|
|
10595
|
+
} : {}
|
|
10596
|
+
});
|
|
10597
|
+
if (claimed === void 0) return !0;
|
|
10598
|
+
const outcome = await dispatchAndReport({
|
|
10599
|
+
handler: handler,
|
|
10600
|
+
candidate: candidate,
|
|
10601
|
+
claimToken: claimed.claimToken,
|
|
10602
|
+
leaseMs: pass.leaseMs,
|
|
10603
|
+
client: client,
|
|
10604
|
+
handlerClient: pass.handlerClient,
|
|
10605
|
+
tag: tag,
|
|
10606
|
+
workflowResource: pass.workflowResource,
|
|
10607
|
+
...pass.resourceClients !== void 0 ? {
|
|
10608
|
+
resourceClients: pass.resourceClients
|
|
10609
|
+
} : {},
|
|
10610
|
+
instanceId: instanceId,
|
|
10611
|
+
clientFor: pass.clientFor,
|
|
10612
|
+
logger: pass.logger,
|
|
10613
|
+
log: pass.log,
|
|
10614
|
+
...pass.executionContext !== void 0 ? {
|
|
10615
|
+
executionContext: pass.executionContext
|
|
10616
|
+
} : {},
|
|
10617
|
+
telemetry: pass.telemetry,
|
|
10618
|
+
clock: pass.clock
|
|
10619
|
+
});
|
|
10620
|
+
return buckets[outcome].push(candidate), !0;
|
|
10621
|
+
}
|
|
10622
|
+
|
|
10219
10623
|
function findClaimableCandidate({instance: instance, now: now, skippedKeys: skippedKeys}) {
|
|
10220
10624
|
return instance.pendingEffects.find(e => (e.claim === void 0 || isClaimExpired(e.claim, now)) && !skippedKeys.has(e._key));
|
|
10221
10625
|
}
|
|
10222
10626
|
|
|
10223
10627
|
async function dispatchAndReport(args) {
|
|
10224
|
-
const {handler: handler, candidate: candidate, client: client, handlerClient: handlerClient, tag: tag, workflowResource: workflowResource, resourceClients: resourceClients, instanceId: instanceId, clientFor: clientFor, logger: logger, log: log, executionContext: executionContext, telemetry: telemetry, clock: clock} = args,
|
|
10628
|
+
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, commitQueue = createEffectCommitQueue({
|
|
10629
|
+
effectKey: candidate._key,
|
|
10630
|
+
claimToken: claimToken,
|
|
10631
|
+
commit: async req => {
|
|
10632
|
+
await workflow.commitEffectOps({
|
|
10633
|
+
client: client,
|
|
10634
|
+
tag: tag,
|
|
10635
|
+
workflowResource: workflowResource,
|
|
10636
|
+
...resourceClients !== void 0 ? {
|
|
10637
|
+
resourceClients: resourceClients
|
|
10638
|
+
} : {},
|
|
10639
|
+
...executionContext !== void 0 ? {
|
|
10640
|
+
executionContext: executionContext
|
|
10641
|
+
} : {},
|
|
10642
|
+
...telemetry !== void 0 ? {
|
|
10643
|
+
telemetry: telemetry
|
|
10644
|
+
} : {},
|
|
10645
|
+
instanceId: instanceId,
|
|
10646
|
+
effectKey: candidate._key,
|
|
10647
|
+
claimToken: claimToken,
|
|
10648
|
+
ops: req.ops,
|
|
10649
|
+
idempotencyKey: req.idempotencyKey,
|
|
10650
|
+
leaseMs: leaseMs,
|
|
10651
|
+
clock: clock
|
|
10652
|
+
});
|
|
10653
|
+
}
|
|
10654
|
+
}), {outputs: outputs, ops: ops, dispatchError: dispatchError} = await dispatchEffect({
|
|
10225
10655
|
handler: handler,
|
|
10226
10656
|
candidate: candidate,
|
|
10657
|
+
queue: commitQueue,
|
|
10227
10658
|
ctx: {
|
|
10228
10659
|
client: handlerClient,
|
|
10229
10660
|
clientFor: clientFor,
|
|
@@ -10303,11 +10734,12 @@ async function assertSkippableOrThrow({missingHandler: missingHandler, candidate
|
|
|
10303
10734
|
}
|
|
10304
10735
|
|
|
10305
10736
|
async function claimPendingEffect({client: client, instance: instance, candidate: candidate, drainerActor: drainerActor, now: now, leaseMs: leaseMs, executionContext: executionContext}) {
|
|
10306
|
-
const priorClaim = candidate.claim, claim = {
|
|
10737
|
+
const priorClaim = candidate.claim, claimToken = randomKey(), claim = {
|
|
10307
10738
|
_type: "pendingEffect.claim",
|
|
10308
10739
|
claimedAt: now,
|
|
10309
10740
|
claimedBy: drainerActor,
|
|
10310
|
-
leaseExpiresAt: addMs(now, leaseMs)
|
|
10741
|
+
leaseExpiresAt: addMs(now, leaseMs),
|
|
10742
|
+
claimToken: claimToken
|
|
10311
10743
|
}, takeoverAudit = priorClaim !== void 0 ? stampHistoryEntries([ claimReleasedEntry({
|
|
10312
10744
|
pending: candidate,
|
|
10313
10745
|
claim: priorClaim,
|
|
@@ -10321,43 +10753,64 @@ async function claimPendingEffect({client: client, instance: instance, candidate
|
|
|
10321
10753
|
...takeoverAudit !== void 0 ? {
|
|
10322
10754
|
history: [ ...instance.history, takeoverAudit ]
|
|
10323
10755
|
} : {}
|
|
10324
|
-
}).commit(SYNC_COMMIT),
|
|
10756
|
+
}).commit(SYNC_COMMIT), {
|
|
10757
|
+
claimToken: claimToken
|
|
10758
|
+
};
|
|
10325
10759
|
} catch (error) {
|
|
10326
10760
|
if (!isRevisionConflict(error)) throw error;
|
|
10327
|
-
return
|
|
10761
|
+
return;
|
|
10328
10762
|
}
|
|
10329
10763
|
}
|
|
10330
10764
|
|
|
10331
|
-
async function
|
|
10765
|
+
async function invokeHandler({handler: handler, candidate: candidate, queue: queue, ctx: ctx}) {
|
|
10332
10766
|
try {
|
|
10333
10767
|
const result = await handler(candidate.params, {
|
|
10334
10768
|
client: ctx.client,
|
|
10335
10769
|
clientFor: ctx.clientFor,
|
|
10336
10770
|
instanceId: ctx.instanceId,
|
|
10337
10771
|
effectKey: candidate._key,
|
|
10338
|
-
log: (message, extra) => ctx.logger(`effect.${candidate.name}`).info(message, extra)
|
|
10772
|
+
log: (message, extra) => ctx.logger(`effect.${candidate.name}`).info(message, extra),
|
|
10773
|
+
commitOps: queue.commitOps,
|
|
10774
|
+
setProgress: queue.setProgress
|
|
10339
10775
|
});
|
|
10340
|
-
return {
|
|
10341
|
-
|
|
10342
|
-
outputs: result.outputs
|
|
10343
|
-
} : {},
|
|
10344
|
-
...result?.ops !== void 0 ? {
|
|
10345
|
-
ops: result.ops
|
|
10346
|
-
} : {}
|
|
10776
|
+
return result === void 0 ? {} : {
|
|
10777
|
+
result: result
|
|
10347
10778
|
};
|
|
10348
10779
|
} catch (err) {
|
|
10349
|
-
const message = errorMessage(err), stack = err instanceof Error && err.stack !== void 0 ? err.stack : void 0;
|
|
10350
10780
|
return {
|
|
10351
|
-
|
|
10352
|
-
|
|
10353
|
-
stack: stack
|
|
10354
|
-
} : {
|
|
10355
|
-
message: message
|
|
10781
|
+
thrown: {
|
|
10782
|
+
err: err
|
|
10356
10783
|
}
|
|
10357
10784
|
};
|
|
10358
10785
|
}
|
|
10359
10786
|
}
|
|
10360
10787
|
|
|
10788
|
+
function shapeDispatchError(err) {
|
|
10789
|
+
const message = errorMessage(err), stack = err instanceof Error && err.stack !== void 0 ? err.stack : void 0;
|
|
10790
|
+
return stack !== void 0 ? {
|
|
10791
|
+
message: message,
|
|
10792
|
+
stack: stack
|
|
10793
|
+
} : {
|
|
10794
|
+
message: message
|
|
10795
|
+
};
|
|
10796
|
+
}
|
|
10797
|
+
|
|
10798
|
+
async function dispatchEffect(args) {
|
|
10799
|
+
const {result: result, thrown: thrown} = await invokeHandler(args), settleFailure = await args.queue.settle(), failed = thrown ?? (settleFailure !== void 0 ? {
|
|
10800
|
+
err: settleFailure
|
|
10801
|
+
} : void 0);
|
|
10802
|
+
return failed !== void 0 ? {
|
|
10803
|
+
dispatchError: shapeDispatchError(failed.err)
|
|
10804
|
+
} : {
|
|
10805
|
+
...result?.outputs !== void 0 ? {
|
|
10806
|
+
outputs: result.outputs
|
|
10807
|
+
} : {},
|
|
10808
|
+
...result?.ops !== void 0 ? {
|
|
10809
|
+
ops: result.ops
|
|
10810
|
+
} : {}
|
|
10811
|
+
};
|
|
10812
|
+
}
|
|
10813
|
+
|
|
10361
10814
|
async function applyMissingHandler({policy: policy, info: info, log: log}) {
|
|
10362
10815
|
if (policy === "fail") return "fail";
|
|
10363
10816
|
if (policy === "skip") return log.warn(`Missing effect handler "${info.name}" — skipping`, {
|
|
@@ -10834,6 +11287,7 @@ function createEngine(args) {
|
|
|
10834
11287
|
fireAction: rest => workflow.fireAction(withScope(rest)),
|
|
10835
11288
|
editField: rest => workflow.editField(withScope(rest)),
|
|
10836
11289
|
completeEffect: rest => workflow.completeEffect(withScope(rest)),
|
|
11290
|
+
commitEffectOps: rest => workflow.commitEffectOps(withScope(rest)),
|
|
10837
11291
|
tick: rest => workflow.tick(withScope(rest)),
|
|
10838
11292
|
evaluate: rest => workflow.evaluate(withScope(rest)),
|
|
10839
11293
|
diagnose: rest => workflow.diagnose(withScope(rest)),
|
|
@@ -11240,6 +11694,10 @@ const HISTORY_DISPLAY = {
|
|
|
11240
11694
|
title: "Number value",
|
|
11241
11695
|
description: "Numeric entry."
|
|
11242
11696
|
},
|
|
11697
|
+
progress: {
|
|
11698
|
+
title: "Progress",
|
|
11699
|
+
description: "Application-defined completion, 0–100 inclusive (fractions allowed)."
|
|
11700
|
+
},
|
|
11243
11701
|
boolean: {
|
|
11244
11702
|
title: "Boolean value",
|
|
11245
11703
|
description: "True/false entry."
|
|
@@ -11402,4 +11860,4 @@ function displayDescription(typeKey) {
|
|
|
11402
11860
|
if (typeKey) return DISPLAY[typeKey]?.description;
|
|
11403
11861
|
}
|
|
11404
11862
|
|
|
11405
|
-
export { ACTION_SEMANTICS, ACTIVITY_KINDS, ACTIVITY_KIND_DISPLAY, ACTOR_KINDS, AUTHORING_DISPLAY, ActionDisabledError, ActionParamsInvalidError, CONDITION_VARS, CONTEXT_ENTRY_DISPLAY, CascadeLimitError, ConcurrentCompleteEffectError, ConcurrentEditFieldError, ConcurrentFireActionError, ContractViolationError, DATA_MODEL_CHANGES, DATA_MODEL_MIN_READER, DATA_MODEL_VERSION, DEFAULT_CONTENT_PERSPECTIVE, DEFAULT_EFFECT_LEASE_MS, DEFAULT_IDEMPOTENCY_TTL_MS, DEFAULT_TRANSITION_WHEN, DISPLAY, DRIVER_KINDS, DRIVER_KIND_DISPLAY, DefinitionInUseError, DefinitionNotFoundError, ENGINE_API_VERSION, EXECUTION_KINDS, EXECUTOR_CLASSIFICATIONS, EXECUTOR_CLASSIFICATION_DISPLAY, EditFieldDeniedError, EffectNotFoundError, EffectOpsInvalidError, EffectOutputsInvalidError, FIELD_KIND_DISPLAY, FILTER_SCOPE_VARS, FieldValueShapeError, GROUP_KINDS, GROUP_KIND_DISPLAY, GUARD_DOC_TYPE, GUARD_OWNER, GUARD_PREDICATE_VARS, HISTORY_DISPLAY, InitialFieldsInvalidError, InstanceNotFoundError, MAX_COUNTERFACTUAL_INDEX2 as MAX_COUNTERFACTUAL_INDEX, MissingHandlerError, ModelVersionAheadError, MutationGuardDeniedError, MutationGuardDocSchema, OP_DISPLAY, PartialGuardDeployError, PersistedDocShapeError, READER_MODEL_ROLLOUT_URL, RESERVED_CONDITION_VARS, ReaderModelAcknowledgementError, RefResourceUndeclaredError, RequiredFieldNotProvidedError, START_ALLOWED_VARS, START_FILTER_VARS, SpawnContractsInvalidError, StartNotAllowedError, StartNotPrimedError, StartNotSettledError, WORKFLOW_DEFINITION_TYPE, WORKFLOW_INSTANCE_TYPE, WorkflowActionFired, WorkflowDefinitionDeleted, WorkflowDefinitionDeployed, WorkflowEffectCompleted, WorkflowEffectsDrained, WorkflowError, WorkflowFieldEdited, WorkflowInstanceAborted, WorkflowInstanceSchema, WorkflowInstanceStarted, WorkflowInstanceTicked, WorkflowStageSet, WorkflowStageTransitioned, WorkflowStateDivergedError, abortReason, acceptsDocumentType, aclPathForResource, actionDisabledDetail, actionRendering, actionVerdict, activityAutonomyOf, analyzeCondition2 as analyzeCondition, applicableDefinitions, assertReadableModel, assertReaderModelAcknowledgement, atomReadsDataset2 as atomReadsDataset, autonomySummary, availableActions, buildInitialFields, buildSnapshot, checklistLines, clientConfigFromResource, clientProjectUserDirectory, compileGuard, computeDiffEntries, conditionFieldReadNames, conditionSitesOf, contentDocQuery, contentDraftFallback, contentReleaseName, contextMap, createEngine, createTelemetryIntake, datasetResourceParts, defaultLoggerFactory, definitionDeployedData, definitionLookupGroq, definitionsListGroq, deniedGuardLabels, deniedGuardRefs, denyingGuards, deployStageGuards, deriveActivityKind, deriveExecutorClassification, deriveWorkflowAutonomy, describeAtom, describeAutonomyWait, describeCondition, describeDefinition, describeFieldInsight, describeNode, describeSite, describeSiteHeading, diagnoseInputFromEvaluation, diagnoseInstance, diffEntry, displayDescription, displayTitle, documentActionDenials, documentPrefilter, driverKind, effectOutputsMap, entryDocRefs, errorMessage, evaluateFromSnapshot, evaluateMutationGuard, evaluateStartFilter, expandResourceAliases, explainCondition2 as explainCondition, explainStartAllowed, extractDocumentId, fieldTreeShape, findCurrentActivityEntry, findOpenStageEntry, formatRead, gdrFromResource, gdrRef, gdrUri, groupMembershipNames, groupSitesOf, guardMatches, guardsForDefinition, guardsForInstance, guardsForResource, guillemets2 as guillemets, hashDefinitionContent, humanize2 as humanize, inFlightFilter, initialFieldIssues, instanceDocId, instanceGuardQuery, instanceWatchesDocument, instancesQuery, isCascadeFired, isClaimExpired, isComparisonOp, isDefinitionApplicable, isFilterScopedOut, isGdr, isInputSourced, isNotesEntry, isProjectUserNotFoundError, isSingleDocRefEntry, isSingleDocRefKind, isStartableDefinition, isSubjectEntry, isTelemetryEnvDenied, isTerminalActivityStatus, isTerminalStage, isTodoListEntry, isTodoListItem, isUnprimed, lakeGuardId, latestDeployedDefinitions, lintEffectOutputs, minReaderModelOf, missingRequiredInputs, modelVersionOf, narrateAutonomyWaits, noopTelemetry, parentRef, parseDefinitionInput, parseDefinitionSnapshot, parseGdr, parseGuardDocument, parseInstanceDocument, parseResourceGdr, processShellUserProperties, projectToWatchRef, quoted2 as quoted, readInstanceDoc, readsRaw, readsRootDocument, refCanvas, refDashboard, refDataset, refKindAcceptsTypes, refMediaLibrary, refsOf, rejectedRefTypes, releaseDocId, releaseRef, remediationsFor, requiredModelFeatures, requiredReaderModel, resolveAccess, resolveActor, resolveClientActor, resolveFieldEntry$1 as resolveFieldEntry, resourceAliasesToMap, resourceFromParsed, resourceGdr, retractStageGuards, sameResource, scalarValidationIssues, schemaTreeShape, sentenceCase, silentLogger, stageAutonomyOf, startFieldsParam, startKindOf, startRefusal, stripSystemFields, subjectDenialLabels, subscriptionDocument, subscriptionDocumentsForInstance, sweepStaleClaims, tagScopeFilter, terminalState, toBareId, tryParseGdr, unboundAllowedReads, unsatisfiedTransitionSummaries, validateDefinition, validateTag, verdictGuardsForInstance, wallClock, whatIfCondition2 as whatIfCondition, withAssignment, workflow };
|
|
11863
|
+
export { ACTION_SEMANTICS, ACTIVITY_KINDS, ACTIVITY_KIND_DISPLAY, ACTOR_KINDS, AUTHORING_DISPLAY, ActionDisabledError, ActionParamsInvalidError, CONDITION_VARS, CONTEXT_ENTRY_DISPLAY, CascadeLimitError, ConcurrentCommitEffectOpsError, ConcurrentCompleteEffectError, ConcurrentEditFieldError, ConcurrentFireActionError, ContractViolationError, DATA_MODEL_CHANGES, DATA_MODEL_MIN_READER, DATA_MODEL_VERSION, DEFAULT_CONTENT_PERSPECTIVE, DEFAULT_EFFECT_LEASE_MS, DEFAULT_IDEMPOTENCY_TTL_MS, DEFAULT_TRANSITION_WHEN, DISPLAY, DRIVER_KINDS, DRIVER_KIND_DISPLAY, DefinitionInUseError, DefinitionNotFoundError, EFFECT_COMMIT_DISPATCH_CAP, EFFECT_COMMIT_QUEUE_DEPTH, ENGINE_API_VERSION, EXECUTION_KINDS, EXECUTOR_CLASSIFICATIONS, EXECUTOR_CLASSIFICATION_DISPLAY, EditFieldDeniedError, EffectCommitQueueOverflowError, EffectNotFoundError, EffectOpsInvalidError, EffectOutputsInvalidError, FIELD_KIND_DISPLAY, FILTER_SCOPE_VARS, FieldValueShapeError, GROUP_KINDS, GROUP_KIND_DISPLAY, GUARD_DOC_TYPE, GUARD_OWNER, GUARD_PREDICATE_VARS, HISTORY_DISPLAY, InitialFieldsInvalidError, InstanceNotFoundError, MAX_COUNTERFACTUAL_INDEX2 as MAX_COUNTERFACTUAL_INDEX, MissingHandlerError, ModelVersionAheadError, MutationGuardDeniedError, MutationGuardDocSchema, OP_DISPLAY, PartialGuardDeployError, PersistedDocShapeError, READER_MODEL_ROLLOUT_URL, RESERVED_CONDITION_VARS, ReaderModelAcknowledgementError, RefResourceUndeclaredError, RequiredFieldNotProvidedError, START_ALLOWED_VARS, START_FILTER_VARS, SpawnContractsInvalidError, StaleEffectClaimError, StartNotAllowedError, StartNotPrimedError, StartNotSettledError, WORKFLOW_DEFINITION_TYPE, WORKFLOW_INSTANCE_TYPE, WorkflowActionFired, WorkflowDefinitionDeleted, WorkflowDefinitionDeployed, WorkflowEffectCompleted, WorkflowEffectStateReported, WorkflowEffectsDrained, WorkflowError, WorkflowFieldEdited, WorkflowInstanceAborted, WorkflowInstanceSchema, WorkflowInstanceStarted, WorkflowInstanceTicked, WorkflowStageSet, WorkflowStageTransitioned, WorkflowStateDivergedError, abortReason, acceptsDocumentType, aclPathForResource, actionDisabledDetail, actionRendering, actionVerdict, activityAutonomyOf, analyzeCondition2 as analyzeCondition, applicableDefinitions, assertReadableModel, assertReaderModelAcknowledgement, atomReadsDataset2 as atomReadsDataset, autonomySummary, availableActions, buildInitialFields, buildSnapshot, checklistLines, clientConfigFromResource, clientProjectUserDirectory, compileGuard, computeDiffEntries, conditionFieldReadNames, conditionSitesOf, contentDocQuery, contentDraftFallback, contentReleaseName, contextMap, createEngine, createTelemetryIntake, datasetResourceParts, defaultLoggerFactory, definitionDeployedData, definitionLookupGroq, definitionsListGroq, deniedGuardLabels, deniedGuardRefs, denyingGuards, deployStageGuards, deriveActivityKind, deriveExecutorClassification, deriveWorkflowAutonomy, describeAtom, describeAutonomyWait, describeCondition, describeDefinition, describeFieldInsight, describeNode, describeSite, describeSiteHeading, diagnoseInputFromEvaluation, diagnoseInstance, diffEntry, displayDescription, displayTitle, documentActionDenials, documentPrefilter, driverKind, effectOutputsMap, entryDocRefs, errorMessage, evaluateFromSnapshot, evaluateMutationGuard, evaluateStartFilter, expandResourceAliases, explainCondition2 as explainCondition, explainStartAllowed, extractDocumentId, fieldTreeShape, findCurrentActivityEntry, findOpenStageEntry, formatRead, gdrFromResource, gdrRef, gdrUri, groupMembershipNames, groupSitesOf, guardMatches, guardsForDefinition, guardsForInstance, guardsForResource, guillemets2 as guillemets, hashDefinitionContent, humanize2 as humanize, inFlightFilter, initialFieldIssues, instanceDocId, instanceGuardQuery, instanceWatchesDocument, instancesQuery, isCascadeFired, isClaimExpired, isComparisonOp, isDefinitionApplicable, isFilterScopedOut, isGdr, isInputSourced, isNotesEntry, isProjectUserNotFoundError, isSingleDocRefEntry, isSingleDocRefKind, isStartableDefinition, isSubjectEntry, isTelemetryEnvDenied, isTerminalActivityStatus, isTerminalStage, isTodoListEntry, isTodoListItem, isUnprimed, lakeGuardId, latestDeployedDefinitions, lintEffectOutputs, minReaderModelOf, missingRequiredInputs, modelVersionOf, narrateAutonomyWaits, noopTelemetry, parentRef, parseDefinitionInput, parseDefinitionSnapshot, parseGdr, parseGuardDocument, parseInstanceDocument, parseResourceGdr, processShellUserProperties, projectToWatchRef, quoted2 as quoted, readInstanceDoc, readsRaw, readsRootDocument, refCanvas, refDashboard, refDataset, refKindAcceptsTypes, refMediaLibrary, refsOf, rejectedRefTypes, releaseDocId, releaseRef, remediationsFor, requiredModelFeatures, requiredReaderModel, resolveAccess, resolveActor, resolveClientActor, resolveFieldEntry$1 as resolveFieldEntry, resourceAliasesToMap, resourceFromParsed, resourceGdr, retractStageGuards, sameResource, scalarValidationIssues, schemaTreeShape, sentenceCase, silentLogger, stageAutonomyOf, startFieldsParam, startKindOf, startRefusal, stripSystemFields, subjectDenialLabels, subscriptionDocument, subscriptionDocumentsForInstance, sweepStaleClaims, tagScopeFilter, terminalState, toBareId, tryParseGdr, unboundAllowedReads, unsatisfiedTransitionSummaries, validateDefinition, validateTag, verdictGuardsForInstance, wallClock, whatIfCondition2 as whatIfCondition, withAssignment, workflow };
|