@sanity/workflow-engine 0.30.0 → 0.32.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/dist/index.js CHANGED
@@ -1,45 +1,16 @@
1
- import { rethrowWithContext, andConditions, deriveActivityKind, CALLER_BOUND_VARS, START_REQUIREMENT_VARS, CONDITION_VARS, isSubjectEntry, isUnevaluable, ContractViolationError, isStartableDefinition, conditionFieldReadNames, isGdr, errorMessage, conditionParameterNames, START_FILTER_VARS, gdrFromResource, selfGdr, isSingleDocRefKind, actorFulfillsRole, toBareId, WorkflowError, sameResource, resourceFromParsed, tryParseGdr, isTerminalActivityStatus, FieldValueShapeError, validateFieldValue, validateFieldAppendItem, isAlwaysArrayFieldKind, evaluateCondition, isRecord, isSingleDocRefEntry, isAssignmentFieldEntry, choiceValueIssues, scalarValidationIssues, StoredFieldOpSchema, formatIssues, conditionSyntaxIssues, checkWorkflowInvariants, formatIssuePath, isGuardReadExpr, conditionEffectReads, EFFECTS_READ, datasetResourceParts, assignmentKindAcceptsRoles, evaluatePredicates, WORKFLOW_DEFINITION_TYPE, tagScopeFilter, isCascadeFired, classifyPrincipalId, directoryBridgeId, parseGdr, isGdrUri, gdrRef, isInputSourced, parseFieldValue, VersionSpecificDatasetGdrError, toPhysicalGdr, isBareSeedId, NonEmptyString, tolerantEntries, FIELD_VALUE_KINDS, tolerantObject, ActorShape, IsoTimestamp, ACTIVITY_STATUSES, GdrShape, DRIVER_KINDS, FIELD_SCOPES, fieldValueSchemas, parsePersistedDoc, ACTOR_KINDS, InstanceNotFoundError, evaluateConditionOutcome, runGroq, FIELD_READ, MUTATION_GUARD_ACTIONS, resourceGdr, resourceFromGdrUri, refTypeIssues, rejectedRefTypes, DOCUMENT_VALUE_PERMISSIONS, lakePrincipalId, firstCarriedGlobalId, driverKind, EffectNotFoundError, deriveExecutorClassification, validateTag, extractDocumentId, parseStoredDefinition, validateResourceAliasName, labelFor, DefinitionNotFoundError, definitionDocId, SpawnContractsInvalidError, gdrResourcePrefix, RESOURCE_ALIAS_NAME_SOURCE, DefinitionInUseError, isParseableInstant, groupMembershipNames } from "./_chunks-es/invariants.js";
1
+ import { rethrowWithContext, findOpenStageEntry, findCurrentActivityEntry, andConditions, deriveActivityKind, CALLER_BOUND_VARS, START_REQUIREMENT_VARS, CONDITION_VARS, isSubjectEntry, isUnevaluable, ContractViolationError, isStartableDefinition, conditionFieldReadNames, isGdr, errorMessage, conditionParameterNames, START_FILTER_VARS, WorkflowError, gdrFromResource, selfGdr, isGdrUri, MUTATION_GUARD_ID_SPACES, mutationGuardActionIdSpace, LAKE_MUTATION_GUARD_ACTIONS, isBareDocumentId, toBareId, normalizeAssignmentMembers, isSingleDocRefKind, actorMatchesAssignment, assignmentMembers, resolvedChildStatus, sameResource, resourceFromParsed, tryParseGdr, isTerminalActivityStatus, FieldValueShapeError, validateFieldValue, validateFieldAppendItem, isAssignmentFieldEntry, isAlwaysArrayFieldKind, evaluateCondition, isRecord, isSingleDocRefEntry, choiceValueIssues, scalarValidationIssues, StoredFieldOpSchema, formatIssues, conditionSyntaxIssues, checkWorkflowInvariants, formatIssuePath, isGuardReadExpr, GUARD_ACTIONS_REQUIRED_MESSAGE, conditionEffectReads, EFFECTS_READ, datasetResourceParts, mutationGuardRequiresSplitEmission, MUTATION_GUARD_ACTIONS, assignmentKindAcceptsRoles, evaluatePredicates, WORKFLOW_DEFINITION_TYPE, tagScopeFilter, liveSubworkflows, isCascadeFired, classifyPrincipalId, directoryBridgeId, parseGdr, gdrRef, isInputSourced, parseFieldValue, VersionSpecificDatasetGdrError, toPhysicalGdr, isBareSeedId, isParseableInstant, NonEmptyString, tolerantEntries, FIELD_VALUE_KINDS, tolerantObject, ActorShape, IsoTimestamp, ACTIVITY_STATUSES, GdrShape, DRIVER_KINDS, FIELD_SCOPES, fieldValueSchemas, parsePersistedDoc, ACTOR_KINDS, InstanceNotFoundError, evaluateConditionOutcome, runGroq as runGroq$1, FIELD_READ, resourceGdr, condemnSubworkflow, resourceFromGdrUri, refTypeIssues, rejectedRefTypes, DOCUMENT_VALUE_PERMISSIONS, lakePrincipalId, firstCarriedGlobalId, driverKind, actorFulfillsRole, condemnedSubworkflows, EffectNotFoundError, isFilterScopedOut, deriveExecutorClassification, validateTag, activeAssignmentMembers, extractDocumentId, UNIVERSAL_ROLE_ALIAS_KEY, parseStoredDefinition, validateResourceAliasName, labelFor, DefinitionNotFoundError, definitionDocId, SpawnContractsInvalidError, gdrResourcePrefix, RESOURCE_ALIAS_NAME_SOURCE, DefinitionInUseError, groupMembershipNames } from "./_chunks-es/invariants.js";
2
2
 
3
- import { ACTION_SEMANTICS, ACTIVITY_KINDS, ANONYMOUS_IDENTITY, DECISION_SEMANTICS, DEFAULT_TRANSITION_WHEN, EXECUTOR_CLASSIFICATIONS, FILTER_SCOPE_VARS, GROUP_KINDS, GUARD_PREDICATE_VARS, PersistedDocShapeError, RESERVED_CONDITION_VARS, SIGNAL_SEMANTICS, SYSTEM_IDENTITY, clientConfigFromResource, gdrUri, isNotesEntry, isTodoListEntry, isTodoListItem, parseResourceGdr, readsRootDocument, refCanvas, refDashboard, refDataset, refKindAcceptsTypes, refMediaLibrary, releaseDocId, releaseRef, resourceAliasesToMap, schemaTreeShape, startKindOf } from "./_chunks-es/invariants.js";
3
+ import { ACTION_SEMANTICS, ACTIVITY_KINDS, ANONYMOUS_IDENTITY, DECISION_SEMANTICS, DEFAULT_TRANSITION_WHEN, EXECUTOR_CLASSIFICATIONS, FILTER_SCOPE_VARS, GROUP_KINDS, GUARD_PREDICATE_VARS, PersistedDocShapeError, RESERVED_CONDITION_VARS, SIGNAL_SEMANTICS, SYSTEM_IDENTITY, assignmentMatch, assignmentState, assignmentStateCounts, clientConfigFromResource, gdrUri, identityMatchesAssignment, instanceAssignmentStateCounts, isNotesEntry, isTodoListEntry, isTodoListItem, openActivityAssignments, parseResourceGdr, readsRootDocument, refCanvas, refDashboard, refDataset, refKindAcceptsTypes, refMediaLibrary, releaseDocId, releaseRef, resourceAliasesToMap, schemaTreeShape, startKindOf } from "./_chunks-es/invariants.js";
4
4
 
5
- import { atomNode, dedupeBy, analyzeCondition, atomReadsDataset, checklistLines as checklistLines$1, phrase, quoted, listAnd, describeAtom as describeAtom$1, describeCondition as describeCondition$1, guillemets, formatValue, humanize, listOr, scopeReadOf, describeRead, explainCondition, dedupeReads, whatIfCondition, MAX_COUNTERFACTUAL_INDEX } from "@sanity/groq-condition-describe";
5
+ import { atomNode, dedupeBy, analyzeCondition, atomReadsDataset, checklistLines as checklistLines$1, phrase, quoted, listAnd, describeAtom as describeAtom$1, describeCondition as describeCondition$1, guillemets, formatValue, humanize, listOr, scopeReadOf, describeRead, explainCondition, _exhaustiveOptions, whatIfCondition, runGroq, formatRead, dedupeReads, MAX_COUNTERFACTUAL_INDEX } from "@sanity/groq-condition-describe";
6
6
 
7
- import { MAX_COUNTERFACTUAL_INDEX as MAX_COUNTERFACTUAL_INDEX2, NEUTRAL_MARK, OUTCOME_MARKS, analyzeCondition as analyzeCondition2, atomReadsDataset as atomReadsDataset2, explainCondition as explainCondition2, formatRead, guillemets as guillemets2, humanize as humanize2, isComparisonOp, quoted as quoted2, sentenceCase, whatIfCondition as whatIfCondition2, withAssignment } from "@sanity/groq-condition-describe";
7
+ import { MAX_COUNTERFACTUAL_INDEX as MAX_COUNTERFACTUAL_INDEX2, NEUTRAL_MARK, OUTCOME_MARKS, analyzeCondition as analyzeCondition2, atomReadsDataset as atomReadsDataset2, explainCondition as explainCondition2, formatRead as formatRead2, guillemets as guillemets2, humanize as humanize2, isComparisonOp, quoted as quoted2, sentenceCase, whatIfCondition as whatIfCondition2, withAssignment } from "@sanity/groq-condition-describe";
8
8
 
9
9
  import { evaluate, parse } from "groq-js";
10
10
 
11
- import * as v from "valibot";
12
-
13
- function isFilterScopedOut(entry) {
14
- return entry.status === "skipped" && entry.startedAt === void 0;
15
- }
16
-
17
- function liveSubworkflows(host) {
18
- return (host.subworkflows ?? []).filter(row => row.resolved === void 0);
19
- }
20
-
21
- function resolvedChildStatus(child) {
22
- if (!(child.completedAt === void 0 || child.completedAt === null)) return child.abortedAt !== void 0 && child.abortedAt !== null ? "aborted" : "done";
23
- }
24
-
25
- function condemnedSubworkflows(host) {
26
- return liveSubworkflows(host).filter(row => row.abortPending !== void 0);
27
- }
28
-
29
- function condemnSubworkflow(row, owed) {
30
- row.abortPending !== void 0 || row.resolved !== void 0 || (row.abortPending = {
31
- at: owed.at,
32
- reason: owed.reason
33
- });
34
- }
11
+ import { getPublishedId, getDraftId } from "@sanity/id-utils";
35
12
 
36
- function findOpenStageEntry(host) {
37
- return host.stages.find(s => s.name === host.currentStage && s.exitedAt === void 0);
38
- }
39
-
40
- function findCurrentActivityEntry(host, activityName) {
41
- return findOpenStageEntry(host)?.activities.find(a => a.name === activityName);
42
- }
13
+ import * as v from "valibot";
43
14
 
44
15
  const WORKFLOW_INSTANCE_TYPE = "sanity.workflow.instance";
45
16
 
@@ -458,6 +429,11 @@ function rolesGateOf(atom) {
458
429
  if (!(count.type !== "FuncCall" || count.name !== "count" || count.args.length !== 1)) return rolesFilterList(count.args[0]);
459
430
  }
460
431
 
432
+ function isAssignedGate(atom) {
433
+ const node = atomNode(atom);
434
+ return !atom.negated && node.type === "Parameter" && node.name === "assigned";
435
+ }
436
+
461
437
  function rolesFilterList(node) {
462
438
  if (node.type !== "Filter") return;
463
439
  const base = node.base;
@@ -1746,27 +1722,38 @@ function startContextSyntaxIssues({groq: groq, vars: vars, context: context, ski
1746
1722
  return [ ...conditionParameterNames(groq) ].filter(name => !bound.has(name) && name !== skip).map(name => `reads $${name}, which the ${context} context does not bind (an unbound variable is GROQ null, so the predicate silently misevaluates — failing closed or passing vacuously by shape). Bound here: ` + vars.map(v2 => `$${v2.name}`).join(", "));
1747
1723
  }
1748
1724
 
1749
- function getPath(value, path) {
1750
- let current = value;
1751
- for (const part of path.split(".")) {
1752
- if (current == null || typeof current != "object") return;
1753
- current = current[part];
1725
+ const GUARD_DOC_TYPE = "temp.system.guard";
1726
+
1727
+ class MutationGuardDeniedError extends WorkflowError {
1728
+ denied;
1729
+ documentId;
1730
+ action;
1731
+ constructor(args) {
1732
+ const ids = args.denied.map(d => d.guardId).join(", ");
1733
+ super("mutation-guard-denied", `Mutation on "${args.documentId}" (${args.action}) denied by guard(s) [${ids}]`),
1734
+ this.name = "MutationGuardDeniedError", this.denied = args.denied, this.documentId = args.documentId,
1735
+ this.action = args.action;
1736
+ }
1737
+ static fromGuards(args) {
1738
+ return new MutationGuardDeniedError({
1739
+ documentId: args.documentId,
1740
+ action: args.action,
1741
+ denied: deniedGuardRefs(args.guards)
1742
+ });
1754
1743
  }
1755
- return current;
1756
1744
  }
1757
1745
 
1758
- function isReferenceObject(value) {
1759
- return typeof value._ref == "string";
1746
+ function deniedGuardRefs(guards) {
1747
+ return guards.map(g => ({
1748
+ guardId: g._id,
1749
+ ...g.name !== void 0 ? {
1750
+ name: g.name
1751
+ } : {}
1752
+ }));
1760
1753
  }
1761
1754
 
1762
- function walkDocPath(value, path) {
1763
- let current = value;
1764
- for (const part of path.split(".")) {
1765
- if (current == null || typeof current != "object") return;
1766
- if (isReferenceObject(current) && !Object.hasOwn(current, part)) throw new Error(`cannot read "${part}" through the nested reference "${current._ref}" — the workflow only loads references it declares. Declare it as a doc.ref field entry to read through it.`);
1767
- current = current[part];
1768
- }
1769
- return current;
1755
+ function deniedGuardLabels(denied) {
1756
+ return denied.map(d => d.name ?? d.guardId);
1770
1757
  }
1771
1758
 
1772
1759
  function mapJsonStrings(value, transform) {
@@ -1835,199 +1822,658 @@ function overlayInstanceInSnapshot(snapshot, instance) {
1835
1822
  };
1836
1823
  }
1837
1824
 
1838
- function resolveFieldRead(args) {
1839
- const {kind: kind, value: value, path: path, snapshot: snapshot, targetKind: targetKind} = args;
1840
- if (isSingleDocRefKind(kind)) {
1841
- if (!isGdr(value)) return null;
1842
- if (path === void 0 && targetKind !== void 0 && isSingleDocRefKind(targetKind)) return value;
1843
- const base = derefBase(value, snapshot);
1844
- return path === void 0 ? base : walkDocPath(base, path);
1845
- }
1846
- return path !== void 0 ? getPath(value, path) : value;
1825
+ function lakeGuardId(args) {
1826
+ return `${GUARD_DOC_TYPE}.${args.instanceDocId}.${args.guardName}`;
1847
1827
  }
1848
1828
 
1849
- function derefBase(ref, snapshot) {
1850
- return (snapshot !== void 0 ? findSnapshotDoc(snapshot, ref.id) : void 0) ?? {
1851
- _id: ref.id,
1852
- _type: ref.type
1853
- };
1829
+ function lakeActionsFor(action) {
1830
+ return action === "publish" ? [ "create", "update" ] : action === "unpublish" ? [ "delete" ] : [ action ];
1854
1831
  }
1855
1832
 
1856
- function buildParams(args) {
1857
- const {instance: instance, now: now, snapshot: snapshot, extra: extra} = args, currentActivities2 = findOpenStageEntry(instance)?.activities ?? [];
1858
- return {
1859
- self: selfGdr(instance),
1860
- fields: renderedFields(instance.fields ?? [], snapshot),
1861
- parent: parentRef(instance)?.id ?? null,
1862
- ancestors: instance.ancestors.map(a => a.id),
1863
- stage: instance.currentStage,
1864
- now: now,
1865
- context: contextMap(instance),
1866
- effects: effectOutputsMap(instance),
1867
- effectStatus: effectStatusMap(instance),
1868
- activities: currentActivities2,
1869
- subworkflows: subworkflowVarRows(instance, snapshot),
1870
- ...activityGateParams(currentActivities2),
1871
- ...extra
1872
- };
1833
+ function lakeGuardActions(actions) {
1834
+ const requested = new Set(actions.flatMap(lakeActionsFor));
1835
+ return LAKE_MUTATION_GUARD_ACTIONS.filter(action => requested.has(action));
1873
1836
  }
1874
1837
 
1875
- function subworkflowVarRows(instance, snapshot) {
1876
- const openKey = findOpenStageEntry(instance)?._key;
1877
- return (instance.subworkflows ?? []).map(row => ({
1878
- _id: toBareId(row.ref.id),
1879
- rowKey: row.rowKey,
1880
- activity: row.activity,
1881
- action: row.action,
1882
- definition: row.definition,
1883
- current: row.stageEntry === openKey,
1884
- ...childState(row, snapshot),
1885
- spawnedAt: row.spawnedAt
1886
- }));
1838
+ function publishedId(id) {
1839
+ return getPublishedId(id);
1887
1840
  }
1888
1841
 
1889
- function childState(row, snapshot) {
1890
- if (row.resolved !== void 0) return {
1891
- stage: row.resolved.stage ?? null,
1892
- status: row.resolved.aborted === !0 ? "aborted" : "done"
1842
+ function draftId(id) {
1843
+ return getDraftId(id);
1844
+ }
1845
+
1846
+ function versionPatternTranslation(pattern) {
1847
+ const documentSeparator = pattern.indexOf(".", 9);
1848
+ if (documentSeparator === -1) return {
1849
+ issue: `id pattern "${pattern}" does not identify a versioned document`
1893
1850
  };
1894
- const doc = snapshot === void 0 ? void 0 : findSnapshotDoc(snapshot, row.ref.id);
1895
- return doc === void 0 ? {
1896
- stage: null,
1897
- status: "active"
1851
+ const documentPattern = pattern.slice(documentSeparator + 1);
1852
+ return /^\*+$/.test(documentPattern) ? {
1853
+ issue: `id pattern "${pattern}" cannot be translated from a release version to a draft or published document without broadening it to every document`
1898
1854
  } : {
1899
- stage: doc.currentStage ?? null,
1900
- status: resolvedChildStatus(doc) ?? "active"
1855
+ documentPattern: documentPattern
1901
1856
  };
1902
1857
  }
1903
1858
 
1904
- function activityGateParams(activities) {
1905
- return {
1906
- allActivitiesDone: activities.every(activity => activity.status === "done" || activity.status === "skipped"),
1907
- anyActivityFailed: activities.some(activity => activity.status === "failed")
1908
- };
1859
+ function publishedPattern(pattern) {
1860
+ if (pattern.startsWith("drafts.")) return pattern.slice(7);
1861
+ if (!pattern.startsWith("versions.")) return pattern;
1862
+ const translated = versionPatternTranslation(pattern);
1863
+ if (translated.issue !== void 0) throw new Error(translated.issue);
1864
+ return translated.documentPattern;
1909
1865
  }
1910
1866
 
1911
- function renderedFields(entries, snapshot) {
1912
- const out = {};
1913
- for (const entry of entries) out[entry.name] = renderedValue(entry, snapshot);
1914
- return out;
1867
+ function draftPattern(pattern) {
1868
+ return `drafts.${publishedPattern(pattern)}`;
1915
1869
  }
1916
1870
 
1917
- function renderedValue(entry, snapshot) {
1918
- return resolveFieldRead({
1919
- kind: entry._type,
1920
- value: entry.value,
1921
- path: void 0,
1922
- snapshot: snapshot
1923
- });
1871
+ function idsInSpace(args) {
1872
+ const {ids: ids, space: space, sourceInstanceId: sourceInstanceId} = args;
1873
+ return space === "authored" ? ids : space === "published" ? [ ...new Set(ids.map(publishedId)) ] : [ ...new Set(ids.map(id => publishedId(id) === sourceInstanceId ? publishedId(id) : draftId(id))) ];
1924
1874
  }
1925
1875
 
1926
- function scopedFieldOverlay({instance: instance, snapshot: snapshot, activityName: activityName}) {
1927
- const stageEntry = findOpenStageEntry(instance);
1928
- if (stageEntry === void 0) return {};
1929
- const stageFields = renderedFields(stageEntry.fields ?? [], snapshot), activity = activityName ? stageEntry.activities.find(t => t.name === activityName) : void 0;
1876
+ function patternsInSpace(patterns, space) {
1877
+ if (space === "authored") return patterns;
1878
+ const convert = space === "edit" ? draftPattern : publishedPattern;
1879
+ return [ ...new Set(patterns.map(convert)) ];
1880
+ }
1881
+
1882
+ function matchInSpace(args) {
1883
+ const {match: match, actions: actions, space: space, sourceInstanceId: sourceInstanceId} = args;
1930
1884
  return {
1931
- ...stageFields,
1932
- ...renderedFields(activity?.fields ?? [], snapshot)
1885
+ ...match,
1886
+ ...match.idRefs !== void 0 ? {
1887
+ idRefs: idsInSpace({
1888
+ ids: match.idRefs,
1889
+ space: space,
1890
+ sourceInstanceId: sourceInstanceId
1891
+ })
1892
+ } : {},
1893
+ ...match.idPatterns !== void 0 ? {
1894
+ idPatterns: patternsInSpace(match.idPatterns, space)
1895
+ } : {},
1896
+ actions: lakeGuardActions(actions)
1933
1897
  };
1934
1898
  }
1935
1899
 
1936
- function assignedFor({instance: instance, activityName: activityName, actor: actor, roleAliases: roleAliases}) {
1937
- if (actor === void 0) return !1;
1938
- const entry = findCurrentActivityEntry(instance, activityName)?.fields?.find(s => s._type === "assignees");
1939
- return entry === void 0 ? !1 : entry.value.some(a => a.type === "user" ? a.id === actor.id : actorFulfillsRole({
1940
- actorRoles: actor.roles,
1941
- required: a.role,
1942
- aliases: roleAliases
1900
+ function guardPartitions(id, actions) {
1901
+ return MUTATION_GUARD_ID_SPACES.flatMap(space => {
1902
+ const matchingActions = actions.filter(action => mutationGuardActionIdSpace(action) === space);
1903
+ return matchingActions.length > 0 ? [ {
1904
+ space: space,
1905
+ actions: matchingActions
1906
+ } ] : [];
1907
+ }).map((partition, index) => ({
1908
+ ...partition,
1909
+ id: index === 0 ? id : `${id}.${partition.space}`
1943
1910
  }));
1944
1911
  }
1945
1912
 
1946
- function effectStatusMap(instance) {
1947
- const entry = findOpenStageEntry(instance);
1948
- if (entry === void 0) return {};
1949
- const out = {};
1950
- for (const run of instance.effectHistory) run.stageEntryKey === entry._key && (out[run.name] = run.status);
1951
- return out;
1913
+ function compiledGuardIds(args) {
1914
+ return guardPartitions(args.id, args.actions).map(partition => partition.id);
1952
1915
  }
1953
1916
 
1954
- function contextMap(instance) {
1955
- const out = {};
1956
- for (const entry of instance.context) out[entry.name] = entry._type === "context.json" ? parseJsonContextEntry(entry) : entry.value;
1957
- return out;
1917
+ function lakeGuardDocument(args) {
1918
+ return {
1919
+ _id: args.id,
1920
+ _type: GUARD_DOC_TYPE,
1921
+ resourceType: args.resourceType,
1922
+ resourceId: args.resourceId,
1923
+ owner: args.owner,
1924
+ sourceInstanceId: args.sourceInstanceId,
1925
+ sourceDefinition: args.sourceDefinition,
1926
+ sourceStage: args.sourceStage,
1927
+ ...args.name !== void 0 ? {
1928
+ name: args.name
1929
+ } : {},
1930
+ ...args.description !== void 0 ? {
1931
+ description: args.description
1932
+ } : {},
1933
+ match: args.match,
1934
+ predicate: args.predicate,
1935
+ metadata: args.metadata
1936
+ };
1958
1937
  }
1959
1938
 
1960
- function effectOutputsMap(instance) {
1961
- const out = {};
1962
- for (const run of instance.effectHistory) run.outputs !== void 0 && (out[run.name] = run.outputs);
1963
- return out;
1939
+ function compileGuards(args) {
1940
+ const [first, ...remaining] = guardPartitions(args.id, args.match.actions);
1941
+ if (first === void 0) throw new Error("cannot compile a guard without an action");
1942
+ const compilePartition = ({space: space, actions: actions, id: id}) => lakeGuardDocument({
1943
+ ...args,
1944
+ id: id,
1945
+ match: matchInSpace({
1946
+ match: args.match,
1947
+ actions: actions,
1948
+ space: space,
1949
+ sourceInstanceId: args.sourceInstanceId
1950
+ })
1951
+ });
1952
+ return [ compilePartition(first), ...remaining.map(compilePartition) ];
1964
1953
  }
1965
1954
 
1966
- function parseJsonContextEntry(entry) {
1967
- try {
1968
- return JSON.parse(entry.value);
1969
- } catch (err) {
1970
- rethrowWithContext(err, `context entry "${entry.name}" holds unparseable JSON`);
1971
- }
1955
+ function globMatch(pattern, value) {
1956
+ if (!pattern.includes("*")) return pattern === value;
1957
+ const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
1958
+ return new RegExp(`^${escaped}$`).test(value);
1972
1959
  }
1973
1960
 
1974
- function paramsForLake(params) {
1975
- return {
1976
- ...params,
1977
- self: typeof params.self == "string" ? toBareId(params.self) : params.self,
1978
- parent: typeof params.parent == "string" ? toBareId(params.parent) : params.parent,
1979
- ancestors: Array.isArray(params.ancestors) ? params.ancestors.map(a => typeof a == "string" ? toBareId(a) : a) : params.ancestors,
1980
- fields: stripFieldsForLake(params.fields)
1981
- };
1961
+ function guardIdPatternIssue(pattern, actions) {
1962
+ const representativeId = pattern.replaceAll("*", "x");
1963
+ if (!isBareDocumentId(representativeId)) return `id pattern "${pattern}" is not a valid resource-local document-id glob; use document-id characters (letters, digits, \`_\`, \`-\`, \`.\`) and \`*\` wildcards`;
1964
+ if (actions?.some(action => mutationGuardActionIdSpace(action) !== "authored") && pattern.startsWith("versions.")) return versionPatternTranslation(pattern).issue;
1982
1965
  }
1983
1966
 
1984
- function stripFieldsForLake(value) {
1985
- return mapJsonStrings(value, s => toBareId(s));
1967
+ function parseGuardPredicate(predicate) {
1968
+ return parse(predicate, {
1969
+ mode: "delta"
1970
+ });
1986
1971
  }
1987
1972
 
1988
- function resolveStaticValueExpr(src, ctx) {
1989
- switch (src.type) {
1990
- case "literal":
1991
- return src.value;
1992
-
1993
- case "param":
1994
- return ctx.params?.[src.param];
1973
+ function guardPredicateSyntaxIssue(predicate) {
1974
+ try {
1975
+ parseGuardPredicate(predicate);
1976
+ return;
1977
+ } catch (error) {
1978
+ return errorMessage(error);
1979
+ }
1980
+ }
1995
1981
 
1996
- case "actor":
1997
- return ctx.actor;
1982
+ function lakeDocumentIdForAction(id, action) {
1983
+ return mutationGuardActionIdSpace(action) === "published" ? publishedId(id) : id;
1984
+ }
1998
1985
 
1999
- case "now":
2000
- return ctx.now;
1986
+ function legacyLifecycleActionFor(action, documentId) {
1987
+ if (publishedId(documentId) === documentId) {
1988
+ if (action === "create" || action === "update") return "publish";
1989
+ if (action === "delete") return "unpublish";
2001
1990
  }
2002
1991
  }
2003
1992
 
2004
- function randomKey(length = 12) {
2005
- const bytes = new Uint8Array(length);
2006
- return globalThis.crypto.getRandomValues(bytes), [ ...bytes ].map(b => b.toString(16).padStart(2, "0")).join("").slice(0, length);
1993
+ function guardMatches({guard: guard, doc: doc, action: action}) {
1994
+ const m = guard.match, documentId = lakeDocumentIdForAction(doc.id, action), legacyLifecycleAction = legacyLifecycleActionFor(action, documentId);
1995
+ if (![ action, ...lakeActionsFor(action), legacyLifecycleAction ].some(candidate => candidate !== void 0 && m.actions.includes(candidate)) || m.types && m.types.length > 0 && !(doc.type !== void 0 && m.types.includes(doc.type))) return !1;
1996
+ if (m.idRefs && m.idRefs.length > 0 || m.idPatterns && m.idPatterns.length > 0) {
1997
+ const byRef = m.idRefs?.includes(documentId) ?? !1, byPattern = m.idPatterns?.some(p => globMatch(p, documentId)) ?? !1;
1998
+ if (!byRef && !byPattern) return !1;
1999
+ }
2000
+ return !0;
2007
2001
  }
2008
2002
 
2009
- function instanceDocId(tag) {
2010
- return `${tag}.wf-instance.${randomKey()}`;
2003
+ function guardPredicateRoot(args) {
2004
+ return {
2005
+ document: {
2006
+ before: args.context.before,
2007
+ after: args.context.after
2008
+ },
2009
+ guard: args.guard,
2010
+ mutation: {
2011
+ action: args.context.action
2012
+ }
2013
+ };
2011
2014
  }
2012
2015
 
2013
- function mapFieldRefValues(args) {
2014
- const {entryType: entryType, value: value, fields: fields, of: of, mapRef: mapRef} = args;
2015
- return value == null ? value : isSingleDocRefKind(entryType) || entryType === "release.ref" ? mapRef(value) : entryType === "doc.refs" ? mapItems(value, mapRef) : entryType === "object" ? mapRowRefValues({
2016
- row: value,
2017
- shapes: fields ?? [],
2018
- mapRef: mapRef
2019
- }) : entryType === "array" ? mapItems(value, row => mapRowRefValues({
2020
- row: row,
2021
- shapes: of ?? [],
2022
- mapRef: mapRef
2023
- })) : value;
2016
+ function lakeShapedSnapshotDocument(doc) {
2017
+ return mapJsonStrings(doc, (value, key) => key === "_id" || key === "_ref" ? toBareId(value) : value);
2024
2018
  }
2025
2019
 
2026
- function mapItems(value, mapItem) {
2027
- return Array.isArray(value) ? value.map(mapItem) : value;
2020
+ function snapshotGuardDereference(args) {
2021
+ return ({_ref: _ref}) => {
2022
+ const lookupId = isGdrUri(_ref) ? _ref : gdrFromResource(args.resource, _ref), found = findSnapshotDoc(args.snapshot, lookupId);
2023
+ return Promise.resolve(found === void 0 ? null : lakeShapedSnapshotDocument(found));
2024
+ };
2028
2025
  }
2029
2026
 
2030
- function mapRowRefValues(args) {
2027
+ async function evaluateMutationGuard(args) {
2028
+ const {guard: guard, context: context, dereference: dereference} = args;
2029
+ if (guard.predicate === "") return !1;
2030
+ const root = guardPredicateRoot({
2031
+ guard: guard,
2032
+ context: context
2033
+ });
2034
+ let dereferenceFailed = !1;
2035
+ const guardedDereference = dereference === void 0 ? void 0 : async ref => {
2036
+ try {
2037
+ return await dereference(ref);
2038
+ } catch (error) {
2039
+ throw dereferenceFailed = !0, error;
2040
+ }
2041
+ };
2042
+ try {
2043
+ const tree = parseGuardPredicate(guard.predicate);
2044
+ return await (await evaluate(tree, {
2045
+ root: root,
2046
+ before: context.before,
2047
+ after: context.after,
2048
+ ...guardedDereference !== void 0 ? {
2049
+ dataset: [],
2050
+ dereference: guardedDereference
2051
+ } : {},
2052
+ ...context.identity !== void 0 ? {
2053
+ identity: context.identity
2054
+ } : {}
2055
+ })).get() === !0;
2056
+ } catch (error) {
2057
+ if (dereferenceFailed) throw error;
2058
+ return !1;
2059
+ }
2060
+ }
2061
+
2062
+ async function denyingGuards(args) {
2063
+ const {guards: guards, doc: doc, context: context, dereference: dereference} = args, denied = [];
2064
+ for (const guard of guards) guardMatches({
2065
+ guard: guard,
2066
+ doc: doc,
2067
+ action: context.action
2068
+ }) && (await evaluateMutationGuard({
2069
+ guard: guard,
2070
+ context: context,
2071
+ ...dereference !== void 0 ? {
2072
+ dereference: dereference
2073
+ } : {}
2074
+ }) || denied.push(guard));
2075
+ return denied;
2076
+ }
2077
+
2078
+ async function documentActionDenials(args) {
2079
+ const {mutation: mutation, resource: resource, guards: guards, identity: identity, dereference: dereference} = args, document = mutation.after ?? mutation.before;
2080
+ if (document === null) throw new Error("a concrete Lake mutation requires a before or after image");
2081
+ const enforceable = guards.filter(g => g.resourceType === resource.type && g.resourceId === resource.id);
2082
+ return enforceable.length === 0 ? [] : denyingGuards({
2083
+ guards: enforceable,
2084
+ doc: {
2085
+ id: document._id,
2086
+ type: document._type
2087
+ },
2088
+ context: {
2089
+ ...mutation,
2090
+ ...identity !== void 0 ? {
2091
+ identity: identity
2092
+ } : {}
2093
+ },
2094
+ ...dereference !== void 0 ? {
2095
+ dereference: dereference
2096
+ } : {}
2097
+ });
2098
+ }
2099
+
2100
+ async function instanceWriteDenials(args) {
2101
+ const {instance: instance, guards: guards, identity: identity, dereference: dereference} = args;
2102
+ return documentActionDenials({
2103
+ mutation: {
2104
+ before: instance,
2105
+ after: instance,
2106
+ action: "update"
2107
+ },
2108
+ resource: instance.workflowResource,
2109
+ guards: guards,
2110
+ dereference: dereference,
2111
+ ...identity !== void 0 ? {
2112
+ identity: identity
2113
+ } : {}
2114
+ });
2115
+ }
2116
+
2117
+ async function assertInstanceWriteAllowed(args) {
2118
+ const denied = await instanceWriteDenials(args);
2119
+ if (denied.length !== 0) throw MutationGuardDeniedError.fromGuards({
2120
+ documentId: args.instance._id,
2121
+ action: "update",
2122
+ guards: denied
2123
+ });
2124
+ }
2125
+
2126
+ const ASSIGNMENT_LIST_MODEL = 9;
2127
+
2128
+ function isAssignmentListModel(modelVersion) {
2129
+ return modelVersion >= ASSIGNMENT_LIST_MODEL;
2130
+ }
2131
+
2132
+ function definitionAssignmentModel(instance) {
2133
+ if (typeof instance.definitionSnapshot != "string") return 0;
2134
+ const modelVersion = parseDefinitionSnapshotValue({
2135
+ _id: instance._id,
2136
+ definitionSnapshot: instance.definitionSnapshot
2137
+ }).modelVersion;
2138
+ return typeof modelVersion == "number" ? modelVersion : 0;
2139
+ }
2140
+
2141
+ function writesSingularAssignmentLists(instance) {
2142
+ return isAssignmentListModel(definitionAssignmentModel(instance));
2143
+ }
2144
+
2145
+ function legacyAssigneeValue(entry) {
2146
+ return legacyAssignmentValue(entry.value, entry.name);
2147
+ }
2148
+
2149
+ function legacyAssignmentValue(value, path) {
2150
+ if (!Array.isArray(value)) return value;
2151
+ if (value.length === 0) return null;
2152
+ if (value.length === 1) return value[0];
2153
+ throw new ContractViolationError(`legacy assignee field "${path}" cannot persist ${value.length} members; redeploy the definition at the assignment-list reader model before writing a role pool`);
2154
+ }
2155
+
2156
+ function mapShapeValues(args) {
2157
+ const {value: value, shapes: shapes, mapAssignee: mapAssignee, parentPath: parentPath} = args;
2158
+ if (typeof value != "object" || value === null || Array.isArray(value)) return value;
2159
+ const source = value, next = {
2160
+ ...source
2161
+ };
2162
+ for (const shape of shapes) {
2163
+ if (!Object.hasOwn(source, shape.name)) continue;
2164
+ const path = `${parentPath}.${shape.name}`, member = source[shape.name], mapped = mappedShapeValue({
2165
+ shape: shape,
2166
+ member: member,
2167
+ mapAssignee: mapAssignee,
2168
+ path: path
2169
+ });
2170
+ mapped.found && defineOwnValue(next, {
2171
+ name: shape.name,
2172
+ value: mapped.value
2173
+ });
2174
+ }
2175
+ return next;
2176
+ }
2177
+
2178
+ function mappedShapeValue(args) {
2179
+ const {shape: shape, member: member, mapAssignee: mapAssignee, path: path} = args;
2180
+ return shape.type === "assignee" ? {
2181
+ found: !0,
2182
+ value: mapAssignee(member, path)
2183
+ } : shape.type === "object" ? {
2184
+ found: !0,
2185
+ value: mapShapeValues({
2186
+ value: member,
2187
+ shapes: shape.fields ?? [],
2188
+ mapAssignee: mapAssignee,
2189
+ parentPath: path
2190
+ })
2191
+ } : shape.type !== "array" || !Array.isArray(member) ? {
2192
+ found: !1
2193
+ } : {
2194
+ found: !0,
2195
+ value: member.map((row, index) => mapShapeValues({
2196
+ value: row,
2197
+ shapes: shape.of ?? [],
2198
+ mapAssignee: mapAssignee,
2199
+ parentPath: `${path}[${index}]`
2200
+ }))
2201
+ };
2202
+ }
2203
+
2204
+ function defineOwnValue(target, property) {
2205
+ Object.defineProperty(target, property.name, {
2206
+ configurable: !0,
2207
+ enumerable: !0,
2208
+ value: property.value,
2209
+ writable: !0
2210
+ });
2211
+ }
2212
+
2213
+ function mapNestedAssignmentValues(entry, mapAssignee) {
2214
+ return entry._type === "object" ? mapShapeValues({
2215
+ value: entry.value,
2216
+ shapes: entry.fields,
2217
+ mapAssignee: mapAssignee,
2218
+ parentPath: entry.name
2219
+ }) : entry.value.map((row, index) => mapShapeValues({
2220
+ value: row,
2221
+ shapes: entry.of,
2222
+ mapAssignee: mapAssignee,
2223
+ parentPath: `${entry.name}[${index}]`
2224
+ }));
2225
+ }
2226
+
2227
+ function normalizeNestedAssignmentValues(entry) {
2228
+ return entry._type !== "object" && entry._type !== "array" ? entry : {
2229
+ ...entry,
2230
+ value: mapNestedAssignmentValues(entry, normalizeAssignmentMembers)
2231
+ };
2232
+ }
2233
+
2234
+ function assignmentFieldsForWrite(fields, listModel) {
2235
+ return listModel ? fields : fields.map(entry => entry._type === "assignee" ? {
2236
+ ...entry,
2237
+ value: legacyAssigneeValue(entry)
2238
+ } : entry._type === "object" || entry._type === "array" ? {
2239
+ ...entry,
2240
+ value: mapNestedAssignmentValues(entry, legacyAssignmentValue)
2241
+ } : entry);
2242
+ }
2243
+
2244
+ function activityForWrite(activity, listModel) {
2245
+ return {
2246
+ ...activity,
2247
+ ...activity.fields === void 0 ? {} : {
2248
+ fields: assignmentFieldsForWrite(activity.fields, listModel)
2249
+ }
2250
+ };
2251
+ }
2252
+
2253
+ function assignmentStagesForWrite(stages, listModel) {
2254
+ return stages.map(stage => ({
2255
+ ...stage,
2256
+ fields: assignmentFieldsForWrite(stage.fields ?? [], listModel),
2257
+ activities: stage.activities.map(activity => activityForWrite(activity, listModel))
2258
+ }));
2259
+ }
2260
+
2261
+ function getPath(value, path) {
2262
+ let current = value;
2263
+ for (const part of path.split(".")) {
2264
+ if (current == null || typeof current != "object") return;
2265
+ current = current[part];
2266
+ }
2267
+ return current;
2268
+ }
2269
+
2270
+ function isReferenceObject(value) {
2271
+ return typeof value._ref == "string";
2272
+ }
2273
+
2274
+ function walkDocPath(value, path) {
2275
+ let current = value;
2276
+ for (const part of path.split(".")) {
2277
+ if (current == null || typeof current != "object") return;
2278
+ if (isReferenceObject(current) && !Object.hasOwn(current, part)) throw new Error(`cannot read "${part}" through the nested reference "${current._ref}" — the workflow only loads references it declares. Declare it as a doc.ref field entry to read through it.`);
2279
+ current = current[part];
2280
+ }
2281
+ return current;
2282
+ }
2283
+
2284
+ function resolveFieldRead(args) {
2285
+ const {kind: kind, value: value, path: path, snapshot: snapshot, targetKind: targetKind} = args;
2286
+ if (isSingleDocRefKind(kind)) {
2287
+ if (!isGdr(value)) return null;
2288
+ if (path === void 0 && targetKind !== void 0 && isSingleDocRefKind(targetKind)) return value;
2289
+ const base = derefBase(value, snapshot);
2290
+ return path === void 0 ? base : walkDocPath(base, path);
2291
+ }
2292
+ return path !== void 0 ? getPath(value, path) : value;
2293
+ }
2294
+
2295
+ function derefBase(ref, snapshot) {
2296
+ return (snapshot !== void 0 ? findSnapshotDoc(snapshot, ref.id) : void 0) ?? {
2297
+ _id: ref.id,
2298
+ _type: ref.type
2299
+ };
2300
+ }
2301
+
2302
+ function buildParams(args) {
2303
+ const {instance: instance, now: now, snapshot: snapshot, extra: extra} = args, currentActivities2 = findOpenStageEntry(instance)?.activities ?? [];
2304
+ return {
2305
+ self: selfGdr(instance),
2306
+ fields: renderedFields(instance.fields ?? [], snapshot),
2307
+ parent: parentRef(instance)?.id ?? null,
2308
+ ancestors: instance.ancestors.map(a => a.id),
2309
+ stage: instance.currentStage,
2310
+ now: now,
2311
+ context: contextMap(instance),
2312
+ effects: effectOutputsMap(instance),
2313
+ effectStatus: effectStatusMap(instance),
2314
+ activities: currentActivities2,
2315
+ subworkflows: subworkflowVarRows(instance, snapshot),
2316
+ ...activityGateParams(currentActivities2),
2317
+ ...extra
2318
+ };
2319
+ }
2320
+
2321
+ function subworkflowVarRows(instance, snapshot) {
2322
+ const openKey = findOpenStageEntry(instance)?._key;
2323
+ return (instance.subworkflows ?? []).map(row => ({
2324
+ _id: toBareId(row.ref.id),
2325
+ rowKey: row.rowKey,
2326
+ activity: row.activity,
2327
+ action: row.action,
2328
+ definition: row.definition,
2329
+ current: row.stageEntry === openKey,
2330
+ ...childState(row, snapshot),
2331
+ spawnedAt: row.spawnedAt
2332
+ }));
2333
+ }
2334
+
2335
+ function childState(row, snapshot) {
2336
+ if (row.resolved !== void 0) return {
2337
+ stage: row.resolved.stage ?? null,
2338
+ status: row.resolved.aborted === !0 ? "aborted" : "done"
2339
+ };
2340
+ const doc = snapshot === void 0 ? void 0 : findSnapshotDoc(snapshot, row.ref.id);
2341
+ return doc === void 0 ? {
2342
+ stage: null,
2343
+ status: "active"
2344
+ } : {
2345
+ stage: doc.currentStage ?? null,
2346
+ status: resolvedChildStatus(doc) ?? "active"
2347
+ };
2348
+ }
2349
+
2350
+ function activityGateParams(activities) {
2351
+ return {
2352
+ allActivitiesDone: activities.every(activity => activity.status === "done" || activity.status === "skipped"),
2353
+ anyActivityFailed: activities.some(activity => activity.status === "failed")
2354
+ };
2355
+ }
2356
+
2357
+ function renderedFields(entries, snapshot) {
2358
+ const out = {};
2359
+ for (const entry of entries) out[entry.name] = renderedValue(entry, snapshot);
2360
+ return out;
2361
+ }
2362
+
2363
+ function renderedValue(entry, snapshot) {
2364
+ return resolveFieldRead({
2365
+ kind: entry._type,
2366
+ value: entry.value,
2367
+ path: void 0,
2368
+ snapshot: snapshot
2369
+ });
2370
+ }
2371
+
2372
+ function scopedFieldOverlay({instance: instance, snapshot: snapshot, activityName: activityName}) {
2373
+ const stageEntry = findOpenStageEntry(instance);
2374
+ if (stageEntry === void 0) return {};
2375
+ const stageFields = renderedFields(stageEntry.fields ?? [], snapshot), activity = activityName ? stageEntry.activities.find(t => t.name === activityName) : void 0;
2376
+ return {
2377
+ ...stageFields,
2378
+ ...renderedFields(activity?.fields ?? [], snapshot)
2379
+ };
2380
+ }
2381
+
2382
+ function assignedFor({instance: instance, activityName: activityName, actor: actor, roleAliases: roleAliases}) {
2383
+ if (actor === void 0) return !1;
2384
+ const activity = findCurrentActivityEntry(instance, activityName);
2385
+ return actorMatchesAssignment({
2386
+ actor: actor,
2387
+ members: assignmentMembers(activity?.fields ?? []),
2388
+ roleAliases: roleAliases
2389
+ });
2390
+ }
2391
+
2392
+ function effectStatusMap(instance) {
2393
+ const entry = findOpenStageEntry(instance);
2394
+ if (entry === void 0) return {};
2395
+ const out = {};
2396
+ for (const run of instance.effectHistory) run.stageEntryKey === entry._key && (out[run.name] = run.status);
2397
+ return out;
2398
+ }
2399
+
2400
+ function contextMap(instance) {
2401
+ const out = {};
2402
+ for (const entry of instance.context) out[entry.name] = entry._type === "context.json" ? parseJsonContextEntry(entry) : entry.value;
2403
+ return out;
2404
+ }
2405
+
2406
+ function effectOutputsMap(instance) {
2407
+ const out = {};
2408
+ for (const run of instance.effectHistory) run.outputs !== void 0 && (out[run.name] = run.outputs);
2409
+ return out;
2410
+ }
2411
+
2412
+ function parseJsonContextEntry(entry) {
2413
+ try {
2414
+ return JSON.parse(entry.value);
2415
+ } catch (err) {
2416
+ rethrowWithContext(err, `context entry "${entry.name}" holds unparseable JSON`);
2417
+ }
2418
+ }
2419
+
2420
+ function paramsForLake(params) {
2421
+ return {
2422
+ ...params,
2423
+ self: typeof params.self == "string" ? toBareId(params.self) : params.self,
2424
+ parent: typeof params.parent == "string" ? toBareId(params.parent) : params.parent,
2425
+ ancestors: Array.isArray(params.ancestors) ? params.ancestors.map(a => typeof a == "string" ? toBareId(a) : a) : params.ancestors,
2426
+ fields: stripFieldsForLake(params.fields)
2427
+ };
2428
+ }
2429
+
2430
+ function stripFieldsForLake(value) {
2431
+ return mapJsonStrings(value, s => toBareId(s));
2432
+ }
2433
+
2434
+ function resolveStaticValueExpr(src, ctx) {
2435
+ switch (src.type) {
2436
+ case "literal":
2437
+ return src.value;
2438
+
2439
+ case "param":
2440
+ return ctx.params?.[src.param];
2441
+
2442
+ case "actor":
2443
+ return ctx.actor;
2444
+
2445
+ case "now":
2446
+ return ctx.now;
2447
+ }
2448
+ }
2449
+
2450
+ function randomKey(length = 12) {
2451
+ const bytes = new Uint8Array(length);
2452
+ return globalThis.crypto.getRandomValues(bytes), [ ...bytes ].map(b => b.toString(16).padStart(2, "0")).join("").slice(0, length);
2453
+ }
2454
+
2455
+ function instanceDocId(tag) {
2456
+ return `${tag}.wf-instance.${randomKey()}`;
2457
+ }
2458
+
2459
+ function mapFieldRefValues(args) {
2460
+ const {entryType: entryType, value: value, fields: fields, of: of, mapRef: mapRef} = args;
2461
+ return value == null ? value : isSingleDocRefKind(entryType) || entryType === "release.ref" ? mapRef(value) : entryType === "doc.refs" ? mapItems(value, mapRef) : entryType === "object" ? mapRowRefValues({
2462
+ row: value,
2463
+ shapes: fields ?? [],
2464
+ mapRef: mapRef
2465
+ }) : entryType === "array" ? mapItems(value, row => mapRowRefValues({
2466
+ row: row,
2467
+ shapes: of ?? [],
2468
+ mapRef: mapRef
2469
+ })) : value;
2470
+ }
2471
+
2472
+ function mapItems(value, mapItem) {
2473
+ return Array.isArray(value) ? value.map(mapItem) : value;
2474
+ }
2475
+
2476
+ function mapRowRefValues(args) {
2031
2477
  const {row: row, shapes: shapes, mapRef: mapRef} = args;
2032
2478
  if (typeof row != "object" || row === null || Array.isArray(row)) return row;
2033
2479
  const record = row, out = {
@@ -2254,10 +2700,12 @@ class WorkflowStateDivergedError extends WorkflowError {
2254
2700
  class PartialGuardDeployError extends WorkflowError {
2255
2701
  stageName;
2256
2702
  deployed;
2703
+ rollbackError;
2257
2704
  constructor(args) {
2258
2705
  super("partial-guard-deploy", `Partial guard deploy on stage "${args.stageName}": ${args.deployed} guard(s) deployed before a later one failed.`, {
2259
2706
  cause: args.cause
2260
- }), this.name = "PartialGuardDeployError", this.stageName = args.stageName, this.deployed = args.deployed;
2707
+ }), this.name = "PartialGuardDeployError", this.stageName = args.stageName, this.deployed = args.deployed,
2708
+ args.rollbackError !== void 0 && (this.rollbackError = args.rollbackError);
2261
2709
  }
2262
2710
  }
2263
2711
 
@@ -2298,7 +2746,23 @@ function staleClaimDetail(reason) {
2298
2746
  const CONCURRENT_COMMIT_MAX_ATTEMPTS = 3;
2299
2747
 
2300
2748
  function lostRaceMessage(args) {
2301
- return `${args.what} (instance ${args.instanceId}) lost the optimistic-locking race ${args.attempts} attempts running — a concurrent writer kept committing first. Re-read and retry, or investigate a write storm on this instance.`;
2749
+ return `${args.what} (instance ${args.instanceId}) lost the optimistic-locking race ${args.attempts} attempts running — a concurrent writer kept committing first. ${args.recovery ?? "Re-read and retry, or investigate a write storm on this instance."}`;
2750
+ }
2751
+
2752
+ function isRetryableInstanceCommitConflict(error) {
2753
+ return !isCreateIdCollision(error) && isRevisionConflict(error);
2754
+ }
2755
+
2756
+ class InstanceCommitRevisionConflict extends Error {
2757
+ constructor(cause) {
2758
+ super("Instance commit lost its revision fence", {
2759
+ cause: cause
2760
+ }), this.name = "InstanceCommitRevisionConflict";
2761
+ }
2762
+ }
2763
+
2764
+ function isInstanceCommitRevisionConflict(error) {
2765
+ return error instanceof InstanceCommitRevisionConflict;
2302
2766
  }
2303
2767
 
2304
2768
  class ConcurrentFireActionError extends WorkflowError {
@@ -2316,6 +2780,19 @@ class ConcurrentFireActionError extends WorkflowError {
2316
2780
  }
2317
2781
  }
2318
2782
 
2783
+ class ConcurrentCascadeError extends WorkflowError {
2784
+ instanceId;
2785
+ attempts;
2786
+ constructor(args) {
2787
+ super("concurrent-cascade", lostRaceMessage({
2788
+ what: "Automatic cascade",
2789
+ instanceId: args.instanceId,
2790
+ attempts: args.attempts,
2791
+ recovery: "An earlier verb commit may already have landed. Re-read the instance and invoke tick after contention subsides."
2792
+ })), this.name = "ConcurrentCascadeError", this.instanceId = args.instanceId, this.attempts = args.attempts;
2793
+ }
2794
+ }
2795
+
2319
2796
  function isRevisionConflict(error) {
2320
2797
  if (typeof error != "object" || error === null) return !1;
2321
2798
  const {statusCode: statusCode, message: message} = error;
@@ -2598,11 +3075,15 @@ function applyFieldSet(op, ctx) {
2598
3075
  ...entryShape(entry),
2599
3076
  memberRoles: ctx.memberRoles,
2600
3077
  roleAliases: ctx.roleAliases,
2601
- ...entry._type === "assignees" || entry._type === "object" || entry._type === "array" ? {
3078
+ ...entry._type === "assignee" || entry._type === "assignees" || entry._type === "object" || entry._type === "array" ? {
2602
3079
  previousValue: entry.value
2603
3080
  } : {}
2604
3081
  });
2605
- return assertRuntimeRefsWithinSurface({
3082
+ return assertLegacySingularValue({
3083
+ entry: entry,
3084
+ value: validated,
3085
+ ctx: ctx
3086
+ }), assertRuntimeRefsWithinSurface({
2606
3087
  entryType: entry._type,
2607
3088
  entryName: entry.name,
2608
3089
  value: validated,
@@ -2619,12 +3100,12 @@ function applyFieldSet(op, ctx) {
2619
3100
  }
2620
3101
 
2621
3102
  function applyFieldSetIfMissing(op, ctx) {
2622
- const entry = locateEntry(ctx, op.target);
2623
- return isAlwaysArrayFieldKind(entry._type) && rejectFieldOpTarget({
3103
+ const entry = locateEntry(ctx, op.target), legacySingular = isLegacySingularAssignment(entry, ctx);
3104
+ return isAlwaysArrayFieldKind(entry._type) && !legacySingular && rejectFieldOpTarget({
2624
3105
  entry: entry,
2625
3106
  op: op,
2626
3107
  issue: "is always array-valued — setIfMissing applies to nullable entries only; an empty array entry already holds []"
2627
- }), entry.value !== null && entry.value !== void 0 ? {
3108
+ }), entry.value !== null && entry.value !== void 0 && !(legacySingular && Array.isArray(entry.value) && entry.value.length === 0) ? {
2628
3109
  opType: op.type,
2629
3110
  target: op.target
2630
3111
  } : {
@@ -2744,7 +3225,14 @@ function applyFieldUnset(op, ctx) {
2744
3225
  }
2745
3226
 
2746
3227
  function applyFieldAppend(op, ctx) {
2747
- const entry = locateEntry(ctx, op.target), slot = appendItemSlot(entry), item = resolveOpValue({
3228
+ const entry = locateEntry(ctx, op.target);
3229
+ if (isLegacySingularAssignment(entry, ctx)) throw new FieldValueShapeError({
3230
+ entryType: entry._type,
3231
+ entryName: entry.name,
3232
+ mode: "item",
3233
+ issues: [ "legacy singular assignee entries do not support append; redeploy at model 9" ]
3234
+ });
3235
+ const slot = appendItemSlot(entry), item = resolveOpValue({
2748
3236
  src: op.value,
2749
3237
  ctx: ctx,
2750
3238
  ...slot !== void 0 ? {
@@ -2756,7 +3244,7 @@ function applyFieldAppend(op, ctx) {
2756
3244
  item: item,
2757
3245
  types: entry._type === "doc.refs" ? entry.types : void 0,
2758
3246
  of: entry._type === "array" ? entry.of : void 0,
2759
- roles: entry._type === "assignees" ? entry.roles : void 0,
3247
+ roles: isAssignmentFieldEntry(entry) ? entry.roles : void 0,
2760
3248
  memberRoles: ctx.memberRoles,
2761
3249
  roleAliases: ctx.roleAliases
2762
3250
  });
@@ -2768,8 +3256,16 @@ function applyFieldAppend(op, ctx) {
2768
3256
  ctx: ctx,
2769
3257
  src: op.value
2770
3258
  });
2771
- const current = Array.isArray(entry.value) ? entry.value : [];
2772
- return setEntryValue(entry, [ ...current, withRowKey(validated) ]), {
3259
+ const next = [ ...Array.isArray(entry.value) ? entry.value : [], withRowKey(validated) ], value = isAssignmentFieldEntry(entry) ? validateFieldValue({
3260
+ entryType: entry._type,
3261
+ entryName: entry.name,
3262
+ value: next,
3263
+ ...assignmentEntryShape(entry),
3264
+ previousValue: entry.value,
3265
+ memberRoles: ctx.memberRoles,
3266
+ roleAliases: ctx.roleAliases
3267
+ }) : next;
3268
+ return setEntryValue(entry, value), {
2773
3269
  opType: op.type,
2774
3270
  target: op.target,
2775
3271
  resolved: {
@@ -2846,7 +3342,14 @@ async function applyFieldUpdateWhere(op, ctx) {
2846
3342
  }
2847
3343
 
2848
3344
  async function applyFieldRemoveWhere(op, ctx) {
2849
- const entry = locateEntry(ctx, op.target), rows = requireArrayValue(entry, op), matches = await rowMatches({
3345
+ const entry = locateEntry(ctx, op.target);
3346
+ if (isLegacySingularAssignment(entry, ctx)) throw new FieldValueShapeError({
3347
+ entryType: entry._type,
3348
+ entryName: entry.name,
3349
+ mode: "value",
3350
+ issues: [ "legacy singular assignee entries do not support removeWhere; redeploy at model 9" ]
3351
+ });
3352
+ const rows = requireArrayValue(entry, op), matches = await rowMatches({
2850
3353
  where: op.where,
2851
3354
  rows: rows,
2852
3355
  ctx: ctx
@@ -2857,6 +3360,20 @@ async function applyFieldRemoveWhere(op, ctx) {
2857
3360
  };
2858
3361
  }
2859
3362
 
3363
+ function assertLegacySingularValue(args) {
3364
+ const {entry: entry, value: value, ctx: ctx} = args;
3365
+ if (!(!isLegacySingularAssignment(entry, ctx) || !Array.isArray(value) || value.length <= 1)) throw new FieldValueShapeError({
3366
+ entryType: entry._type,
3367
+ entryName: entry.name,
3368
+ mode: "value",
3369
+ issues: [ "legacy singular assignee entries accept at most one total member; redeploy at model 9" ]
3370
+ });
3371
+ }
3372
+
3373
+ function isLegacySingularAssignment(entry, ctx) {
3374
+ return entry._type === "assignee" && !isAssignmentListModel(ctx.mutation.definitionModelVersion);
3375
+ }
3376
+
2860
3377
  function requireMergeableRows(entry, op) {
2861
3378
  if (entry._type !== "array") throw new Error(`${op.type} target ${op.target.scope}:"${op.target.field}" is a ${entry._type} entry — updateWhere merges declared row sub-fields, so it targets \`array\` entries only`);
2862
3379
  }
@@ -3032,6 +3549,14 @@ function whereParams(ctx) {
3032
3549
 
3033
3550
  function validateDefinition(definition) {
3034
3551
  const v2 = createDefinitionValidator();
3552
+ validateConditionSites(v2, definition), validateStart(v2, definition);
3553
+ for (const entry of definition.fields ?? []) v2.checkEntry(entry, `workflow.fields "${entry.name}"`);
3554
+ for (const stage of definition.stages) validateStage(v2, stage);
3555
+ for (const issue of checkWorkflowInvariants(definition)) v2.errors.push(` · ${formatIssuePath(issue.path)}: ${issue.message}`);
3556
+ if (v2.errors.length > 0) throw new Error(`validateDefinition("${definition.name}"): ${v2.errors.length} deploy-time validation error${v2.errors.length === 1 ? "" : "s"}:\n` + v2.errors.join(`\n`));
3557
+ }
3558
+
3559
+ function validateConditionSites(v2, definition) {
3035
3560
  for (const site of conditionSitesOf(definition)) {
3036
3561
  const {address: address} = site;
3037
3562
  address.kind !== "editable-field" && v2.checkCondition(site.condition, conditionSiteLabel({
@@ -3039,12 +3564,11 @@ function validateDefinition(definition) {
3039
3564
  address: address
3040
3565
  }));
3041
3566
  }
3567
+ }
3568
+
3569
+ function validateStart(v2, definition) {
3042
3570
  definition.start?.filter !== void 0 && v2.report("start.filter", startFilterSyntaxIssues(definition.start.filter));
3043
3571
  for (const [index, requirement] of (definition.start?.requirements ?? []).entries()) requirement.type === "groq" && v2.report(`start.requirements[${index}].query`, startRequirementSyntaxIssues(requirement.query));
3044
- for (const entry of definition.fields ?? []) v2.checkEntry(entry, `workflow.fields "${entry.name}"`);
3045
- for (const stage of definition.stages) validateStage(v2, stage);
3046
- for (const issue of checkWorkflowInvariants(definition)) v2.errors.push(` · ${formatIssuePath(issue.path)}: ${issue.message}`);
3047
- if (v2.errors.length > 0) throw new Error(`validateDefinition("${definition.name}"): ${v2.errors.length} deploy-time validation error${v2.errors.length === 1 ? "" : "s"}:\n` + v2.errors.join(`\n`));
3048
3572
  }
3049
3573
 
3050
3574
  function createDefinitionValidator() {
@@ -3073,35 +3597,14 @@ function createDefinitionValidator() {
3073
3597
  };
3074
3598
  }
3075
3599
 
3076
- function conditionSiteLabel({stage: stage, address: address}) {
3077
- const at = `stage "${stage}"`;
3078
- switch (address.kind) {
3079
- case "predicate":
3080
- return `predicate "${address.predicate}"`;
3081
-
3082
- case "transition":
3083
- return `${at} transition "${address.transition}" when`;
3084
-
3085
- case "activity-filter":
3086
- return `${at} activity "${address.activity}".filter`;
3087
-
3088
- case "requirement":
3089
- return `${at} activity "${address.activity}".requirements "${address.requirement}"`;
3090
-
3091
- case "action":
3092
- return `${at} activity "${address.activity}" action "${address.action}".filter`;
3093
-
3094
- case "action-when":
3095
- return `${at} activity "${address.activity}" action "${address.action}".when`;
3096
-
3097
- case "editable-override":
3098
- return `${at}.editable "${address.name}"`;
3099
- }
3600
+ function conditionSiteLabel({stage: stage, address: address}) {
3601
+ const at = `stage "${stage}"`;
3602
+ return address.kind === "predicate" ? `predicate "${address.predicate}"` : address.kind === "transition" ? `${at} transition "${address.transition}" when` : address.kind === "activity-filter" ? `${at} activity "${address.activity}".filter` : address.kind === "requirement" ? `${at} activity "${address.activity}".requirements "${address.requirement}"` : address.kind === "action" ? `${at} activity "${address.activity}" action "${address.action}".filter` : address.kind === "action-when" ? `${at} activity "${address.activity}" action "${address.action}".when` : `${at}.editable "${address.name}"`;
3100
3603
  }
3101
3604
 
3102
3605
  function validateStage(v2, stage) {
3103
3606
  for (const entry of stage.fields ?? []) v2.checkEntry(entry, `stage "${stage.name}".fields "${entry.name}"`);
3104
- for (const guard of stage.guards ?? []) validateGuardReads({
3607
+ for (const guard of stage.guards ?? []) validateGuard({
3105
3608
  v: v2,
3106
3609
  stageName: stage.name,
3107
3610
  guard: guard
@@ -3113,12 +3616,28 @@ function validateStage(v2, stage) {
3113
3616
  });
3114
3617
  }
3115
3618
 
3116
- function validateGuardReads({v: v2, stageName: stageName, guard: guard}) {
3619
+ function validateGuard({v: v2, stageName: stageName, guard: guard}) {
3117
3620
  const where = `stage "${stageName}" guard "${guard.name}"`;
3118
- for (const expr of guard.match.idRefs ?? []) v2.checkGuardRead(expr, `${where} match.idRefs`);
3621
+ if (validateGuardMatch({
3622
+ v: v2,
3623
+ where: where,
3624
+ guard: guard
3625
+ }), guard.predicate !== void 0 && guard.predicate !== "") {
3626
+ const issue = guardPredicateSyntaxIssue(guard.predicate);
3627
+ issue !== void 0 && v2.report(`${where} predicate`, [ issue ]);
3628
+ }
3119
3629
  for (const [key, expr] of Object.entries(guard.metadata ?? {})) v2.checkGuardRead(expr, `${where} metadata "${key}"`);
3120
3630
  }
3121
3631
 
3632
+ function validateGuardMatch({v: v2, where: where, guard: guard}) {
3633
+ guard.match.actions.length === 0 && v2.report(`${where} match.actions`, [ GUARD_ACTIONS_REQUIRED_MESSAGE ]);
3634
+ for (const pattern of guard.match.idPatterns ?? []) {
3635
+ const issue = guardIdPatternIssue(pattern, guard.match.actions);
3636
+ issue !== void 0 && v2.report(`${where} match.idPatterns`, [ issue ]);
3637
+ }
3638
+ for (const expr of guard.match.idRefs ?? []) v2.checkGuardRead(expr, `${where} match.idRefs`);
3639
+ }
3640
+
3122
3641
  function validateActivity({v: v2, stageName: stageName, activity: activity}) {
3123
3642
  const where = `stage "${stageName}" activity "${activity.name}"`;
3124
3643
  for (const entry of activity.fields ?? []) v2.checkEntry(entry, `${where}.fields "${entry.name}"`);
@@ -3259,175 +3778,7 @@ function hasPassed(instant, now) {
3259
3778
  return Date.parse(instant) <= Date.parse(now);
3260
3779
  }
3261
3780
 
3262
- const GUARD_DOC_TYPE = "temp.system.guard";
3263
-
3264
- class MutationGuardDeniedError extends WorkflowError {
3265
- denied;
3266
- documentId;
3267
- action;
3268
- constructor(args) {
3269
- const ids = args.denied.map(d => d.guardId).join(", ");
3270
- super("mutation-guard-denied", `Mutation on "${args.documentId}" (${args.action}) denied by guard(s) [${ids}]`),
3271
- this.name = "MutationGuardDeniedError", this.denied = args.denied, this.documentId = args.documentId,
3272
- this.action = args.action;
3273
- }
3274
- static fromGuards(args) {
3275
- return new MutationGuardDeniedError({
3276
- documentId: args.documentId,
3277
- action: args.action,
3278
- denied: deniedGuardRefs(args.guards)
3279
- });
3280
- }
3281
- }
3282
-
3283
- function deniedGuardRefs(guards) {
3284
- return guards.map(g => ({
3285
- guardId: g._id,
3286
- ...g.name !== void 0 ? {
3287
- name: g.name
3288
- } : {}
3289
- }));
3290
- }
3291
-
3292
- function deniedGuardLabels(denied) {
3293
- return denied.map(d => d.name ?? d.guardId);
3294
- }
3295
-
3296
- function lakeGuardId(args) {
3297
- return `${GUARD_DOC_TYPE}.${args.instanceDocId}.${args.guardName}`;
3298
- }
3299
-
3300
- function compileGuard(args) {
3301
- return {
3302
- _id: args.id,
3303
- _type: GUARD_DOC_TYPE,
3304
- resourceType: args.resourceType,
3305
- resourceId: args.resourceId,
3306
- owner: args.owner,
3307
- sourceInstanceId: args.sourceInstanceId,
3308
- sourceDefinition: args.sourceDefinition,
3309
- sourceStage: args.sourceStage,
3310
- ...args.name !== void 0 ? {
3311
- name: args.name
3312
- } : {},
3313
- ...args.description !== void 0 ? {
3314
- description: args.description
3315
- } : {},
3316
- match: args.match,
3317
- predicate: args.predicate,
3318
- metadata: args.metadata
3319
- };
3320
- }
3321
-
3322
- function toGroqJsPredicate(predicate) {
3323
- return predicate.replace(new RegExp("(?<!\\$)\\bguard\\.", "g"), "$guard.").replace(new RegExp("(?<!\\$)\\bmutation\\.", "g"), "$mutation.");
3324
- }
3325
-
3326
- function globMatch(pattern, value) {
3327
- if (!pattern.includes("*")) return pattern === value;
3328
- const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
3329
- return new RegExp(`^${escaped}$`).test(value);
3330
- }
3331
-
3332
- function guardMatches({guard: guard, doc: doc, action: action}) {
3333
- const m = guard.match;
3334
- if (!m.actions.includes(action) || m.types && m.types.length > 0 && !(doc.type !== void 0 && m.types.includes(doc.type))) return !1;
3335
- if (m.idRefs && m.idRefs.length > 0 || m.idPatterns && m.idPatterns.length > 0) {
3336
- const byRef = m.idRefs?.includes(doc.id) ?? !1, byPattern = m.idPatterns?.some(p => globMatch(p, doc.id)) ?? !1;
3337
- if (!byRef && !byPattern) return !1;
3338
- }
3339
- return !0;
3340
- }
3341
-
3342
- function guardPredicateParams(args) {
3343
- return {
3344
- guard: args.guard,
3345
- mutation: {
3346
- action: args.action
3347
- }
3348
- };
3349
- }
3350
-
3351
- async function evaluateMutationGuard(args) {
3352
- const {guard: guard, context: context} = args;
3353
- if (guard.predicate === "") return !1;
3354
- const params = guardPredicateParams({
3355
- guard: guard,
3356
- action: context.action
3357
- });
3358
- try {
3359
- const tree = parse(toGroqJsPredicate(guard.predicate), {
3360
- mode: "delta",
3361
- params: params
3362
- });
3363
- return await (await evaluate(tree, {
3364
- before: context.before,
3365
- after: context.after,
3366
- params: params,
3367
- ...context.identity !== void 0 ? {
3368
- identity: context.identity
3369
- } : {}
3370
- })).get() === !0;
3371
- } catch {
3372
- return !1;
3373
- }
3374
- }
3375
-
3376
- async function denyingGuards(args) {
3377
- const {guards: guards, doc: doc, context: context} = args, denied = [];
3378
- for (const guard of guards) guardMatches({
3379
- guard: guard,
3380
- doc: doc,
3381
- action: context.action
3382
- }) && (await evaluateMutationGuard({
3383
- guard: guard,
3384
- context: context
3385
- }) || denied.push(guard));
3386
- return denied;
3387
- }
3388
-
3389
- async function documentActionDenials(args) {
3390
- const {doc: doc, resource: resource, action: action, guards: guards, identity: identity} = args, enforceable = guards.filter(g => g.resourceType === resource.type && g.resourceId === resource.id);
3391
- return enforceable.length === 0 ? [] : denyingGuards({
3392
- guards: enforceable,
3393
- doc: {
3394
- id: doc._id,
3395
- type: doc._type
3396
- },
3397
- context: {
3398
- action: action,
3399
- before: doc,
3400
- after: doc,
3401
- ...identity !== void 0 ? {
3402
- identity: identity
3403
- } : {}
3404
- }
3405
- });
3406
- }
3407
-
3408
- async function instanceWriteDenials(args) {
3409
- const {instance: instance, guards: guards, identity: identity} = args;
3410
- return documentActionDenials({
3411
- doc: instance,
3412
- resource: instance.workflowResource,
3413
- action: "update",
3414
- guards: guards,
3415
- ...identity !== void 0 ? {
3416
- identity: identity
3417
- } : {}
3418
- });
3419
- }
3420
-
3421
- async function assertInstanceWriteAllowed(args) {
3422
- const denied = await instanceWriteDenials(args);
3423
- if (denied.length !== 0) throw MutationGuardDeniedError.fromGuards({
3424
- documentId: args.instance._id,
3425
- action: "update",
3426
- guards: denied
3427
- });
3428
- }
3429
-
3430
- const DATA_MODEL_VERSION = 8, DATA_MODEL_MIN_READER = 4, DATA_MODEL_MAX_READER = 8, READER_MODEL_ROLLOUT_URL = "https://www.sanity.io/docs/editorial-workflows/prerelease";
3781
+ const DATA_MODEL_VERSION = 9, DATA_MODEL_MIN_READER = 4, DATA_MODEL_MAX_READER = 9, READER_MODEL_ROLLOUT_URL = "https://www.sanity.io/docs/workflows/prerelease";
3431
3782
 
3432
3783
  class ReaderModelAcknowledgementError extends WorkflowError {
3433
3784
  code="WORKFLOW_READER_MODEL_ACKNOWLEDGEMENT_MISMATCH";
@@ -3567,6 +3918,22 @@ const DATA_MODEL_CHANGES = Object.freeze([ Object.freeze({
3567
3918
  compatibility: "reader-floor",
3568
3919
  applicability: "detectable",
3569
3920
  summary: "Assignee fields may restrict newly assigned users and collective roles by role."
3921
+ }), Object.freeze({
3922
+ id: "split-guard-id-spaces",
3923
+ introducedInModel: 9,
3924
+ minReaderModel: 9,
3925
+ documentTypes: Object.freeze([ "definition", "instance" ]),
3926
+ compatibility: "reader-floor",
3927
+ applicability: "detectable",
3928
+ summary: "Guards spanning edit and lifecycle id spaces emit independently retractable documents."
3929
+ }), Object.freeze({
3930
+ id: "singular-assignee-lists",
3931
+ introducedInModel: 9,
3932
+ minReaderModel: 9,
3933
+ documentTypes: Object.freeze([ "definition", "instance" ]),
3934
+ compatibility: "reader-floor",
3935
+ applicability: "detectable",
3936
+ summary: "Singular assignee fields use member lists with at most one user and any number of roles."
3570
3937
  }) ]);
3571
3938
 
3572
3939
  function recordOf(value) {
@@ -3690,6 +4057,25 @@ function hasRoleConstrainedAssignments(document) {
3690
4057
  return persistedFieldEntries(document).some(entry => entryKindMatches(entry, assignmentKindAcceptsRoles) && Array.isArray(entry.roles) && entry.roles.length > 0);
3691
4058
  }
3692
4059
 
4060
+ function hasSingularAssigneeLists(document) {
4061
+ const root = recordOf(document);
4062
+ if (root === void 0) return !1;
4063
+ if (typeof root.definitionSnapshot != "string") return persistedFieldEntries(document).some(entry => entryHasKind(entry, "assignee"));
4064
+ const snapshot = parsedDefinitionSnapshot(root);
4065
+ return typeof snapshot?.modelVersion == "number" && isAssignmentListModel(snapshot.modelVersion) && persistedFieldEntries(document).some(entry => entryHasKind(entry, "assignee"));
4066
+ }
4067
+
4068
+ function isMutationGuardAction(action) {
4069
+ return typeof action == "string" && MUTATION_GUARD_ACTIONS.some(candidate => candidate === action);
4070
+ }
4071
+
4072
+ function hasSplitGuardIdSpaces(document) {
4073
+ return persistedDefinitionTree(document)?.stages.some(stage => recordsAt(stage, "guards").some(guard => {
4074
+ const actions = recordOf(guard.match)?.actions;
4075
+ return Array.isArray(actions) && mutationGuardRequiresSplitEmission(actions.filter(isMutationGuardAction));
4076
+ })) ?? !1;
4077
+ }
4078
+
3693
4079
  const featureDetectors = {
3694
4080
  "governed-model-stamps": () => !0,
3695
4081
  "subject-field-kind": document => hasFieldKind(document, "subject"),
@@ -3702,6 +4088,8 @@ const featureDetectors = {
3702
4088
  "readiness-requirements": hasReadinessRequirements,
3703
4089
  "due-date-field-kinds": document => hasFieldKind(document, "dueDate") || hasFieldKind(document, "dueDatetime"),
3704
4090
  "role-constrained-assignment-fields": hasRoleConstrainedAssignments,
4091
+ "singular-assignee-lists": hasSingularAssigneeLists,
4092
+ "split-guard-id-spaces": hasSplitGuardIdSpaces,
3705
4093
  "node-semantics": hasNodeSemantics,
3706
4094
  "field-patch-ops": document => hasPersistedOpType(document, [ "field.inc", "field.dec", "field.setIfMissing" ]),
3707
4095
  "attributes-condition-var": () => !0
@@ -3837,8 +4225,93 @@ function findStageNode(args) {
3837
4225
  return args.definition?.stages.find(entry => entry.name === args.stageName);
3838
4226
  }
3839
4227
 
3840
- function findActivityNode(args) {
3841
- return findStageNode(args)?.activities?.find(entry => entry.name === args.activityName);
4228
+ function findActivityNode(args) {
4229
+ return findStageNode(args)?.activities?.find(entry => entry.name === args.activityName);
4230
+ }
4231
+
4232
+ function requirementDescriptor(requirement) {
4233
+ return {
4234
+ name: requirement.name,
4235
+ ...requirement.title !== void 0 ? {
4236
+ title: requirement.title
4237
+ } : {},
4238
+ ...requirement.description !== void 0 ? {
4239
+ description: requirement.description
4240
+ } : {}
4241
+ };
4242
+ }
4243
+
4244
+ function subjectDenialLabels(denied) {
4245
+ return denied.map(d => `${d.permission} on ${d.subject} (${d.resource})`);
4246
+ }
4247
+
4248
+ class ActionDisabledError extends WorkflowError {
4249
+ reason;
4250
+ activity;
4251
+ action;
4252
+ constructor(args) {
4253
+ super("action-disabled", formatDisabledReason({
4254
+ activity: args.activity,
4255
+ action: args.action,
4256
+ reason: args.reason
4257
+ })), this.name = "ActionDisabledError", this.reason = args.reason, this.activity = args.activity,
4258
+ this.action = args.action;
4259
+ }
4260
+ }
4261
+
4262
+ class StartNotAllowedError extends WorkflowError {
4263
+ definition;
4264
+ unmetRequirements;
4265
+ constructor(args) {
4266
+ super("start-not-allowed", `startInstance refused definition "${args.definition}": ${args.unmetRequirements.map(requirement => requirement.title ?? requirement.name).join(", ")}. Pre-flight the verdict with evaluateStart.`),
4267
+ this.name = "StartNotAllowedError", this.definition = args.definition, this.unmetRequirements = args.unmetRequirements;
4268
+ }
4269
+ }
4270
+
4271
+ function actionRendering(action) {
4272
+ const kind = action.disabledReason?.kind;
4273
+ return kind === "filter-failed" ? "absent" : action.triggered === !0 || kind === "cascade-fired" ? "automation" : "button";
4274
+ }
4275
+
4276
+ const disabledReasonDetail = {
4277
+ "filter-failed": r => `action filter returned false${r.detail ? ` (${r.detail})` : ""}`,
4278
+ "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`,
4279
+ "activity-not-active": r => `activity status is "${r.status}"`,
4280
+ "stage-terminal": r => `stage "${r.stage}" is terminal`,
4281
+ "instance-completed": r => `instance completed at ${r.completedAt}`,
4282
+ "instance-aborted": r => `instance aborted at ${r.abortedAt}`,
4283
+ "requirements-unmet": r => `unmet requirement(s): ${r.unmetRequirements.map(requirement => requirement.name).join(", ")}`,
4284
+ "subject-permission-denied": r => `missing subject permission(s): ${subjectDenialLabels(r.denied).join(", ")}`
4285
+ };
4286
+
4287
+ function actionDisabledDetail(reason) {
4288
+ return disabledReasonDetail[reason.kind](reason);
4289
+ }
4290
+
4291
+ function formatDisabledReason({activity: activity, action: action, reason: reason}) {
4292
+ return `Action "${activity}:${action}" is not allowed: ${actionDisabledDetail(reason)}`;
4293
+ }
4294
+
4295
+ class EditFieldDeniedError extends WorkflowError {
4296
+ reason;
4297
+ target;
4298
+ constructor(args) {
4299
+ super("edit-field-denied", formatEditDisabledReason(args.target, args.reason)),
4300
+ this.name = "EditFieldDeniedError", this.reason = args.reason, this.target = args.target;
4301
+ }
4302
+ }
4303
+
4304
+ const editDisabledReasonDetail = {
4305
+ "not-editable": () => "field is not declared editable",
4306
+ "instance-completed": r => `instance completed at ${r.completedAt}`,
4307
+ "instance-aborted": r => `instance aborted at ${r.abortedAt}`,
4308
+ "edit-window-closed": r => `edit window closed (${r.detail})`,
4309
+ "editor-not-permitted": r => `editor not permitted (${r.predicate})`
4310
+ };
4311
+
4312
+ function formatEditDisabledReason(target, reason) {
4313
+ const detail = editDisabledReasonDetail[reason.kind](reason), where = target.activity !== void 0 ? `${target.activity}.${target.field}` : target.field;
4314
+ return `Field "${target.scope}:${where}" is not editable: ${detail}`;
3842
4315
  }
3843
4316
 
3844
4317
  function liveChildrenField(instance) {
@@ -3867,7 +4340,8 @@ function openStage(instance) {
3867
4340
  }
3868
4341
 
3869
4342
  function assigneesOf(stage, activityName) {
3870
- return (stage?.activities.find(t => t.name === activityName)?.fields ?? []).filter(s => s._type === "assignees").flatMap(s => s.value);
4343
+ const entry = stage?.activities.find(t => t.name === activityName);
4344
+ return [ ...assignmentMembers(entry?.fields ?? []) ];
3871
4345
  }
3872
4346
 
3873
4347
  function isResolved(status) {
@@ -3900,11 +4374,16 @@ function failedActivityCause(input) {
3900
4374
 
3901
4375
  function waitingState(input) {
3902
4376
  const active = input.activities.find(t => t.status === "active" && (t.activity.actions ?? []).length > 0 && (t.unmetRequirements ?? []).length === 0);
3903
- if (active !== void 0) return {
4377
+ if (active === void 0) return;
4378
+ const actions = active.actions.filter(action => actionRendering(action) === "button" && action.allowed).map(action => action.action.name), hasManualAction = active.actions.some(action => !isCascadeFired(action.action));
4379
+ let waitingFor = "automation";
4380
+ return actions.length > 0 ? waitingFor = "caller" : hasManualAction && (waitingFor = "manual-action"),
4381
+ {
3904
4382
  state: "waiting",
3905
4383
  activity: active.activity.name,
3906
4384
  assignees: input.assignees[active.activity.name] ?? [],
3907
- actions: (active.activity.actions ?? []).filter(a => !isCascadeFired(a)).map(a => a.name)
4385
+ actions: actions,
4386
+ waitingFor: waitingFor
3908
4387
  };
3909
4388
  }
3910
4389
 
@@ -4030,7 +4509,7 @@ function isClaimExpired(claim, now) {
4030
4509
  return claim.leaseExpiresAt === void 0 || hasPassed(claim.leaseExpiresAt, now);
4031
4510
  }
4032
4511
 
4033
- const EFFECT_RUN_STATUSES = [ "done", "failed", "cancelled" ];
4512
+ const EFFECT_RUN_STATUSES = _exhaustiveOptions()([ "done", "failed", "cancelled" ]);
4034
4513
 
4035
4514
  function userLoginProvider(user) {
4036
4515
  return user?.provider ?? user?.loginProvider;
@@ -4812,6 +5291,96 @@ function coerceToGdr(raw, workflowResource) {
4812
5291
  } : null;
4813
5292
  }
4814
5293
 
5294
+ const NOW = {
5295
+ variable: "now",
5296
+ path: []
5297
+ }, LAST_REPRESENTABLE_MS = 864e13;
5298
+
5299
+ function readsNowIn(reads) {
5300
+ return reads.some(read => read.variable === "now");
5301
+ }
5302
+
5303
+ function readsNow(condition) {
5304
+ return readsNowIn(analyzeCondition(condition).reads);
5305
+ }
5306
+
5307
+ async function stageTimeSites(args) {
5308
+ const {definition: definition, stage: stage, scopeFor: scopeFor} = args, sites = [];
5309
+ for (const site of conditionSitesOf(definition)) {
5310
+ if (site.stage !== stage || site.address.kind === "editable-override" || !readsNow(site.condition)) continue;
5311
+ const activity = "activity" in site.address ? site.address.activity : void 0;
5312
+ sites.push({
5313
+ condition: site.condition,
5314
+ params: await scopeFor(activity)
5315
+ });
5316
+ }
5317
+ return sites;
5318
+ }
5319
+
5320
+ async function nextEvaluationInstant(args) {
5321
+ const {sites: sites, dataset: dataset, now: now} = args, boundaries = [];
5322
+ for (const site of sites) {
5323
+ const boundary = await siteBoundary({
5324
+ ...site,
5325
+ dataset: dataset,
5326
+ now: now
5327
+ });
5328
+ boundary !== void 0 && boundaries.push(boundary);
5329
+ }
5330
+ return [ ...boundaries ].sort(byInstant)[0];
5331
+ }
5332
+
5333
+ function byInstant(a, b) {
5334
+ return Date.parse(a) - Date.parse(b);
5335
+ }
5336
+
5337
+ async function siteBoundary(args) {
5338
+ const {condition: condition, params: params, dataset: dataset, now: now} = args, insight = await explainCondition({
5339
+ condition: condition,
5340
+ dataset: dataset,
5341
+ params: params
5342
+ });
5343
+ for (const candidate of await candidateInstants({
5344
+ insight: insight,
5345
+ params: params,
5346
+ dataset: dataset,
5347
+ now: now
5348
+ })) if ((await whatIfCondition({
5349
+ condition: condition,
5350
+ dataset: dataset,
5351
+ params: params,
5352
+ assign: {
5353
+ target: NOW,
5354
+ value: candidate
5355
+ }
5356
+ })).changed) return candidate;
5357
+ }
5358
+
5359
+ async function candidateInstants(args) {
5360
+ const {insight: insight, params: params, dataset: dataset, now: now} = args, comparands = [];
5361
+ for (const atom of insight.atoms) readsNowIn(atom.atom.reads) && comparands.push(...await comparandsOf({
5362
+ atom: atom,
5363
+ params: params,
5364
+ dataset: dataset
5365
+ }));
5366
+ const instants = comparands.filter(isParseableInstant).map(normalizeInstant).filter(instant => Date.parse(instant) < LAST_REPRESENTABLE_MS).flatMap(instant => [ instant, addMs(instant, 1) ]).filter(instant => !hasPassed(instant, now));
5367
+ return [ ...new Set(instants) ].sort(byInstant);
5368
+ }
5369
+
5370
+ function normalizeInstant(value) {
5371
+ return new Date(Date.parse(value)).toISOString();
5372
+ }
5373
+
5374
+ async function comparandsOf(args) {
5375
+ const {atom: atom, params: params, dataset: dataset} = args, requirement = atom.requirement, values = requirement !== void 0 && requirement.target.variable === "now" && "value" in requirement ? [ requirement.value ] : [];
5376
+ for (const read of atom.atom.reads) read.variable !== "now" && values.push(await runGroq({
5377
+ groq: formatRead(read),
5378
+ params: params,
5379
+ dataset: dataset
5380
+ }));
5381
+ return values;
5382
+ }
5383
+
4815
5384
  const NonEmpty = NonEmptyString, PersistedAssignmentRolesSchema = v.pipe(v.array(NonEmptyString), v.minLength(1, "assignment roles must not be empty")), UnknownRecord = v.record(v.string(), v.unknown()), PersistedChoiceOptionsSchema = v.looseObject({
4816
5385
  list: v.array(v.looseObject({
4817
5386
  title: v.string(),
@@ -4864,7 +5433,7 @@ function fieldArm(kind, value) {
4864
5433
  };
4865
5434
  }
4866
5435
 
4867
- const OptionalRefTypes = v.exactOptional(v.array(v.string())), ResolvedFieldEntrySchema = v.variant("_type", [ v.looseObject(tolerantEntries()({
5436
+ const OptionalRefTypes = v.exactOptional(v.array(v.string())), RawResolvedFieldEntrySchema = v.variant("_type", [ v.looseObject(tolerantEntries()({
4868
5437
  ...fieldArm("doc.ref", fieldValueSchemas["doc.ref"]),
4869
5438
  types: OptionalRefTypes
4870
5439
  })), v.looseObject(tolerantEntries()({
@@ -4885,7 +5454,7 @@ const OptionalRefTypes = v.exactOptional(v.array(v.string())), ResolvedFieldEntr
4885
5454
  })), v.looseObject(tolerantEntries()({
4886
5455
  ...fieldArm("array", v.array(UnknownRecord)),
4887
5456
  of: v.array(PersistedFieldShapeSchema)
4888
- })) ]), EffectOriginSchema = tolerantObject()({
5457
+ })) ]), ResolvedFieldEntrySchema = v.pipe(RawResolvedFieldEntrySchema, v.transform(normalizeNestedAssignmentValues)), EffectOriginSchema = tolerantObject()({
4889
5458
  kind: v.literal("action"),
4890
5459
  name: NonEmpty
4891
5460
  }), PendingEffectClaimSchema = tolerantObject()({
@@ -5746,6 +6315,25 @@ function buildInstanceBase(args) {
5746
6315
  };
5747
6316
  }
5748
6317
 
6318
+ function instanceDocumentForWrite(instance) {
6319
+ const listModel = writesSingularAssignmentLists(instance);
6320
+ return {
6321
+ ...instance,
6322
+ fields: assignmentFieldsForWrite(instance.fields, listModel),
6323
+ stages: assignmentStagesForWrite(instance.stages, listModel)
6324
+ };
6325
+ }
6326
+
6327
+ function memoizedBy(compute) {
6328
+ const cache = /* @__PURE__ */ new Map;
6329
+ return key => {
6330
+ const hit = cache.get(key);
6331
+ if (hit !== void 0) return hit;
6332
+ const value = (async () => compute(key))();
6333
+ return cache.set(key, value), value;
6334
+ };
6335
+ }
6336
+
5749
6337
  async function hydrateSnapshot(args) {
5750
6338
  const {client: client, clientForGdr: clientForGdr, instance: instance, overlay: overlay} = args, loaded = [], visited = /* @__PURE__ */ new Set;
5751
6339
  loaded.push({
@@ -5896,16 +6484,26 @@ function loadCallContext({client: client, instanceId: instanceId, options: optio
5896
6484
 
5897
6485
  async function retryOnRevisionConflict(args) {
5898
6486
  const {client: client, instanceId: instanceId, options: options, commit: commit, onExhausted: onExhausted} = args;
5899
- for (let attempt = 1; attempt <= CONCURRENT_COMMIT_MAX_ATTEMPTS; attempt++) {
5900
- const ctx = await loadCallContext({
6487
+ return retryCommit({
6488
+ load: () => loadCallContext({
5901
6489
  client: client,
5902
6490
  instanceId: instanceId,
5903
6491
  options: options
5904
- });
6492
+ }),
6493
+ commit: commit,
6494
+ shouldRetry: isInstanceCommitRevisionConflict,
6495
+ onExhausted: onExhausted
6496
+ });
6497
+ }
6498
+
6499
+ async function retryCommit(args) {
6500
+ const {load: load, commit: commit, shouldRetry: shouldRetry, onExhausted: onExhausted} = args;
6501
+ for (let attempt = 1; attempt <= CONCURRENT_COMMIT_MAX_ATTEMPTS; attempt++) {
6502
+ const ctx = await load();
5905
6503
  try {
5906
6504
  return await commit(ctx);
5907
6505
  } catch (error) {
5908
- if (!isRevisionConflict(error)) throw error;
6506
+ if (!shouldRetry(error)) throw error;
5909
6507
  }
5910
6508
  }
5911
6509
  throw onExhausted();
@@ -5953,6 +6551,22 @@ async function ctxConditionParams(ctx, opts) {
5953
6551
  }, opts);
5954
6552
  }
5955
6553
 
6554
+ async function ctxNextEvaluationAt(ctx) {
6555
+ if (ctx.instance.completedAt !== void 0) return;
6556
+ const scopeFor = memoizedBy(activityName => ctxConditionParams(ctx, activityName === void 0 ? void 0 : {
6557
+ activityName: activityName
6558
+ })), sites = await stageTimeSites({
6559
+ definition: ctx.definition,
6560
+ stage: ctx.instance.currentStage,
6561
+ scopeFor: scopeFor
6562
+ });
6563
+ if (sites.length !== 0) return nextEvaluationInstant({
6564
+ sites: sites,
6565
+ dataset: ctx.snapshot.docs,
6566
+ now: ctx.now
6567
+ });
6568
+ }
6569
+
5956
6570
  async function ctxEvaluateConditionOutcome({ctx: ctx, condition: condition, opts: opts}) {
5957
6571
  return condition === void 0 ? "satisfied" : evaluateConditionOutcome({
5958
6572
  condition: condition,
@@ -6124,7 +6738,7 @@ async function resolveActivityFieldEntries(args) {
6124
6738
 
6125
6739
  async function resolveBindings(args) {
6126
6740
  const resolved = {};
6127
- for (const [key, groq] of Object.entries(args.bindings ?? {})) resolved[key] = await runGroq({
6741
+ for (const [key, groq] of Object.entries(args.bindings ?? {})) resolved[key] = await runGroq$1({
6128
6742
  groq: groq,
6129
6743
  params: args.params,
6130
6744
  snapshot: args.snapshot
@@ -6581,7 +7195,7 @@ function resolveGuard({guard: guard, instance: instance, stageName: stageName, n
6581
7195
  if (route === null) return null;
6582
7196
  const resource = assertSingleResource(route.targets), types = resolveMatchTypes(route.targets, guard.match.types);
6583
7197
  return {
6584
- doc: compileGuard({
7198
+ docs: compileGuards({
6585
7199
  id: route.guardId,
6586
7200
  resourceType: resource.type,
6587
7201
  resourceId: resource.id,
@@ -6613,19 +7227,63 @@ function resolveGuard({guard: guard, instance: instance, stageName: stageName, n
6613
7227
  async function upsertGuard(args) {
6614
7228
  const {client: client, doc: doc, exists: exists} = args;
6615
7229
  if (!exists) try {
6616
- await client.create(doc, {
6617
- ...SYNC_COMMIT,
6618
- tag: REQUEST_TAG.guardDeploy
6619
- });
6620
- return;
7230
+ return {
7231
+ created: await client.create(doc, {
7232
+ ...SYNC_COMMIT,
7233
+ tag: REQUEST_TAG.guardDeploy
7234
+ })
7235
+ };
6621
7236
  } catch (error) {
6622
7237
  if (!isCreateIdCollision(error)) throw error;
6623
7238
  }
6624
7239
  const {_id: _id, _type: _type, _rev: _rev, _createdAt: _createdAt, _updatedAt: _updatedAt, ...body} = doc;
6625
- await client.patch(doc._id).set(body).commit({
7240
+ return await client.patch(doc._id).set(body).commit({
6626
7241
  ...SYNC_COMMIT,
6627
7242
  tag: REQUEST_TAG.guardRefresh
7243
+ }), {
7244
+ created: void 0
7245
+ };
7246
+ }
7247
+
7248
+ async function retractCreatedGuards(guards) {
7249
+ for (const {client: client, doc: doc} of guards.toReversed()) {
7250
+ if (doc._rev === void 0) return new Error(`Cannot roll back guard ${doc._id}: created document has no revision`);
7251
+ try {
7252
+ const revisionCheck = client.patch(doc._id).set({
7253
+ predicate: doc.predicate
7254
+ }).ifRevisionId(doc._rev);
7255
+ await client.transaction().patch(revisionCheck).delete(doc._id).commit({
7256
+ tag: REQUEST_TAG.guardDeploy
7257
+ });
7258
+ } catch (error) {
7259
+ return error;
7260
+ }
7261
+ }
7262
+ }
7263
+
7264
+ async function deployResolvedGuard(args) {
7265
+ const {client: client, resourceKey: resourceKey, doc: doc, guard: guard, stageArgs: stageArgs, existingByResource: existingByResource} = args, localized = await localizeGuardMetadata({
7266
+ doc: doc,
7267
+ guard: guard,
7268
+ client: client,
7269
+ instance: stageArgs.instance,
7270
+ definition: stageArgs.definition
6628
7271
  });
7272
+ return (await upsertGuard({
7273
+ client: client,
7274
+ doc: localized,
7275
+ exists: existingByResource.get(resourceKey)?.has(doc._id) ?? !1
7276
+ })).created;
7277
+ }
7278
+
7279
+ async function failGuardDeploy(args) {
7280
+ const {cause: cause, stageName: stageName, deployed: deployed, createdGuards: createdGuards, updatedExistingGuard: updatedExistingGuard} = args, rollbackError = updatedExistingGuard ? void 0 : await retractCreatedGuards(createdGuards);
7281
+ throw deployed > 0 && (updatedExistingGuard || rollbackError !== void 0) ? new PartialGuardDeployError({
7282
+ stageName: stageName,
7283
+ deployed: deployed,
7284
+ cause: cause,
7285
+ rollbackError: rollbackError
7286
+ }) : cause;
6629
7287
  }
6630
7288
 
6631
7289
  function resolvedStageGuards(args) {
@@ -6638,10 +7296,12 @@ function resolvedStageGuards(args) {
6638
7296
  now: args.now,
6639
7297
  snapshot: args.snapshot
6640
7298
  });
6641
- resolved !== null && out.push({
6642
- client: args.clientForGdr(resolved.routeGdr),
6643
- resourceKey: resourceGdr(resourceFromParsed(resolved.routeGdr)),
6644
- value: resolved.doc,
7299
+ if (resolved === null) continue;
7300
+ const client = args.clientForGdr(resolved.routeGdr), resourceKey = resourceGdr(resourceFromParsed(resolved.routeGdr));
7301
+ for (const doc of resolved.docs) out.push({
7302
+ client: client,
7303
+ resourceKey: resourceKey,
7304
+ value: doc,
6645
7305
  guard: guard
6646
7306
  });
6647
7307
  }
@@ -6657,10 +7317,15 @@ function resolvedStageGuardRoutes(args) {
6657
7317
  }, out = [];
6658
7318
  for (const guard of stage?.guards ?? []) {
6659
7319
  const route = resolveGuardRoute(guard, ctx);
6660
- route !== null && out.push({
6661
- client: args.clientForGdr(route.routeGdr),
6662
- resourceKey: resourceGdr(resourceFromParsed(route.routeGdr)),
6663
- value: route.guardId
7320
+ if (route === null) continue;
7321
+ const client = args.clientForGdr(route.routeGdr), resourceKey = resourceGdr(resourceFromParsed(route.routeGdr));
7322
+ for (const guardId of compiledGuardIds({
7323
+ id: route.guardId,
7324
+ actions: guard.match.actions
7325
+ })) out.push({
7326
+ client: client,
7327
+ resourceKey: resourceKey,
7328
+ value: guardId
6664
7329
  });
6665
7330
  }
6666
7331
  return out;
@@ -6716,26 +7381,30 @@ async function deployStageGuards(args) {
6716
7381
  if (live === void 0 || live.currentStage !== args.stageName || live.abortedAt !== void 0) return;
6717
7382
  const routed = resolvedStageGuards(args), existingByResource = await existingGuardIds(groupByResource(routed));
6718
7383
  let deployed = 0;
7384
+ const createdGuards = [];
7385
+ let updatedExistingGuard = !1;
6719
7386
  for (const {client: client, resourceKey: resourceKey, value: doc, guard: guard} of routed) {
6720
7387
  try {
6721
- const localized = await localizeGuardMetadata({
7388
+ const created = await deployResolvedGuard({
7389
+ client: client,
7390
+ resourceKey: resourceKey,
6722
7391
  doc: doc,
6723
7392
  guard: guard,
6724
- client: client,
6725
- instance: args.instance,
6726
- definition: args.definition
7393
+ stageArgs: args,
7394
+ existingByResource: existingByResource
6727
7395
  });
6728
- await upsertGuard({
7396
+ created === void 0 ? updatedExistingGuard = !0 : createdGuards.push({
6729
7397
  client: client,
6730
- doc: localized,
6731
- exists: existingByResource.get(resourceKey)?.has(doc._id) ?? !1
7398
+ doc: created
6732
7399
  });
6733
7400
  } catch (cause) {
6734
- throw deployed > 0 ? new PartialGuardDeployError({
7401
+ await failGuardDeploy({
7402
+ cause: cause,
6735
7403
  stageName: args.stageName,
6736
7404
  deployed: deployed,
6737
- cause: cause
6738
- }) : cause;
7405
+ createdGuards: createdGuards,
7406
+ updatedExistingGuard: updatedExistingGuard
7407
+ });
6739
7408
  }
6740
7409
  deployed += 1;
6741
7410
  }
@@ -6967,7 +7636,8 @@ async function persistThenDeploy({ctx: ctx, mutation: mutation, deploy: deploy})
6967
7636
  committedRev: committed._rev,
6968
7637
  restore: instanceStateFields({
6969
7638
  ...ctx.instance,
6970
- minReaderModel: minReaderModelOf(ctx.instance)
7639
+ minReaderModel: minReaderModelOf(ctx.instance),
7640
+ definitionModelVersion: definitionAssignmentModel(ctx.instance)
6971
7641
  }),
6972
7642
  unset: ctx.instance.completedAt === void 0 ? [ "completedAt" ] : [],
6973
7643
  reversible: !spawned,
@@ -7232,7 +7902,7 @@ async function projectRowFields({ctx: ctx, sub: sub, childDefinition: childDefin
7232
7902
  for (const [name, groq] of Object.entries(sub.with ?? {})) {
7233
7903
  const declared = (childDefinition.fields ?? []).find(e => e.name === name);
7234
7904
  if (declared === void 0) throw new Error(`spawn.with["${name}"]: subworkflow "${childDefinition.name}" declares no field entry "${name}"`);
7235
- const raw = await runGroq({
7905
+ const raw = await runGroq$1({
7236
7906
  groq: groq,
7237
7907
  params: {
7238
7908
  ...parentScope,
@@ -7275,7 +7945,7 @@ async function projectRowFields({ctx: ctx, sub: sub, childDefinition: childDefin
7275
7945
  async function resolveContextHandoff({ctx: ctx, sub: sub, parentScope: parentScope}) {
7276
7946
  const out = {};
7277
7947
  for (const [name, groq] of Object.entries(sub.context ?? {})) {
7278
- const v2 = await runGroq({
7948
+ const v2 = await runGroq$1({
7279
7949
  groq: groq,
7280
7950
  params: parentScope,
7281
7951
  snapshot: ctx.snapshot
@@ -8061,91 +8731,6 @@ async function callerBoundVarsForCall(args) {
8061
8731
  });
8062
8732
  }
8063
8733
 
8064
- function requirementDescriptor(requirement) {
8065
- return {
8066
- name: requirement.name,
8067
- ...requirement.title !== void 0 ? {
8068
- title: requirement.title
8069
- } : {},
8070
- ...requirement.description !== void 0 ? {
8071
- description: requirement.description
8072
- } : {}
8073
- };
8074
- }
8075
-
8076
- function subjectDenialLabels(denied) {
8077
- return denied.map(d => `${d.permission} on ${d.subject} (${d.resource})`);
8078
- }
8079
-
8080
- class ActionDisabledError extends WorkflowError {
8081
- reason;
8082
- activity;
8083
- action;
8084
- constructor(args) {
8085
- super("action-disabled", formatDisabledReason({
8086
- activity: args.activity,
8087
- action: args.action,
8088
- reason: args.reason
8089
- })), this.name = "ActionDisabledError", this.reason = args.reason, this.activity = args.activity,
8090
- this.action = args.action;
8091
- }
8092
- }
8093
-
8094
- class StartNotAllowedError extends WorkflowError {
8095
- definition;
8096
- unmetRequirements;
8097
- constructor(args) {
8098
- super("start-not-allowed", `startInstance refused definition "${args.definition}": ${args.unmetRequirements.map(requirement => requirement.title ?? requirement.name).join(", ")}. Pre-flight the verdict with evaluateStart.`),
8099
- this.name = "StartNotAllowedError", this.definition = args.definition, this.unmetRequirements = args.unmetRequirements;
8100
- }
8101
- }
8102
-
8103
- function actionRendering(action) {
8104
- const kind = action.disabledReason?.kind;
8105
- return kind === "filter-failed" ? "absent" : action.triggered === !0 || kind === "cascade-fired" ? "automation" : "button";
8106
- }
8107
-
8108
- const disabledReasonDetail = {
8109
- "filter-failed": r => `action filter returned false${r.detail ? ` (${r.detail})` : ""}`,
8110
- "cascade-fired": r => `the action is cascade-fired (when: ${JSON.stringify(r.when)}) — the engine fires it on truth; it cannot be invoked via fireAction`,
8111
- "activity-not-active": r => `activity status is "${r.status}"`,
8112
- "stage-terminal": r => `stage "${r.stage}" is terminal`,
8113
- "instance-completed": r => `instance completed at ${r.completedAt}`,
8114
- "instance-aborted": r => `instance aborted at ${r.abortedAt}`,
8115
- "requirements-unmet": r => `unmet requirement(s): ${r.unmetRequirements.map(requirement => requirement.name).join(", ")}`,
8116
- "subject-permission-denied": r => `missing subject permission(s): ${subjectDenialLabels(r.denied).join(", ")}`
8117
- };
8118
-
8119
- function actionDisabledDetail(reason) {
8120
- return disabledReasonDetail[reason.kind](reason);
8121
- }
8122
-
8123
- function formatDisabledReason({activity: activity, action: action, reason: reason}) {
8124
- return `Action "${activity}:${action}" is not allowed: ${actionDisabledDetail(reason)}`;
8125
- }
8126
-
8127
- class EditFieldDeniedError extends WorkflowError {
8128
- reason;
8129
- target;
8130
- constructor(args) {
8131
- super("edit-field-denied", formatEditDisabledReason(args.target, args.reason)),
8132
- this.name = "EditFieldDeniedError", this.reason = args.reason, this.target = args.target;
8133
- }
8134
- }
8135
-
8136
- const editDisabledReasonDetail = {
8137
- "not-editable": () => "field is not declared editable",
8138
- "instance-completed": r => `instance completed at ${r.completedAt}`,
8139
- "instance-aborted": r => `instance aborted at ${r.abortedAt}`,
8140
- "edit-window-closed": r => `edit window closed (${r.detail})`,
8141
- "editor-not-permitted": r => `editor not permitted (${r.predicate})`
8142
- };
8143
-
8144
- function formatEditDisabledReason(target, reason) {
8145
- const detail = editDisabledReasonDetail[reason.kind](reason), where = target.activity !== void 0 ? `${target.activity}.${target.field}` : target.field;
8146
- return `Field "${target.scope}:${where}" is not editable: ${detail}`;
8147
- }
8148
-
8149
8734
  async function fireAction(args) {
8150
8735
  const {client: client, instanceId: instanceId, activity: activity, action: action, params: params, requestRecord: requestRecord, options: options} = args;
8151
8736
  return retryOnRevisionConflict({
@@ -8313,7 +8898,7 @@ async function commitAction({ctx: ctx, activityName: activityName, actionName: a
8313
8898
  actionName: actionName,
8314
8899
  callerParams: callerParams,
8315
8900
  options: options
8316
- }), mutation = startMutation(ctx.instance);
8901
+ }), mutation = startRevisionRetryMutation(ctx.instance);
8317
8902
  recordProcessedRequest({
8318
8903
  mutation: mutation,
8319
8904
  record: requestRecord,
@@ -8479,7 +9064,7 @@ async function primeInitialStage(args) {
8479
9064
  })
8480
9065
  });
8481
9066
  const terminal = isTerminalStage(stage), committed = await client.patch(instance._id).set({
8482
- stages: [ initialStageEntry ],
9067
+ stages: assignmentStagesForWrite([ initialStageEntry ], writesSingularAssignmentLists(instance)),
8483
9068
  lastChangedAt: now,
8484
9069
  ...terminal ? {
8485
9070
  completedAt: now
@@ -8537,32 +9122,59 @@ async function runCascadeHop({client: client, instanceId: instanceId, actor: act
8537
9122
  settlingCohorts: settlingCohorts
8538
9123
  } : {},
8539
9124
  memberRolesLoader: memberRolesLoader
8540
- }, ctx = instance === void 0 ? await loadContext({
8541
- client: client,
8542
- instanceId: instanceId,
8543
- options: contextOptions
8544
- }) : await buildEngineContext({
8545
- client: client,
8546
- instance: instance,
8547
- definition: parseDefinitionSnapshot(instance),
8548
- ...contextOptions
9125
+ };
9126
+ let preloadedInstance = instance;
9127
+ return retryCommit({
9128
+ load: async () => {
9129
+ if (preloadedInstance === void 0) return loadContext({
9130
+ client: client,
9131
+ instanceId: instanceId,
9132
+ options: contextOptions
9133
+ });
9134
+ const loaded = preloadedInstance;
9135
+ return preloadedInstance = void 0, buildEngineContext({
9136
+ client: client,
9137
+ instance: loaded,
9138
+ definition: parseDefinitionSnapshot(loaded),
9139
+ ...contextOptions
9140
+ });
9141
+ },
9142
+ commit: ctx => commitCascadeHop({
9143
+ ctx: ctx,
9144
+ actor: actor
9145
+ }),
9146
+ shouldRetry: isInstanceCommitRevisionConflict,
9147
+ onExhausted: () => new ConcurrentCascadeError({
9148
+ instanceId: instanceId,
9149
+ attempts: CONCURRENT_COMMIT_MAX_ATTEMPTS
9150
+ })
8549
9151
  });
9152
+ }
9153
+
9154
+ async function commitCascadeHop({ctx: ctx, actor: actor}) {
8550
9155
  if (isTerminal(ctx)) return {
8551
9156
  moved: !1
8552
9157
  };
8553
- const stage = findStage(ctx.definition, ctx.instance.currentStage), mutation = startMutation(ctx.instance), fired = await runTriggeredActions({
9158
+ const stage = findStage(ctx.definition, ctx.instance.currentStage), mutation = startRevisionRetryMutation(ctx.instance), fired = await runTriggeredActions({
8554
9159
  ctx: ctx,
8555
9160
  mutation: mutation,
8556
9161
  stage: stage
8557
9162
  }), hopCtx = liveViewContext(ctx, mutation), transition = await pickTransition(hopCtx, stage);
8558
- return transition === void 0 ? (fired.fires > 0 && await persistThenMaybeRefresh({
8559
- ctx: ctx,
8560
- mutation: mutation,
8561
- stageName: stage.name,
8562
- didChangeState: fired.ranFieldOps
8563
- }), {
8564
- moved: !1
8565
- }) : (await commitStageMove({
9163
+ if (transition === void 0) {
9164
+ const nextEvaluationAt = await ctxNextEvaluationAt(hopCtx);
9165
+ return fired.fires > 0 && await persistThenMaybeRefresh({
9166
+ ctx: ctx,
9167
+ mutation: mutation,
9168
+ stageName: stage.name,
9169
+ didChangeState: fired.ranFieldOps
9170
+ }), {
9171
+ moved: !1,
9172
+ ...nextEvaluationAt !== void 0 ? {
9173
+ nextEvaluationAt: nextEvaluationAt
9174
+ } : {}
9175
+ };
9176
+ }
9177
+ return await commitStageMove({
8566
9178
  ctx: ctx,
8567
9179
  mutation: mutation,
8568
9180
  fromStage: stage,
@@ -8572,7 +9184,7 @@ async function runCascadeHop({client: client, instanceId: instanceId, actor: act
8572
9184
  actor: actor
8573
9185
  }), {
8574
9186
  moved: !0
8575
- });
9187
+ };
8576
9188
  }
8577
9189
 
8578
9190
  async function cascadeAutoTransitions({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, executionContext: executionContext, telemetry: telemetry, overlay: overlay, settlingCohorts: settlingCohorts}) {
@@ -8588,8 +9200,7 @@ async function cascadeAutoTransitions({client: client, instanceId: instanceId, a
8588
9200
  clock: clock,
8589
9201
  executionContext: executionContext,
8590
9202
  telemetry: telemetry
8591
- });
8592
- if (!(await runCascadeHop({
9203
+ }), hop = await runCascadeHop({
8593
9204
  client: client,
8594
9205
  instanceId: instanceId,
8595
9206
  actor: actor,
@@ -8604,7 +9215,13 @@ async function cascadeAutoTransitions({client: client, instanceId: instanceId, a
8604
9215
  ...drained.drained ? {} : {
8605
9216
  instance: drained.instance
8606
9217
  }
8607
- })).moved) return count;
9218
+ });
9219
+ if (!hop.moved) return {
9220
+ cascaded: count,
9221
+ ...hop.nextEvaluationAt !== void 0 ? {
9222
+ nextEvaluationAt: hop.nextEvaluationAt
9223
+ } : {}
9224
+ };
8608
9225
  if (count++, count >= CASCADE_LIMIT) throw new CascadeLimitError({
8609
9226
  instanceId: instanceId,
8610
9227
  limit: CASCADE_LIMIT
@@ -8904,6 +9521,8 @@ async function recordOrphanedPropagation({ctx: ctx, mutation: mutation, child: c
8904
9521
  function startMutation(instance) {
8905
9522
  return {
8906
9523
  minReaderModel: minReaderModelOf(instance),
9524
+ definitionSnapshot: instance.definitionSnapshot,
9525
+ definitionModelVersion: definitionAssignmentModel(instance),
8907
9526
  currentStage: instance.currentStage,
8908
9527
  fields: (instance.fields ?? []).map(s => ({
8909
9528
  ...s
@@ -8951,11 +9570,17 @@ function startMutation(instance) {
8951
9570
  };
8952
9571
  }
8953
9572
 
9573
+ function startRevisionRetryMutation(instance) {
9574
+ const mutation = startMutation(instance);
9575
+ return mutation.commitErrorMapper = error => isRetryableInstanceCommitConflict(error) ? new InstanceCommitRevisionConflict(error) : error,
9576
+ mutation;
9577
+ }
9578
+
8954
9579
  function instanceStateFields(src) {
8955
- const state = {
9580
+ const listModel = isAssignmentListModel(src.definitionModelVersion), state = {
8956
9581
  currentStage: src.currentStage,
8957
- fields: src.fields,
8958
- stages: src.stages,
9582
+ fields: assignmentFieldsForWrite(src.fields, listModel),
9583
+ stages: assignmentStagesForWrite(src.stages, listModel),
8959
9584
  subworkflows: src.subworkflows ?? [],
8960
9585
  pendingEffects: src.pendingEffects,
8961
9586
  effectHistory: src.effectHistory,
@@ -8972,7 +9597,10 @@ function instanceStateFields(src) {
8972
9597
  return {
8973
9598
  ...modelStampFor({
8974
9599
  documentType: "instance",
8975
- document: state,
9600
+ document: {
9601
+ ...state,
9602
+ definitionSnapshot: src.definitionSnapshot
9603
+ },
8976
9604
  storedMinReaderModel: src.minReaderModel
8977
9605
  }),
8978
9606
  ...state
@@ -9007,11 +9635,11 @@ async function persist(ctx, mutation) {
9007
9635
  ...instanceStateFields(mutation),
9008
9636
  lastChangedAt: ctx.now
9009
9637
  }, pendingCreates = mutation.pendingCreates;
9010
- if (pendingCreates.length === 0) return ctx.client.patch(ctx.instance._id).set(set).ifRevisionId(ctx.instance._rev).commit(SYNC_COMMIT);
9638
+ if (pendingCreates.length === 0) return commitInstanceWrite(() => ctx.client.patch(ctx.instance._id).set(set).ifRevisionId(ctx.instance._rev).commit(SYNC_COMMIT), mutation.commitErrorMapper);
9011
9639
  const tx = ctx.client.transaction();
9012
- for (const {body: body} of pendingCreates) tx.create(body);
9640
+ for (const {body: body} of pendingCreates) tx.create(instanceDocumentForWrite(body));
9013
9641
  tx.patch(ctx.client.patch(ctx.instance._id).set(set).ifRevisionId(ctx.instance._rev)),
9014
- await tx.commit(), mutation.pendingCreates = [];
9642
+ await commitInstanceWrite(() => tx.commit(), mutation.commitErrorMapper), mutation.pendingCreates = [];
9015
9643
  const actorForPriming = ctx.actor, settlingCohorts = new Set(ctx.settlingCohorts).add(ctx.instance._id);
9016
9644
  for (const {body: body} of pendingCreates) try {
9017
9645
  const stepArgs = spawnStepArgs(ctx, {
@@ -9038,6 +9666,14 @@ async function persist(ctx, mutation) {
9038
9666
  return reloaded;
9039
9667
  }
9040
9668
 
9669
+ async function commitInstanceWrite(write, mapError) {
9670
+ try {
9671
+ return await write();
9672
+ } catch (error) {
9673
+ throw mapError?.(error) ?? error;
9674
+ }
9675
+ }
9676
+
9041
9677
  async function settleSpawnBatch(ctx, args) {
9042
9678
  try {
9043
9679
  await propagateSpawnBatch({
@@ -9300,7 +9936,7 @@ async function commitCompleteEffect({ctx: ctx, effectKey: effectKey, status: sta
9300
9936
  outputs: outputs,
9301
9937
  memberRoles: ctx.memberRoles,
9302
9938
  roleAliases: ctx.definition.roleAliases
9303
- }), mutation = startMutation(ctx.instance);
9939
+ }), mutation = startRevisionRetryMutation(ctx.instance);
9304
9940
  recordProcessedRequest({
9305
9941
  mutation: mutation,
9306
9942
  record: requestRecord,
@@ -9479,7 +10115,7 @@ async function commitReport({ctx: ctx, effectKey: effectKey, claimToken: claimTo
9479
10115
  effect: pending.name,
9480
10116
  issues: [ "a mid-dispatch report must carry at least one field op — there is nothing to commit" ]
9481
10117
  });
9482
- const mutation = startMutation(ctx.instance);
10118
+ const mutation = startRevisionRetryMutation(ctx.instance);
9483
10119
  return recordProcessedRequest({
9484
10120
  mutation: mutation,
9485
10121
  record: requestRecord,
@@ -9515,7 +10151,7 @@ async function commitReport({ctx: ctx, effectKey: effectKey, claimToken: claimTo
9515
10151
  };
9516
10152
  }
9517
10153
 
9518
- const RESET_ACTIVITY_TARGETS = [ "active", "skipped" ];
10154
+ const RESET_ACTIVITY_TARGETS = _exhaustiveOptions()([ "active", "skipped" ]);
9519
10155
 
9520
10156
  function isResetActivityTarget(value) {
9521
10157
  return RESET_ACTIVITY_TARGETS.includes(value);
@@ -9627,6 +10263,10 @@ function replayable(args) {
9627
10263
  return (stage.activities ?? []).every(declared => primed.has(declared.name)) ? !(stage.activities ?? []).flatMap(declared => declared.actions ?? []).some(sibling => isCascadeFired(sibling) && sibling.spawn !== void 0) : !1;
9628
10264
  }
9629
10265
 
10266
+ function clientGuardDereference(client) {
10267
+ return async ({_ref: _ref}) => await client.getDocument(toBareId(_ref)) ?? null;
10268
+ }
10269
+
9630
10270
  async function buildFieldInsights({sites: sites, snapshot: snapshot}) {
9631
10271
  const insights = [];
9632
10272
  for (const field of fieldsReadAcross(sites)) {
@@ -9782,6 +10422,7 @@ async function evaluateInstance(args) {
9782
10422
  guards: guards,
9783
10423
  now: now,
9784
10424
  resourceGrants: resourceGrants,
10425
+ guardDereference: clientGuardDereference(client),
9785
10426
  ...localPrincipalId !== void 0 ? {
9786
10427
  localPrincipalId: localPrincipalId
9787
10428
  } : {},
@@ -9794,16 +10435,6 @@ async function evaluateInstance(args) {
9794
10435
  });
9795
10436
  }
9796
10437
 
9797
- function memoizedByName(render) {
9798
- const rendered = /* @__PURE__ */ new Map;
9799
- return activityName => {
9800
- const hit = rendered.get(activityName);
9801
- if (hit !== void 0) return hit;
9802
- const scope = render(activityName);
9803
- return rendered.set(activityName, scope), scope;
9804
- };
9805
- }
9806
-
9807
10438
  async function callerBoundProjectionScopes(args) {
9808
10439
  const can = await advisoryCan({
9809
10440
  instance: args.instance,
@@ -9824,13 +10455,17 @@ async function callerBoundProjectionScopes(args) {
9824
10455
  };
9825
10456
  return {
9826
10457
  scope: await renderConditionScope(args.scopeSource, opts),
9827
- scopeForActivity: memoizedByName(activityName => renderConditionScope(args.scopeSource, {
10458
+ scopeForActivity: memoizedBy(activityName => renderConditionScope(args.scopeSource, {
9828
10459
  activityName: activityName,
9829
10460
  ...opts
9830
10461
  }))
9831
10462
  };
9832
10463
  }
9833
10464
 
10465
+ function clockSitesOf(instance, sites) {
10466
+ return terminalState(instance) !== "in-flight" ? [] : sites.filter(entry => readsNowIn(entry.insight.analysis.reads));
10467
+ }
10468
+
9834
10469
  function currentStageOf(instance, definition) {
9835
10470
  try {
9836
10471
  return findStage(definition, instance.currentStage);
@@ -9880,12 +10515,13 @@ async function evaluateFromSnapshot(args) {
9880
10515
  grants: grants,
9881
10516
  attributes: attributes,
9882
10517
  identity: anchorIdentity
9883
- }), cascadeScopeForActivity = memoizedByName(activityName => renderConditionScope(scopeSource, {
10518
+ }), cascadeScopeForActivity = memoizedBy(activityName => renderConditionScope(scopeSource, {
9884
10519
  activityName: activityName
9885
10520
  })), currentActivityEntries = findOpenStageEntry(instance)?.activities ?? [], guardDenial = await instanceGuardReason({
9886
10521
  instance: instance,
9887
10522
  identity: anchorIdentity,
9888
- guards: args.guards
10523
+ guards: args.guards,
10524
+ dereference: args.guardDereference
9889
10525
  }), subjectDenials = await forecastSubjectDenials({
9890
10526
  instance: instance,
9891
10527
  snapshot: snapshot,
@@ -9942,6 +10578,10 @@ async function evaluateFromSnapshot(args) {
9942
10578
  scopeForActivity: scopeForActivity,
9943
10579
  guardDenial: editGuardDenial,
9944
10580
  sites: sites
10581
+ }), nextEvaluationAt = await nextEvaluationInstant({
10582
+ sites: clockSitesOf(instance, sites),
10583
+ dataset: snapshot.docs,
10584
+ now: now
9945
10585
  });
9946
10586
  return {
9947
10587
  instance: instance,
@@ -9956,7 +10596,10 @@ async function evaluateFromSnapshot(args) {
9956
10596
  sites: sites,
9957
10597
  snapshot: snapshot
9958
10598
  }),
9959
- autonomy: autonomy
10599
+ autonomy: autonomy,
10600
+ ...nextEvaluationAt !== void 0 ? {
10601
+ nextEvaluationAt: nextEvaluationAt
10602
+ } : {}
9960
10603
  };
9961
10604
  }
9962
10605
 
@@ -10104,7 +10747,8 @@ async function evaluateActivity(args) {
10104
10747
  subjectPermissionReason: subjectPermissionReason,
10105
10748
  requirementsReason: requirementsReason,
10106
10749
  sites: sites,
10107
- fireConsequence: a => fireConsequence(activity, a)
10750
+ fireConsequence: a => fireConsequence(activity, a),
10751
+ assignment: assignmentMembers(statusEntry?.fields ?? [])
10108
10752
  }));
10109
10753
  return {
10110
10754
  activity: activity,
@@ -10261,19 +10905,31 @@ function fireableActionVerdict({args: args, insights: insights}) {
10261
10905
  reason: args.requirementsReason,
10262
10906
  ...insights
10263
10907
  });
10264
- const {insight: insight} = insights;
10265
- return action.filter !== void 0 && insight !== void 0 && insight.outcome !== "satisfied" ? disabled({
10908
+ const filterFailure = filterFailureVerdict(args, insights);
10909
+ return filterFailure !== void 0 ? filterFailure : {
10910
+ ...actionEvaluationIdentity(action),
10911
+ allowed: !0,
10912
+ ...insights
10913
+ };
10914
+ }
10915
+
10916
+ function filterFailureVerdict(args, insights) {
10917
+ const {action: action} = args, {insight: insight} = insights;
10918
+ if (action.filter === void 0 || insight === void 0 || insight.outcome === "satisfied") return;
10919
+ const holderGate = insight.blockedBy.some(entry => isAssignedGate(entry.atom)) ? {
10920
+ holders: [ ...activeAssignmentMembers(args.assignment) ]
10921
+ } : void 0;
10922
+ return disabled({
10266
10923
  action: action,
10267
10924
  reason: {
10268
10925
  kind: "filter-failed",
10269
10926
  filter: action.filter
10270
10927
  },
10271
- ...insights
10272
- }) : {
10273
- ...actionEvaluationIdentity(action),
10274
- allowed: !0,
10275
- ...insights
10276
- };
10928
+ ...insights,
10929
+ ...holderGate === void 0 ? {} : {
10930
+ holderGate: holderGate
10931
+ }
10932
+ });
10277
10933
  }
10278
10934
 
10279
10935
  function actionEvaluationIdentity(action) {
@@ -10289,12 +10945,13 @@ function semanticsOf(node) {
10289
10945
  };
10290
10946
  }
10291
10947
 
10292
- async function instanceGuardReason({instance: instance, identity: identity, guards: guards}) {
10948
+ async function instanceGuardReason({instance: instance, identity: identity, guards: guards, dereference: dereference}) {
10293
10949
  if (guards === void 0 || guards.length === 0) return;
10294
10950
  const denied = await instanceWriteDenials({
10295
10951
  instance: instance,
10296
10952
  guards: guards,
10297
- identity: identity
10953
+ identity: identity,
10954
+ dereference: dereference ?? (() => Promise.resolve(null))
10298
10955
  });
10299
10956
  if (denied.length !== 0) return {
10300
10957
  kind: "mutation-guard-denied",
@@ -10325,10 +10982,29 @@ function disabled(args) {
10325
10982
  } : {},
10326
10983
  ...args.whenInsight !== void 0 ? {
10327
10984
  whenInsight: args.whenInsight
10985
+ } : {},
10986
+ ...args.holderGate !== void 0 ? {
10987
+ holderGate: args.holderGate
10328
10988
  } : {}
10329
10989
  };
10330
10990
  }
10331
10991
 
10992
+ const ASSIGNMENT_KINDS = '["assignee", "assignees"]', HAS_USER = '(count(value[@.type == "user"]) > 0 || value.type == "user")', HAS_MEMBER = "(count(value) > 0 || defined(value.type))";
10993
+
10994
+ function assignmentActivityArm(state) {
10995
+ const assignmentFields = `fields[_type in ${ASSIGNMENT_KINDS}]`;
10996
+ return state === "unrouted" ? `count(${assignmentFields}[${HAS_MEMBER}]) == 0` : state === "held" ? `count(${assignmentFields}[${HAS_USER} && (count(value[@.type == "user" && id == $assignmentUserId]) > 0 || (value.type == "user" && value.id == $assignmentUserId))]) > 0` : `count(${assignmentFields}[${HAS_USER}]) == 0 && count(${assignmentFields}[count(value[@.type == "role" && role in $assignmentRoles]) > 0 || (value.type == "role" && value.role in $assignmentRoles)]) > 0`;
10997
+ }
10998
+
10999
+ function assignmentPrefilter(assignment, params) {
11000
+ if (assignment.userId.length === 0) throw new ContractViolationError("instancesQuery: assignment.userId must be non-empty");
11001
+ const states = [ ...new Set(assignment.states ?? [ "unrouted", "routed", "held" ]) ];
11002
+ if (states.length === 0) throw new ContractViolationError("instancesQuery: assignment.states must not be empty");
11003
+ if (states.some(state => state !== "unrouted" && state !== "routed" && state !== "held")) throw new ContractViolationError("instancesQuery: assignment.states contains an unknown state");
11004
+ return params.assignmentUserId = assignment.userId, params.assignmentRoles = [ ...new Set(assignment.roles ?? []) ].sort(),
11005
+ `count(stages[!defined(exitedAt)].activities[status == "active" && (${states.map(assignmentActivityArm).join(" || ")})]) > 0`;
11006
+ }
11007
+
10332
11008
  function inFlightFilter() {
10333
11009
  return "!defined(completedAt)";
10334
11010
  }
@@ -10368,7 +11044,7 @@ function compiledConditions(filter, params) {
10368
11044
  const conditions = [ `_type == "${WORKFLOW_INSTANCE_TYPE}"`, tagScopeFilter() ];
10369
11045
  filter.includeCompleted !== !0 && conditions.push(inFlightFilter()), filter.definition !== void 0 && (conditions.push("definition == $definition"),
10370
11046
  params.definition = filter.definition), filter.stage !== void 0 && (conditions.push("currentStage == $stage"),
10371
- params.stage = filter.stage);
11047
+ params.stage = filter.stage), filter.assignment !== void 0 && conditions.push(assignmentPrefilter(filter.assignment, params));
10372
11048
  const cursor = beforeArm(filter, params);
10373
11049
  cursor !== void 0 && conditions.push(cursor);
10374
11050
  const arms = [ documentArm(filter, params), idsArm(filter, params) ].filter(arm => arm !== void 0);
@@ -10471,6 +11147,222 @@ function stripClaim(entry) {
10471
11147
  return rest;
10472
11148
  }
10473
11149
 
11150
+ function isProjectRole(value) {
11151
+ return isRecord(value) && typeof value.name == "string";
11152
+ }
11153
+
11154
+ async function projectRoleNames(request, projectId) {
11155
+ const result = await request({
11156
+ uri: `/projects/${encodeURIComponent(projectId)}/roles`
11157
+ });
11158
+ if (!Array.isArray(result) || !result.every(isProjectRole)) throw new Error(`Project role catalog response is invalid for project "${projectId}"`);
11159
+ return new Set(result.map(role => role.name));
11160
+ }
11161
+
11162
+ function memberRoleNames(member, projectId) {
11163
+ if (!Array.isArray(member.roles) || !member.roles.every(isProjectRole)) throw new Error(`Project member role directory response is invalid for project "${projectId}" and member "${member.id}"`);
11164
+ return member.roles.map(role => role.name);
11165
+ }
11166
+
11167
+ function heldRoleNames(members, projectId) {
11168
+ return new Set(members.flatMap(member => member.isRobot === !0 ? [] : memberRoleNames(member, projectId)));
11169
+ }
11170
+
11171
+ function addRoles(collector, args) {
11172
+ for (const role of args.roles ?? []) collector.references.push({
11173
+ definition: collector.definition,
11174
+ path: args.path,
11175
+ role: role
11176
+ });
11177
+ }
11178
+
11179
+ function visitAssigneeValue(collector, args) {
11180
+ const members = Array.isArray(args.value) ? args.value : [ args.value ];
11181
+ for (const [index, member] of members.entries()) !isRecord(member) || member.type !== "role" || typeof member.role != "string" || collector.references.push({
11182
+ definition: collector.definition,
11183
+ path: `${args.path}[${index}].role`,
11184
+ role: member.role
11185
+ });
11186
+ }
11187
+
11188
+ function visitObjectValue(collector, args) {
11189
+ for (const shape of args.fields ?? []) visitShapeValue(collector, {
11190
+ path: `${args.path}.${shape.name}`,
11191
+ shape: shape,
11192
+ value: args.value[shape.name]
11193
+ });
11194
+ }
11195
+
11196
+ function visitArrayValue(collector, args) {
11197
+ for (const [index, row] of args.rows.entries()) if (isRecord(row)) for (const shape of args.shapes ?? []) visitShapeValue(collector, {
11198
+ path: `${args.path}[${index}].${shape.name}`,
11199
+ shape: shape,
11200
+ value: row[shape.name]
11201
+ });
11202
+ }
11203
+
11204
+ function visitAssignmentValue(collector, args) {
11205
+ args.kind === "assignee" || args.kind === "assignees" ? visitAssigneeValue(collector, args) : args.kind === "object" && isRecord(args.value) ? visitObjectValue(collector, {
11206
+ ...args,
11207
+ value: args.value
11208
+ }) : args.kind === "array" && Array.isArray(args.value) && visitArrayValue(collector, {
11209
+ path: args.path,
11210
+ rows: args.value,
11211
+ shapes: args.of
11212
+ });
11213
+ }
11214
+
11215
+ function visitShapeValue(collector, args) {
11216
+ visitAssignmentValue(collector, {
11217
+ path: args.path,
11218
+ kind: args.shape.type,
11219
+ value: args.value,
11220
+ fields: args.shape.fields,
11221
+ of: args.shape.of
11222
+ });
11223
+ }
11224
+
11225
+ function visitField(collector, args) {
11226
+ addRoles(collector, {
11227
+ path: `${args.path}.roles`,
11228
+ roles: args.field.roles
11229
+ });
11230
+ for (const shape of args.field.fields ?? args.field.of ?? []) visitFieldShape(collector, {
11231
+ parentPath: args.path,
11232
+ shape: shape
11233
+ });
11234
+ args.field.initialValue?.type === "literal" && visitAssignmentValue(collector, {
11235
+ path: `${args.path}.initialValue.value`,
11236
+ kind: args.field.type,
11237
+ value: args.field.initialValue.value,
11238
+ fields: args.field.fields,
11239
+ of: args.field.of
11240
+ });
11241
+ }
11242
+
11243
+ function visitFieldShape(collector, args) {
11244
+ const path = `${args.parentPath}.${args.shape.name}`;
11245
+ addRoles(collector, {
11246
+ path: `${path}.roles`,
11247
+ roles: args.shape.roles
11248
+ });
11249
+ for (const child of args.shape.fields ?? args.shape.of ?? []) visitFieldShape(collector, {
11250
+ parentPath: path,
11251
+ shape: child
11252
+ });
11253
+ }
11254
+
11255
+ function rolesFromConditions(definition) {
11256
+ return conditionSitesOf(definition).flatMap(site => {
11257
+ try {
11258
+ return analyzeCondition(site.condition).atoms.flatMap(atom => rolesGateOf(atom) ?? []);
11259
+ } catch {
11260
+ return [];
11261
+ }
11262
+ });
11263
+ }
11264
+
11265
+ function visitRoleAliases(collector, aliases) {
11266
+ for (const [role, fulfillers] of Object.entries(aliases ?? {})) role !== UNIVERSAL_ROLE_ALIAS_KEY && collector.references.push({
11267
+ definition: collector.definition,
11268
+ path: `roleAliases.${role}`,
11269
+ role: role
11270
+ }), addRoles(collector, {
11271
+ path: `roleAliases.${role}`,
11272
+ roles: fulfillers
11273
+ });
11274
+ }
11275
+
11276
+ function visitFields(collector, args) {
11277
+ for (const [index, field] of (args.fields ?? []).entries()) visitField(collector, {
11278
+ path: `${args.parentPath}[${index}]`,
11279
+ field: field
11280
+ });
11281
+ }
11282
+
11283
+ function visitAction(collector, args) {
11284
+ addRoles(collector, {
11285
+ path: `${args.path}.roles`,
11286
+ roles: args.action.roles
11287
+ });
11288
+ for (const [effectIndex, effect] of (args.action.effects ?? []).entries()) for (const shape of effect.outputs ?? []) visitFieldShape(collector, {
11289
+ parentPath: `${args.path}.effects[${effectIndex}].outputs`,
11290
+ shape: shape
11291
+ });
11292
+ }
11293
+
11294
+ function visitStage(collector, args) {
11295
+ const stagePath = `stages[${args.stageIndex}]`;
11296
+ visitFields(collector, {
11297
+ fields: args.stage.fields,
11298
+ parentPath: `${stagePath}.fields`
11299
+ });
11300
+ for (const [activityIndex, activity] of (args.stage.activities ?? []).entries()) {
11301
+ const activityPath = `${stagePath}.activities[${activityIndex}]`;
11302
+ visitFields(collector, {
11303
+ fields: activity.fields,
11304
+ parentPath: `${activityPath}.fields`
11305
+ });
11306
+ for (const [actionIndex, action] of (activity.actions ?? []).entries()) visitAction(collector, {
11307
+ action: action,
11308
+ path: `${activityPath}.actions[${actionIndex}]`
11309
+ });
11310
+ }
11311
+ }
11312
+
11313
+ function addConditionReferences(collector, definition) {
11314
+ for (const role of rolesFromConditions(definition)) collector.references.push({
11315
+ definition: collector.definition,
11316
+ path: "generated role condition",
11317
+ role: role
11318
+ });
11319
+ }
11320
+
11321
+ function definitionRoleReferences(definition) {
11322
+ const collector = {
11323
+ references: [],
11324
+ definition: definition.name
11325
+ };
11326
+ visitRoleAliases(collector, definition.roleAliases), visitFields(collector, {
11327
+ fields: definition.fields,
11328
+ parentPath: "fields"
11329
+ });
11330
+ for (const [stageIndex, stage] of definition.stages.entries()) visitStage(collector, {
11331
+ stage: stage,
11332
+ stageIndex: stageIndex
11333
+ });
11334
+ return addConditionReferences(collector, definition), collector.references;
11335
+ }
11336
+
11337
+ function definitionRoleNames(definition) {
11338
+ return [ ...new Set(definitionRoleReferences(definition).map(reference => reference.role)) ];
11339
+ }
11340
+
11341
+ function roleCatalogProjectId(client, resource) {
11342
+ if (resource.type === "dataset") return datasetResourceParts(resource.id).projectId;
11343
+ const projectId = client.config?.().projectId;
11344
+ if (typeof projectId == "string" && projectId.length > 0) return projectId;
11345
+ throw new Error(`workflow.deployDefinitions: definitions reference project roles on ${resource.type} workflow resources, but the client does not expose a projectId for the project role catalog`);
11346
+ }
11347
+
11348
+ async function validateDefinitionRoles(args) {
11349
+ const references = args.definitions.flatMap(definitionRoleReferences);
11350
+ if (references.length === 0) /* @__PURE__ */ return new Map;
11351
+ if (args.client.request === void 0) throw new Error("workflow.deployDefinitions: definitions reference project roles, but the client cannot read the project role catalog");
11352
+ const projectId = roleCatalogProjectId(args.client, args.workflowResource), [known, members] = await Promise.all([ projectRoleNames(args.client.request, projectId), requestProjectMembers(args.client.request, projectId) ]), unknown = references.filter(reference => !known.has(reference.role));
11353
+ if (unknown.length > 0) {
11354
+ const lines = unknown.map(({definition: definition, path: path, role: role}) => ` - ${definition}: ${path} references unknown role "${role}"`);
11355
+ throw new Error(`workflow.deployDefinitions: unknown project roles for project "${projectId}":\n${lines.join(`\n`)}`);
11356
+ }
11357
+ const held = heldRoleNames(members, projectId), warnings = /* @__PURE__ */ new Map;
11358
+ for (const {definition: definition, role: role} of references) {
11359
+ if (held.has(role)) continue;
11360
+ const message = `Project role "${role}" has no current user holder; work routed to it has no recipient.`, definitionWarnings = warnings.get(definition) ?? [];
11361
+ definitionWarnings.includes(message) || definitionWarnings.push(message), warnings.set(definition, definitionWarnings);
11362
+ }
11363
+ return warnings;
11364
+ }
11365
+
10474
11366
  async function sortByDependencies({client: client, definitions: definitions, tag: tag}) {
10475
11367
  const byName = /* @__PURE__ */ new Map;
10476
11368
  for (const def of definitions) {
@@ -10771,7 +11663,7 @@ async function commitEdit({ctx: ctx, target: target, mode: mode, value: value, r
10771
11663
  site: site,
10772
11664
  mode: mode,
10773
11665
  value: value
10774
- }), mutation = startMutation(ctx.instance);
11666
+ }), mutation = startRevisionRetryMutation(ctx.instance);
10775
11667
  recordProcessedRequest({
10776
11668
  mutation: mutation,
10777
11669
  record: requestRecord,
@@ -10915,14 +11807,14 @@ async function runCommitVerb(params) {
10915
11807
  client: client,
10916
11808
  instanceId: instanceId,
10917
11809
  tag: tag
10918
- });
11810
+ }), now = clock();
10919
11811
  return runDeduped({
10920
11812
  client: client,
10921
11813
  tag: tag,
10922
11814
  instanceId: instanceId,
10923
11815
  record: record,
10924
11816
  before: before,
10925
- now: clock(),
11817
+ now: now,
10926
11818
  actor: context.actor,
10927
11819
  clientForGdr: context.clientForGdr,
10928
11820
  refSurface: context.refSurface,
@@ -10933,6 +11825,7 @@ async function runCommitVerb(params) {
10933
11825
  run: () => run({
10934
11826
  ...context,
10935
11827
  clock: clock,
11828
+ now: now,
10936
11829
  record: record,
10937
11830
  before: before
10938
11831
  })
@@ -10956,7 +11849,7 @@ async function runDeduped(args) {
10956
11849
  client: client,
10957
11850
  instanceId: instanceId,
10958
11851
  tag: tag
10959
- }), {cascaded: cascaded, instance: instance} = await cascadeAndReload({
11852
+ }), settled = await cascadeAndReload({
10960
11853
  client: client,
10961
11854
  tag: tag,
10962
11855
  instanceId: instanceId,
@@ -10968,11 +11861,10 @@ async function runDeduped(args) {
10968
11861
  executionContext: executionContext,
10969
11862
  telemetry: telemetry
10970
11863
  });
10971
- return {
10972
- instance: instance,
10973
- cascaded: cascaded,
10974
- changed: instance._rev !== pre._rev
10975
- };
11864
+ return settledResult({
11865
+ ...settled,
11866
+ changed: settled.instance._rev !== pre._rev
11867
+ });
10976
11868
  };
10977
11869
  if (record !== void 0 && before !== void 0 && findProcessedRequest({
10978
11870
  instance: before,
@@ -10988,7 +11880,7 @@ async function runDeduped(args) {
10988
11880
  }
10989
11881
 
10990
11882
  async function cascade({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, executionContext: executionContext, telemetry: telemetry, overlay: overlay}) {
10991
- const count = await cascadeAutoTransitions({
11883
+ const settled = await cascadeAutoTransitions({
10992
11884
  client: client,
10993
11885
  instanceId: instanceId,
10994
11886
  actor: actor,
@@ -11024,13 +11916,13 @@ async function cascade({client: client, instanceId: instanceId, actor: actor, cl
11024
11916
  ...telemetry !== void 0 ? {
11025
11917
  telemetry: telemetry
11026
11918
  } : {}
11027
- }), count;
11919
+ }), settled;
11028
11920
  }
11029
11921
 
11030
11922
  async function cascadeAndReload(args) {
11031
11923
  const {client: client, tag: tag, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock} = args;
11032
11924
  return {
11033
- cascaded: await cascade({
11925
+ ...await cascade({
11034
11926
  client: client,
11035
11927
  instanceId: instanceId,
11036
11928
  actor: actor,
@@ -11053,6 +11945,84 @@ async function cascadeAndReload(args) {
11053
11945
  };
11054
11946
  }
11055
11947
 
11948
+ async function unchangedNextEvaluationAt(args) {
11949
+ const {client: client, instance: instance, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, now: now} = args;
11950
+ if (terminalState(instance) !== "in-flight") return;
11951
+ const ctx = await buildEngineContext({
11952
+ client: client,
11953
+ instance: instance,
11954
+ definition: parseDefinitionSnapshot(instance),
11955
+ clientForGdr: clientForGdr,
11956
+ refSurface: refSurface,
11957
+ memberRolesLoader: memberRolesLoader,
11958
+ clock: () => now,
11959
+ ...actor !== void 0 ? {
11960
+ actor: actor
11961
+ } : {},
11962
+ ...args.executionContext !== void 0 ? {
11963
+ executionContext: args.executionContext
11964
+ } : {},
11965
+ ...args.telemetry !== void 0 ? {
11966
+ telemetry: args.telemetry
11967
+ } : {}
11968
+ });
11969
+ return ctxNextEvaluationAt(ctx);
11970
+ }
11971
+
11972
+ async function adminOverrideResult(args) {
11973
+ const {client: client, tag: tag, instanceId: instanceId, fired: fired, now: now, executionContext: executionContext, telemetry: telemetry, ...scope} = args, optional = {
11974
+ ...executionContext !== void 0 ? {
11975
+ executionContext: executionContext
11976
+ } : {},
11977
+ ...telemetry !== void 0 ? {
11978
+ telemetry: telemetry
11979
+ } : {}
11980
+ }, settled = fired ? await cascade({
11981
+ client: client,
11982
+ instanceId: instanceId,
11983
+ ...scope,
11984
+ ...optional
11985
+ }) : {
11986
+ cascaded: 0
11987
+ }, instance = await reload({
11988
+ client: client,
11989
+ instanceId: instanceId,
11990
+ tag: tag
11991
+ });
11992
+ return settledResult({
11993
+ ...settled,
11994
+ instance: instance,
11995
+ changed: fired,
11996
+ ...fired ? {} : {
11997
+ nextEvaluationAt: await unchangedNextEvaluationAt({
11998
+ client: client,
11999
+ instance: instance,
12000
+ now: now,
12001
+ actor: scope.actor,
12002
+ clientForGdr: scope.clientForGdr,
12003
+ refSurface: scope.refSurface,
12004
+ memberRolesLoader: scope.memberRolesLoader,
12005
+ ...optional
12006
+ })
12007
+ }
12008
+ });
12009
+ }
12010
+
12011
+ function settledResult(args) {
12012
+ const {instance: instance, cascaded: cascaded, changed: changed, ranOps: ranOps, nextEvaluationAt: nextEvaluationAt} = args;
12013
+ return {
12014
+ instance: instance,
12015
+ cascaded: cascaded,
12016
+ changed: changed,
12017
+ ...ranOps !== void 0 ? {
12018
+ ranOps: ranOps
12019
+ } : {},
12020
+ ...nextEvaluationAt !== void 0 ? {
12021
+ nextEvaluationAt: nextEvaluationAt
12022
+ } : {}
12023
+ };
12024
+ }
12025
+
11056
12026
  async function settleStart(args) {
11057
12027
  const runArgs = {
11058
12028
  client: args.client,
@@ -11114,7 +12084,7 @@ async function resumeStart(args) {
11114
12084
  instance: existing,
11115
12085
  initialFieldCount: initialFieldCount,
11116
12086
  viaSpawn: !1
11117
- })), cascaded = await settleStart({
12087
+ })), settled = await settleStart({
11118
12088
  ...settlementArgs,
11119
12089
  instanceId: existing._id,
11120
12090
  ...completesStart ? {
@@ -11125,11 +12095,11 @@ async function resumeStart(args) {
11125
12095
  instanceId: existing._id,
11126
12096
  tag: args.tag
11127
12097
  });
11128
- return {
12098
+ return settledResult({
12099
+ ...settled,
11129
12100
  instance: instance,
11130
- cascaded: cascaded,
11131
12101
  changed: instance._rev !== existing._rev
11132
- };
12102
+ });
11133
12103
  }
11134
12104
 
11135
12105
  function engineOptionsForActor({actor: actor, clock: clock, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, executionContext: executionContext, telemetry: telemetry}) {
@@ -11228,7 +12198,7 @@ async function dispatchGatedWrite(args) {
11228
12198
  ...resourceClients !== void 0 ? {
11229
12199
  resourceClients: resourceClients
11230
12200
  } : {}
11231
- }), ranOps = await apply(args.before, evaluation), {cascaded: cascaded, instance: instance} = await cascadeAndReload({
12201
+ }), ranOps = await apply(args.before, evaluation), settled = await cascadeAndReload({
11232
12202
  client: client,
11233
12203
  tag: tag,
11234
12204
  instanceId: instanceId,
@@ -11240,14 +12210,13 @@ async function dispatchGatedWrite(args) {
11240
12210
  executionContext: args.executionContext,
11241
12211
  telemetry: args.telemetry
11242
12212
  });
11243
- return {
11244
- instance: instance,
11245
- cascaded: cascaded,
12213
+ return settledResult({
12214
+ ...settled,
11246
12215
  changed: !0,
11247
12216
  ...ranOps !== void 0 ? {
11248
12217
  ranOps: ranOps
11249
12218
  } : {}
11250
- };
12219
+ });
11251
12220
  }
11252
12221
 
11253
12222
  function guardDeniedError(instanceId, reason) {
@@ -11461,6 +12430,10 @@ async function abortInstances(args) {
11461
12430
  return aborted;
11462
12431
  }
11463
12432
 
12433
+ function lintDeadActivityInputSeeds(definition) {
12434
+ return definition.stages.flatMap(stage => (stage.activities ?? []).flatMap(activity => (activity.fields ?? []).filter(field => field.initialValue?.type === "input").map(field => `Activity field "${stage.name}.${activity.name}.${field.name}" uses an input seed, but start/spawn inputs address workflow-scope fields only; this field will always materialize empty. Use a literal, query, field read, or working-memory field instead.`)));
12435
+ }
12436
+
11464
12437
  function projectStartSliceRow(instance) {
11465
12438
  const subject = (instance.fields ?? []).find(field => field._type === "subject")?.value;
11466
12439
  return {
@@ -11488,14 +12461,18 @@ const workflow = {
11488
12461
  definitions.forEach(validateDefinition), assertReaderModelAcknowledgement(args.expectedMinReaderModel, {
11489
12462
  requiredMinReaderModel: requiredDefinitionReaderModel(definitions)
11490
12463
  });
11491
- const ordered = await sortByDependencies({
12464
+ const roleWarnings = await validateDefinitionRoles({
12465
+ client: client,
12466
+ definitions: definitions,
12467
+ workflowResource: workflowResource
12468
+ }), ordered = await sortByDependencies({
11492
12469
  client: client,
11493
12470
  definitions: definitions,
11494
12471
  tag: tag
11495
12472
  }), results = [], deployed = [], tx = client.transaction();
11496
12473
  let hasWrites = !1;
11497
12474
  for (const def of ordered) {
11498
- const lint = lintEffectOutputs(def), warnings = lint.length > 0 ? {
12475
+ const lint = [ ...lintEffectOutputs(def), ...lintDeadActivityInputSeeds(def), ...roleWarnings.get(def.name) ?? [] ], warnings = lint.length > 0 ? {
11499
12476
  warnings: lint
11500
12477
  } : {}, latest = await loadLatestDeployed({
11501
12478
  client: client,
@@ -11588,12 +12565,27 @@ const workflow = {
11588
12565
  return runCommitVerb({
11589
12566
  args: args,
11590
12567
  op: "fireAction",
11591
- run: async ({access: access, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, record: record, before: before}) => {
11592
- if (findCurrentActivityEntry(before, activity) === void 0 && idempotent === !0) return {
12568
+ run: async ({access: access, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, now: now, record: record, before: before}) => {
12569
+ if (findCurrentActivityEntry(before, activity) === void 0 && idempotent === !0) return settledResult({
11593
12570
  instance: before,
11594
12571
  cascaded: 0,
11595
- changed: !1
11596
- };
12572
+ changed: !1,
12573
+ nextEvaluationAt: await unchangedNextEvaluationAt({
12574
+ client: client,
12575
+ instance: before,
12576
+ actor: actor,
12577
+ clientForGdr: clientForGdr,
12578
+ refSurface: refSurface,
12579
+ memberRolesLoader: memberRolesLoader,
12580
+ now: now,
12581
+ ...executionContext !== void 0 ? {
12582
+ executionContext: executionContext
12583
+ } : {},
12584
+ ...args.telemetry !== void 0 ? {
12585
+ telemetry: args.telemetry
12586
+ } : {}
12587
+ })
12588
+ });
11597
12589
  const result = await dispatchGatedWrite({
11598
12590
  client: client,
11599
12591
  tag: tag,
@@ -11730,7 +12722,7 @@ const workflow = {
11730
12722
  });
11731
12723
  },
11732
12724
  completeEffect: async rawArgs => {
11733
- const args = taggedScope(rawArgs, REQUEST_TAG.completeEffect), {client: client, tag: tag, instanceId: instanceId, effectKey: effectKey, status: status, outputs: outputs, ops: ops, detail: detail, error: error, durationMs: durationMs, executionContext: executionContext} = args, operationContext = await resolveOperationContext(args), clock = args.clock ?? wallClock, record = requestRecordFor({
12725
+ const args = taggedScope(rawArgs, REQUEST_TAG.completeEffect), {client: client, tag: tag, instanceId: instanceId, effectKey: effectKey, status: status, outputs: outputs, ops: ops, detail: detail, error: error, durationMs: durationMs, executionContext: executionContext} = args, operationContext = await resolveOperationContext(args), clock = args.clock ?? wallClock, now = clock(), record = requestRecordFor({
11734
12726
  idempotencyKey: args.idempotencyKey,
11735
12727
  op: "completeEffect",
11736
12728
  idempotencyTtlMs: args.idempotencyTtlMs
@@ -11740,7 +12732,7 @@ const workflow = {
11740
12732
  tag: tag,
11741
12733
  instanceId: instanceId,
11742
12734
  record: record,
11743
- now: clock(),
12735
+ now: now,
11744
12736
  ...operationContext,
11745
12737
  clock: clock,
11746
12738
  executionContext: executionContext,
@@ -11775,7 +12767,7 @@ const workflow = {
11775
12767
  executionContext: executionContext,
11776
12768
  telemetry: args.telemetry
11777
12769
  })
11778
- }), {cascaded: cascaded, instance: instance} = await cascadeAndReload({
12770
+ }), settled = await cascadeAndReload({
11779
12771
  client: client,
11780
12772
  tag: tag,
11781
12773
  instanceId: instanceId,
@@ -11785,22 +12777,21 @@ const workflow = {
11785
12777
  telemetry: args.telemetry
11786
12778
  });
11787
12779
  return resolveTelemetry(args.telemetry).log(WorkflowEffectCompleted, {
11788
- ...definitionHashFragment(instance.pinnedContentHash),
12780
+ ...definitionHashFragment(settled.instance.pinnedContentHash),
11789
12781
  instanceId: instanceId,
11790
12782
  effect: completion.effect,
11791
12783
  status: status,
11792
12784
  origin: completion.origin,
11793
- cascaded: cascaded
11794
- }), {
11795
- instance: instance,
11796
- cascaded: cascaded,
12785
+ cascaded: settled.cascaded
12786
+ }), settledResult({
12787
+ ...settled,
11797
12788
  changed: !0
11798
- };
12789
+ });
11799
12790
  }
11800
12791
  });
11801
12792
  },
11802
12793
  commitEffectOps: async rawArgs => {
11803
- const args = taggedScope(rawArgs, REQUEST_TAG.commitEffectOps), {client: client, tag: tag, instanceId: instanceId, effectKey: effectKey, claimToken: claimToken, ops: ops, executionContext: executionContext} = args, operationContext = await resolveOperationContext(args), clock = args.clock ?? wallClock, record = requestRecordFor({
12794
+ const args = taggedScope(rawArgs, REQUEST_TAG.commitEffectOps), {client: client, tag: tag, instanceId: instanceId, effectKey: effectKey, claimToken: claimToken, ops: ops, executionContext: executionContext} = args, operationContext = await resolveOperationContext(args), clock = args.clock ?? wallClock, now = clock(), record = requestRecordFor({
11804
12795
  idempotencyKey: args.idempotencyKey,
11805
12796
  op: "commitEffectOps",
11806
12797
  idempotencyTtlMs: args.idempotencyTtlMs
@@ -11811,7 +12802,7 @@ const workflow = {
11811
12802
  tag: tag,
11812
12803
  instanceId: instanceId,
11813
12804
  record: record,
11814
- now: clock(),
12805
+ now: now,
11815
12806
  ...operationContext,
11816
12807
  clock: clock,
11817
12808
  executionContext: executionContext,
@@ -11831,7 +12822,7 @@ const workflow = {
11831
12822
  executionContext: executionContext,
11832
12823
  telemetry: args.telemetry
11833
12824
  })
11834
- }), {cascaded: cascaded, instance: instance} = await cascadeAndReload({
12825
+ }), settled = await cascadeAndReload({
11835
12826
  client: client,
11836
12827
  tag: tag,
11837
12828
  instanceId: instanceId,
@@ -11841,15 +12832,14 @@ const workflow = {
11841
12832
  telemetry: args.telemetry
11842
12833
  });
11843
12834
  return resolveTelemetry(args.telemetry).log(WorkflowEffectStateReported, {
11844
- ...definitionHashFragment(instance.pinnedContentHash),
12835
+ ...definitionHashFragment(settled.instance.pinnedContentHash),
11845
12836
  instanceId: instanceId,
11846
12837
  effect: report.effect,
11847
- cascaded: cascaded
11848
- }), {
11849
- instance: instance,
11850
- cascaded: cascaded,
12838
+ cascaded: settled.cascaded
12839
+ }), settledResult({
12840
+ ...settled,
11851
12841
  changed: !0
11852
- };
12842
+ });
11853
12843
  }
11854
12844
  });
11855
12845
  },
@@ -11862,9 +12852,10 @@ const workflow = {
11862
12852
  await assertInstanceWriteAllowed({
11863
12853
  instance: current,
11864
12854
  guards: guards,
11865
- identity: lakePrincipalId(access)
12855
+ identity: lakePrincipalId(access),
12856
+ dereference: clientGuardDereference(client)
11866
12857
  });
11867
- const {cascaded: cascaded, instance: instance} = await cascadeAndReload({
12858
+ const settled = await cascadeAndReload({
11868
12859
  client: client,
11869
12860
  tag: tag,
11870
12861
  instanceId: instanceId,
@@ -11874,21 +12865,20 @@ const workflow = {
11874
12865
  telemetry: args.telemetry
11875
12866
  });
11876
12867
  return resolveTelemetry(args.telemetry).log(WorkflowInstanceTicked, {
11877
- ...definitionHashFragment(instance.pinnedContentHash),
12868
+ ...definitionHashFragment(settled.instance.pinnedContentHash),
11878
12869
  instanceId: instanceId,
11879
- cascaded: cascaded
11880
- }), {
11881
- instance: instance,
11882
- cascaded: cascaded,
11883
- changed: instance._rev !== current._rev
11884
- };
12870
+ cascaded: settled.cascaded
12871
+ }), settledResult({
12872
+ ...settled,
12873
+ changed: settled.instance._rev !== current._rev
12874
+ });
11885
12875
  },
11886
12876
  setStage: async rawArgs => {
11887
12877
  const args = taggedScope(rawArgs, REQUEST_TAG.setStage), {client: client, tag: tag, instanceId: instanceId, targetStage: targetStage, reason: reason, executionContext: executionContext} = args;
11888
12878
  return runCommitVerb({
11889
12879
  args: args,
11890
12880
  op: "setStage",
11891
- run: async ({actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, record: record, before: before}) => {
12881
+ run: async ({actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, now: now, record: record, before: before}) => {
11892
12882
  const result = await setStage({
11893
12883
  client: client,
11894
12884
  instanceId: instanceId,
@@ -11908,34 +12898,29 @@ const workflow = {
11908
12898
  executionContext: executionContext,
11909
12899
  telemetry: args.telemetry
11910
12900
  })
11911
- }), cascaded = result.fired ? await cascade({
12901
+ });
12902
+ return resolveTelemetry(args.telemetry).log(WorkflowStageSet, {
12903
+ ...definitionHashFragment(before.pinnedContentHash),
12904
+ instanceId: instanceId,
12905
+ changed: result.fired
12906
+ }), adminOverrideResult({
11912
12907
  client: client,
12908
+ tag: tag,
11913
12909
  instanceId: instanceId,
12910
+ fired: result.fired,
11914
12911
  actor: actor,
11915
12912
  clientForGdr: clientForGdr,
11916
12913
  refSurface: refSurface,
11917
12914
  memberRolesLoader: memberRolesLoader,
11918
12915
  clock: clock,
12916
+ now: now,
11919
12917
  ...executionContext !== void 0 ? {
11920
12918
  executionContext: executionContext
11921
12919
  } : {},
11922
12920
  ...args.telemetry !== void 0 ? {
11923
12921
  telemetry: args.telemetry
11924
12922
  } : {}
11925
- }) : 0;
11926
- return resolveTelemetry(args.telemetry).log(WorkflowStageSet, {
11927
- ...definitionHashFragment(before.pinnedContentHash),
11928
- instanceId: instanceId,
11929
- changed: result.fired
11930
- }), {
11931
- instance: await reload({
11932
- client: client,
11933
- instanceId: instanceId,
11934
- tag: tag
11935
- }),
11936
- cascaded: cascaded,
11937
- changed: result.fired
11938
- };
12923
+ });
11939
12924
  }
11940
12925
  });
11941
12926
  },
@@ -11968,7 +12953,7 @@ const workflow = {
11968
12953
  ...definitionHashFragment(before.pinnedContentHash),
11969
12954
  instanceId: instanceId,
11970
12955
  changed: changed
11971
- }), {
12956
+ }), settledResult({
11972
12957
  instance: await reload({
11973
12958
  client: client,
11974
12959
  instanceId: instanceId,
@@ -11976,7 +12961,7 @@ const workflow = {
11976
12961
  }),
11977
12962
  cascaded: 0,
11978
12963
  changed: changed
11979
- };
12964
+ });
11980
12965
  }
11981
12966
  });
11982
12967
  },
@@ -11986,7 +12971,7 @@ const workflow = {
11986
12971
  return runCommitVerb({
11987
12972
  args: args,
11988
12973
  op: "resetActivity",
11989
- run: async ({actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, record: record, before: before}) => {
12974
+ run: async ({actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, now: now, record: record, before: before}) => {
11990
12975
  const result = await resetActivity({
11991
12976
  client: client,
11992
12977
  instanceId: instanceId,
@@ -12004,34 +12989,29 @@ const workflow = {
12004
12989
  executionContext: executionContext,
12005
12990
  telemetry: args.telemetry
12006
12991
  })
12007
- }), cascaded = result.fired ? await cascade({
12992
+ });
12993
+ return resolveTelemetry(args.telemetry).log(WorkflowActivityReset, {
12994
+ ...definitionHashFragment(before.pinnedContentHash),
12995
+ instanceId: instanceId,
12996
+ changed: result.fired
12997
+ }), adminOverrideResult({
12008
12998
  client: client,
12999
+ tag: tag,
12009
13000
  instanceId: instanceId,
13001
+ fired: result.fired,
12010
13002
  actor: actor,
12011
13003
  clientForGdr: clientForGdr,
12012
13004
  refSurface: refSurface,
12013
13005
  memberRolesLoader: memberRolesLoader,
12014
13006
  clock: clock,
13007
+ now: now,
12015
13008
  ...executionContext !== void 0 ? {
12016
13009
  executionContext: executionContext
12017
13010
  } : {},
12018
13011
  ...args.telemetry !== void 0 ? {
12019
13012
  telemetry: args.telemetry
12020
13013
  } : {}
12021
- }) : 0;
12022
- return resolveTelemetry(args.telemetry).log(WorkflowActivityReset, {
12023
- ...definitionHashFragment(before.pinnedContentHash),
12024
- instanceId: instanceId,
12025
- changed: result.fired
12026
- }), {
12027
- instance: await reload({
12028
- client: client,
12029
- instanceId: instanceId,
12030
- tag: tag
12031
- }),
12032
- cascaded: cascaded,
12033
- changed: result.fired
12034
- };
13014
+ });
12035
13015
  }
12036
13016
  });
12037
13017
  },
@@ -12364,8 +13344,8 @@ async function startFreshInstance(args) {
12364
13344
  extraHistory: fieldDiscards
12365
13345
  } : {}
12366
13346
  });
12367
- await client.create(base, SYNC_COMMIT);
12368
- const cascaded = await settleStart({
13347
+ await client.create(instanceDocumentForWrite(base), SYNC_COMMIT);
13348
+ const settled = await settleStart({
12369
13349
  client: client,
12370
13350
  tag: tag,
12371
13351
  instanceId: id,
@@ -12387,15 +13367,15 @@ async function startFreshInstance(args) {
12387
13367
  viaSpawn: !1
12388
13368
  }))
12389
13369
  });
12390
- return {
13370
+ return settledResult({
13371
+ ...settled,
12391
13372
  instance: await reload({
12392
13373
  client: client,
12393
13374
  instanceId: id,
12394
13375
  tag: tag
12395
13376
  }),
12396
- cascaded: cascaded,
12397
13377
  changed: !0
12398
- };
13378
+ });
12399
13379
  }
12400
13380
 
12401
13381
  function assertInitialStageExists(definition) {
@@ -12570,7 +13550,7 @@ function missingHandlerMessage(info) {
12570
13550
  }
12571
13551
 
12572
13552
  async function verifyDeployedDefinitionsInternal(args) {
12573
- const {tag: tag, effectHandlers: effectHandlers, missingHandler: missingHandler, logger: logger} = args, client = withRequestTag(args.client, REQUEST_TAG.verifyDefinitions);
13553
+ const {tag: tag, handlers: handlers, missingHandler: missingHandler, logger: logger} = args, client = withRequestTag(args.client, REQUEST_TAG.verifyDefinitions);
12574
13554
  validateTag(tag);
12575
13555
  const log = logger("verifyDeployedDefinitions"), definitions = (await client.fetch(definitionsListGroq("asc"), {
12576
13556
  tag: tag
@@ -12580,7 +13560,7 @@ async function verifyDeployedDefinitionsInternal(args) {
12580
13560
  version: def.version,
12581
13561
  _id: def._id
12582
13562
  }), walkEffectNames(def, (name, location) => {
12583
- if (effectHandlers[name] !== void 0) return;
13563
+ if (handlers[name] !== void 0) return;
12584
13564
  const key = `${name}@@${def._id}`;
12585
13565
  let bucket = missingByName.get(key);
12586
13566
  bucket === void 0 && (bucket = {
@@ -12639,7 +13619,7 @@ function isCancelledCompletion(data) {
12639
13619
  }
12640
13620
 
12641
13621
  async function drainEffectsInternal(args) {
12642
- const {tag: tag, workflowResource: workflowResource, instanceId: instanceId, effectHandlers: effectHandlers, missingHandler: missingHandler, logger: logger, handlerClient: rawHandlerClient, handlerResourceClients: rawHandlerResourceClients} = args, cascadeTelemetry = drainCascadeTelemetry(args.telemetry), {client: client, resourceClients: resourceClients} = taggedScope(args, REQUEST_TAG.drain), leaseMs = args.leaseMs ?? DEFAULT_EFFECT_LEASE_MS, clock = args.clock ?? wallClock, handlerClient = effectHandlerClient(rawHandlerClient), handlerResourceClients = effectHandlerResolver(rawHandlerResourceClients), routeGdr = buildClientForGdr({
13622
+ const {tag: tag, workflowResource: workflowResource, instanceId: instanceId, handlers: handlers, missingHandler: missingHandler, logger: logger, handlerClient: rawHandlerClient, handlerResourceClients: rawHandlerResourceClients} = args, cascadeTelemetry = drainCascadeTelemetry(args.telemetry), {client: client, resourceClients: resourceClients} = taggedScope(args, REQUEST_TAG.drain), leaseMs = args.leaseMs ?? DEFAULT_EFFECT_LEASE_MS, clock = args.clock ?? wallClock, handlerClient = effectHandlerClient(rawHandlerClient), handlerResourceClients = effectHandlerResolver(rawHandlerResourceClients), routeGdr = buildClientForGdr({
12643
13623
  client: handlerClient,
12644
13624
  workflowResource: workflowResource,
12645
13625
  resourceClients: handlerResourceClients
@@ -12659,7 +13639,7 @@ async function drainEffectsInternal(args) {
12659
13639
  resourceClients: resourceClients
12660
13640
  } : {},
12661
13641
  instanceId: instanceId,
12662
- effectHandlers: effectHandlers,
13642
+ handlers: handlers,
12663
13643
  missingHandler: missingHandler,
12664
13644
  logger: logger,
12665
13645
  log: log,
@@ -12691,7 +13671,7 @@ async function drainOneCandidate(pass) {
12691
13671
  skippedKeys: skippedKeys
12692
13672
  });
12693
13673
  if (candidate === void 0) return !1;
12694
- const handler = pass.effectHandlers[candidate.name];
13674
+ const handler = pass.handlers[candidate.name];
12695
13675
  if (handler === void 0) return await assertSkippableOrThrow({
12696
13676
  missingHandler: pass.missingHandler,
12697
13677
  candidate: candidate,
@@ -13100,6 +14080,7 @@ function createInstanceSession(args) {
13100
14080
  snapshot: snapshotFrom(held, normalizedSelf),
13101
14081
  guards: guards,
13102
14082
  resourceGrants: resourceGrants,
14083
+ guardDereference: clientGuardDereference(evalScope.client),
13103
14084
  ...localPrincipalId !== void 0 ? {
13104
14085
  localPrincipalId: localPrincipalId
13105
14086
  } : {},
@@ -13114,7 +14095,7 @@ function createInstanceSession(args) {
13114
14095
  } : {}
13115
14096
  });
13116
14097
  }, settleAfterApply = async ({actor: actor, held: held, ranOps: ranOps, scope: scope, memberRolesLoader: memberRolesLoader}) => {
13117
- const cascaded = await cascadeHeld({
14098
+ const settled = await cascadeHeld({
13118
14099
  scope: scope,
13119
14100
  actor: actor,
13120
14101
  held: held,
@@ -13124,14 +14105,14 @@ function createInstanceSession(args) {
13124
14105
  client: scope.client,
13125
14106
  instanceId: instance._id,
13126
14107
  tag: tag
13127
- }), {
14108
+ }), settledResult({
14109
+ ...settled,
13128
14110
  instance: instance,
13129
- cascaded: cascaded,
13130
14111
  changed: !0,
13131
14112
  ...ranOps !== void 0 ? {
13132
14113
  ranOps: ranOps
13133
14114
  } : {}
13134
- };
14115
+ });
13135
14116
  }, cascadeHeld = ({scope: scope, actor: actor, held: held, memberRolesLoader: memberRolesLoader}) => cascade({
13136
14117
  client: scope.client,
13137
14118
  instanceId: instance._id,
@@ -13242,13 +14223,14 @@ function createInstanceSession(args) {
13242
14223
  identity: lakePrincipalId({
13243
14224
  actor: actor,
13244
14225
  localPrincipalId: localPrincipalId
13245
- })
14226
+ }),
14227
+ dereference: clientGuardDereference(tickScope.client)
13246
14228
  });
13247
14229
  const before = await reload({
13248
14230
  client: tickScope.client,
13249
14231
  instanceId: instance._id,
13250
14232
  tag: tag
13251
- }), cascaded = await cascadeHeld({
14233
+ }), settled = await cascadeHeld({
13252
14234
  scope: tickScope,
13253
14235
  actor: actor,
13254
14236
  held: held,
@@ -13261,12 +14243,12 @@ function createInstanceSession(args) {
13261
14243
  }), telemetry.log(WorkflowInstanceTicked, {
13262
14244
  ...definitionHashFragment(instance.pinnedContentHash),
13263
14245
  instanceId: instance._id,
13264
- cascaded: cascaded
13265
- }), {
14246
+ cascaded: settled.cascaded
14247
+ }), settledResult({
14248
+ ...settled,
13266
14249
  instance: instance,
13267
- cascaded: cascaded,
13268
14250
  changed: instance._rev !== before._rev
13269
- };
14251
+ });
13270
14252
  });
13271
14253
  },
13272
14254
  fireAction({activity: activity, action: action, params: params}) {
@@ -13416,7 +14398,10 @@ const silentLogger = {
13416
14398
  function createEngine(args) {
13417
14399
  const {workflowResource: workflowResource, clock: clock, tag: tag, executionContext: executionContext} = args, {client: client, resourceClients: resourceClients} = taggedScope(args, REQUEST_TAG.engine);
13418
14400
  validateTag(tag);
13419
- const effectHandlers = args.effectHandlers ?? {}, missingHandler = args.missingHandler ?? "fail", logger = args.loggerFactory ?? defaultLoggerFactory, telemetry = resolveTelemetry(args.telemetry), EMPTY_DRAIN_LOG_INTERVAL_MS = 6e4;
14401
+ const effects = {
14402
+ handlers: args.effects?.handlers ?? {},
14403
+ missingHandler: args.effects?.missingHandler ?? "fail"
14404
+ }, logger = args.loggerFactory ?? defaultLoggerFactory, telemetry = resolveTelemetry(args.telemetry), EMPTY_DRAIN_LOG_INTERVAL_MS = 6e4;
13420
14405
  let lastEmptyDrainLoggedAt;
13421
14406
  const emptyDrainDue = () => {
13422
14407
  const now = Date.parse((clock ?? wallClock)());
@@ -13447,8 +14432,7 @@ function createEngine(args) {
13447
14432
  client: client,
13448
14433
  tag: tag,
13449
14434
  workflowResource: workflowResource,
13450
- effectHandlers: effectHandlers,
13451
- missingHandler: missingHandler,
14435
+ effects: effects,
13452
14436
  logger: logger,
13453
14437
  telemetry: telemetry,
13454
14438
  resolveActor: rest => resolveClientActor(client, rest),
@@ -13502,12 +14486,11 @@ function createEngine(args) {
13502
14486
  workflowResource: workflowResource,
13503
14487
  ...optionalScope,
13504
14488
  instanceId: instanceId,
13505
- effectHandlers: effectHandlers,
13506
- missingHandler: missingHandler,
14489
+ ...effects,
13507
14490
  logger: logger,
13508
14491
  telemetry: telemetry,
13509
- ...args.effectLeaseMs !== void 0 ? {
13510
- leaseMs: args.effectLeaseMs
14492
+ ...args.effects?.leaseMs !== void 0 ? {
14493
+ leaseMs: args.effects.leaseMs
13511
14494
  } : {}
13512
14495
  }), drained = result.drained;
13513
14496
  return (drained.length > 0 || emptyDrainDue()) && telemetry.log(WorkflowEffectsDrained, {
@@ -13520,8 +14503,7 @@ function createEngine(args) {
13520
14503
  verifyDeployedDefinitions: () => verifyDeployedDefinitionsInternal({
13521
14504
  client: client,
13522
14505
  tag: tag,
13523
- effectHandlers: effectHandlers,
13524
- missingHandler: missingHandler,
14506
+ ...effects,
13525
14507
  logger: logger
13526
14508
  })
13527
14509
  };
@@ -14060,4 +15042,4 @@ function displayDescription(typeKey) {
14060
15042
  if (typeKey) return DISPLAY[typeKey]?.description;
14061
15043
  }
14062
15044
 
14063
- export { ACTION_SEMANTICS, ACTIVITY_KINDS, ACTIVITY_KIND_DISPLAY, ACTOR_KINDS, ANONYMOUS_IDENTITY, AUTHORING_DISPLAY, ActionDisabledError, ActionParamsInvalidError, CONDITION_VARS, CONTEXT_ENTRY_DISPLAY, CascadeLimitError, ConcurrentCommitEffectOpsError, ConcurrentCompleteEffectError, ConcurrentEditFieldError, ConcurrentFireActionError, ContractViolationError, DATA_MODEL_CHANGES, DATA_MODEL_MAX_READER, DATA_MODEL_MIN_READER, DATA_MODEL_VERSION, DECISION_SEMANTICS, DEFAULT_CONTENT_PERSPECTIVE, DEFAULT_EFFECT_LEASE_MS, DEFAULT_IDEMPOTENCY_TTL_MS, DEFAULT_TRANSITION_WHEN, DISPLAY, DRIVER_KINDS, DRIVER_KIND_DISPLAY, DefinitionInUseError, DefinitionNotFoundError, EFFECT_COMMIT_DISPATCH_CAP, EFFECT_COMMIT_QUEUE_DEPTH, ENGINE_API_VERSION, EXECUTION_KINDS, EXECUTOR_CLASSIFICATIONS, EXECUTOR_CLASSIFICATION_DISPLAY, EditFieldDeniedError, EffectCommitQueueOverflowError, EffectNotFoundError, EffectOpsInvalidError, EffectOutputsInvalidError, FIELD_KIND_DISPLAY, FILTER_SCOPE_VARS, FieldValueShapeError, GROUP_KINDS, GROUP_KIND_DISPLAY, GUARD_DOC_TYPE, GUARD_OWNER, GUARD_PREDICATE_VARS, HISTORY_DISPLAY, InitialFieldsInvalidError, InstanceNotFoundError, MAX_COUNTERFACTUAL_INDEX2 as MAX_COUNTERFACTUAL_INDEX, MissingHandlerError, ModelVersionAheadError, MutationGuardDeniedError, MutationGuardDocSchema, NEUTRAL_MARK, OP_DISPLAY, OUTCOME_MARKS, PartialGuardDeployError, PersistedDocShapeError, READER_MODEL_ROLLOUT_URL, RESERVED_CONDITION_VARS, ReaderModelAcknowledgementError, RefResourceUndeclaredError, RequiredFieldNotProvidedError, SIGNAL_SEMANTICS, START_FILTER_VARS, START_REQUIREMENT_VARS, SYSTEM_IDENTITY, SpawnContractsInvalidError, StaleEffectClaimError, StartNotAllowedError, StartNotPrimedError, StartNotSettledError, WORKFLOW_DEFINITION_TYPE, WORKFLOW_INSTANCE_TYPE, WorkflowActionFired, WorkflowActivityReset, WorkflowDefinitionDeleted, WorkflowDefinitionDeployed, WorkflowEffectCompleted, WorkflowEffectStateReported, WorkflowEffectsDrained, WorkflowError, WorkflowFieldEdited, WorkflowInstanceAborted, WorkflowInstancePreviewSchema, WorkflowInstanceSchema, WorkflowInstanceStarted, WorkflowInstanceTicked, WorkflowStageSet, WorkflowStageTransitioned, WorkflowStateDivergedError, abortReason, acceptsDocumentType, aclPathForResource, actionDisabledDetail, actionRendering, actionVerdict, activityAutonomyOf, actorFulfillsRole, analyzeCondition2 as analyzeCondition, applicableDefinitions, assertReadableModel, assertReaderModelAcknowledgement, atomReadsDataset2 as atomReadsDataset, autonomySummary, availableActions, buildInitialFields, buildSnapshot, checklistLines, classifyPrincipalId, clientConfigFromResource, clientProjectUserDirectory, compileGuard, computeDiffEntries, conditionFieldReadNames, conditionSitesOf, contentDocQuery, contentDraftFallback, contentReleaseName, contextMap, createEngine, createTelemetryIntake, datasetResourceParts, defaultLoggerFactory, definitionDeployedData, definitionLookupGroq, definitionTagsGroq, definitionsListGroq, deniedGuardLabels, deniedGuardRefs, denyingGuards, deployStageGuards, deployedTagsGroq, deriveActivityKind, deriveExecutorClassification, deriveWorkflowAutonomy, describeAtom, describeAutonomyWait, describeCondition, describeDefinition, describeFieldInsight, describeNode, describeSite, describeSiteHeading, diagnoseInputFromEvaluation, diagnoseInstance, diffEntry, displayDescription, displayTitle, documentActionDenials, documentPrefilter, documentStuckCause, driverKind, effectOutputsMap, entryDocRefs, errorMessage, evaluateFromSnapshot, evaluateMutationGuard, evaluateStartFilter, expandResourceAliases, explainCondition2 as explainCondition, explainStartRequirement, extractDocumentId, fieldTreeShape, findActivityNode, findCurrentActivityEntry, findOpenStageEntry, findStageNode, formatRead, gdrFromResource, gdrRef, gdrUri, groupMembershipNames, groupSitesOf, guardMatches, guardsForDefinition, guardsForInstance, guardsForResource, guillemets2 as guillemets, hasSingleSubjectRequirement, hashDefinitionContent, humanize2 as humanize, inFlightFilter, initialFieldIssues, instanceChangesQuery, instanceDocId, instanceGuardQuery, instancePreviewsQuery, instanceWatchesDocument, instancesGuardQuery, instancesQuery, isCascadeFired, isClaimExpired, isClientProjectUser, isComparisonOp, isDefinitionApplicable, isFilterScopedOut, isGdr, isInputSourced, isNotesEntry, isProjectUserNotFoundError, isRevisionConflict, isSingleDocRefEntry, isSingleDocRefKind, isStartableDefinition, isSubjectEntry, isTelemetryEnvDenied, isTerminalActivityStatus, isTerminalStage, isTodoListEntry, isTodoListItem, isUnprimed, lakeGuardId, lakePrincipalId, latestDefinitionsGroq, latestDeployedDefinitions, lintEffectOutputs, minReaderModelOf, missingRequiredInputs, modelVersionOf, narrateAutonomyWaits, noopTelemetry, parentRef, parseDefinitionInput, parseDefinitionSnapshot, parseDefinitionSnapshotValue, parseGdr, parseGuardDocument, parseInstanceDocument, parseInstancePreviewDocument, parseResourceGdr, processShellUserProperties, projectStartSliceRow, projectToWatchRef, quoted2 as quoted, readInstanceDoc, readInstancePreviewDoc, readsRaw, readsRootDocument, refCanvas, refDashboard, refDataset, refKindAcceptsTypes, refMediaLibrary, refsOf, rejectedRefTypes, releaseDocId, releaseRef, remediationsFor, requiredDefinitionReaderModel, requiredModelFeatures, requiredReaderModel, resolveAccess, resolveActor, resolveClientActor, resolveFieldEntry$1 as resolveFieldEntry, resolveUserAttributes, resourceAliasesToMap, resourceFromParsed, resourceGdr, retractStageGuards, sameResource, scalarValidationIssues, schemaTreeShape, sentenceCase, silentLogger, singleSubjectRequirementRefused, stageAutonomyOf, startFieldsParam, startKindOf, startRefusal, stripSystemFields, subjectDenialLabels, subscriptionDocument, subscriptionDocumentsForInstance, sweepStaleClaims, tagScopeFilter, terminalState, toBareId, tryParseGdr, unboundRequirementReads, unsatisfiedTransitionSummaries, userLoginProvider, validateDefinition, validateTag, verdictGuardsForInstance, wallClock, whatIfCondition2 as whatIfCondition, withAssignment, workflow };
15045
+ export { ACTION_SEMANTICS, ACTIVITY_KINDS, ACTIVITY_KIND_DISPLAY, ACTOR_KINDS, ANONYMOUS_IDENTITY, AUTHORING_DISPLAY, ActionDisabledError, ActionParamsInvalidError, CONDITION_VARS, CONTEXT_ENTRY_DISPLAY, CascadeLimitError, ConcurrentCascadeError, ConcurrentCommitEffectOpsError, ConcurrentCompleteEffectError, ConcurrentEditFieldError, ConcurrentFireActionError, ContractViolationError, DATA_MODEL_CHANGES, DATA_MODEL_MAX_READER, DATA_MODEL_MIN_READER, DATA_MODEL_VERSION, DECISION_SEMANTICS, DEFAULT_CONTENT_PERSPECTIVE, DEFAULT_EFFECT_LEASE_MS, DEFAULT_IDEMPOTENCY_TTL_MS, DEFAULT_TRANSITION_WHEN, DISPLAY, DOCUMENT_VALUE_PERMISSIONS, DRIVER_KINDS, DRIVER_KIND_DISPLAY, DefinitionInUseError, DefinitionNotFoundError, EFFECT_COMMIT_DISPATCH_CAP, EFFECT_COMMIT_QUEUE_DEPTH, ENGINE_API_VERSION, EXECUTION_KINDS, EXECUTOR_CLASSIFICATIONS, EXECUTOR_CLASSIFICATION_DISPLAY, EditFieldDeniedError, EffectCommitQueueOverflowError, EffectNotFoundError, EffectOpsInvalidError, EffectOutputsInvalidError, FIELD_KIND_DISPLAY, FILTER_SCOPE_VARS, FieldValueShapeError, GROUP_KINDS, GROUP_KIND_DISPLAY, GUARD_DOC_TYPE, GUARD_OWNER, GUARD_PREDICATE_VARS, HISTORY_DISPLAY, InitialFieldsInvalidError, InstanceNotFoundError, MAX_COUNTERFACTUAL_INDEX2 as MAX_COUNTERFACTUAL_INDEX, MissingHandlerError, ModelVersionAheadError, MutationGuardDeniedError, MutationGuardDocSchema, NEUTRAL_MARK, OP_DISPLAY, OUTCOME_MARKS, PartialGuardDeployError, PersistedDocShapeError, READER_MODEL_ROLLOUT_URL, RESERVED_CONDITION_VARS, ReaderModelAcknowledgementError, RefResourceUndeclaredError, RequiredFieldNotProvidedError, SIGNAL_SEMANTICS, START_FILTER_VARS, START_REQUIREMENT_VARS, SYSTEM_IDENTITY, SpawnContractsInvalidError, StaleEffectClaimError, StartNotAllowedError, StartNotPrimedError, StartNotSettledError, WORKFLOW_DEFINITION_TYPE, WORKFLOW_INSTANCE_TYPE, WorkflowActionFired, WorkflowActivityReset, WorkflowDefinitionDeleted, WorkflowDefinitionDeployed, WorkflowEffectCompleted, WorkflowEffectStateReported, WorkflowEffectsDrained, WorkflowError, WorkflowFieldEdited, WorkflowInstanceAborted, WorkflowInstancePreviewSchema, WorkflowInstanceSchema, WorkflowInstanceStarted, WorkflowInstanceTicked, WorkflowStageSet, WorkflowStageTransitioned, WorkflowStateDivergedError, abortReason, acceptsDocumentType, aclPathForResource, actionDisabledDetail, actionRendering, actionVerdict, activeAssignmentMembers, activityAutonomyOf, actorFulfillsRole, actorMatchesAssignment, analyzeCondition2 as analyzeCondition, applicableDefinitions, assertReadableModel, assertReaderModelAcknowledgement, assignmentMatch, assignmentMembers, assignmentPrefilter, assignmentState, assignmentStateCounts, atomReadsDataset2 as atomReadsDataset, autonomySummary, availableActions, buildInitialFields, buildSnapshot, checklistLines, classifyPrincipalId, clientConfigFromResource, clientGuardDereference, clientProjectUserDirectory, compileGuards, computeDiffEntries, conditionFieldReadNames, conditionSitesOf, contentDocQuery, contentDraftFallback, contentReleaseName, contextMap, createEngine, createTelemetryIntake, datasetResourceParts, defaultLoggerFactory, definitionDeployedData, definitionLookupGroq, definitionRoleNames, definitionTagsGroq, definitionsListGroq, deniedGuardLabels, deniedGuardRefs, denyingGuards, deployStageGuards, deployedTagsGroq, deriveActivityKind, deriveExecutorClassification, deriveWorkflowAutonomy, describeAtom, describeAutonomyWait, describeCondition, describeDefinition, describeFieldInsight, describeNode, describeSite, describeSiteHeading, diagnoseInputFromEvaluation, diagnoseInstance, diffEntry, displayDescription, displayTitle, documentActionDenials, documentPrefilter, documentStuckCause, driverKind, effectOutputsMap, entryDocRefs, errorMessage, evaluateFromSnapshot, evaluateMutationGuard, evaluateStartFilter, expandResourceAliases, explainCondition2 as explainCondition, explainStartRequirement, extractDocumentId, fieldTreeShape, findActivityNode, findCurrentActivityEntry, findOpenStageEntry, findStageNode, formatRead2 as formatRead, gdrFromResource, gdrRef, gdrUri, groupMembershipNames, groupSitesOf, guardMatches, guardsForDefinition, guardsForInstance, guardsForResource, guillemets2 as guillemets, hasSingleSubjectRequirement, hashDefinitionContent, humanize2 as humanize, identityMatchesAssignment, inFlightFilter, initialFieldIssues, instanceAssignmentStateCounts, instanceChangesQuery, instanceDocId, instanceGuardQuery, instancePreviewsQuery, instanceWatchesDocument, instancesGuardQuery, instancesQuery, isCascadeFired, isClaimExpired, isClientProjectUser, isComparisonOp, isDefinitionApplicable, isFilterScopedOut, isGdr, isInputSourced, isNotesEntry, isProjectUserNotFoundError, isRevisionConflict, isSingleDocRefEntry, isSingleDocRefKind, isStartableDefinition, isSubjectEntry, isTelemetryEnvDenied, isTerminalActivityStatus, isTerminalStage, isTodoListEntry, isTodoListItem, isUnprimed, lakeGuardId, lakePrincipalId, latestDefinitionsGroq, latestDeployedDefinitions, lintEffectOutputs, minReaderModelOf, missingRequiredInputs, modelVersionOf, narrateAutonomyWaits, noopTelemetry, openActivityAssignments, parentRef, parseDefinitionInput, parseDefinitionSnapshot, parseDefinitionSnapshotValue, parseGdr, parseGuardDocument, parseInstanceDocument, parseInstancePreviewDocument, parseResourceGdr, processShellUserProperties, projectStartSliceRow, projectToWatchRef, quoted2 as quoted, readInstanceDoc, readInstancePreviewDoc, readsRaw, readsRootDocument, refCanvas, refDashboard, refDataset, refKindAcceptsTypes, refMediaLibrary, refsOf, rejectedRefTypes, releaseDocId, releaseRef, remediationsFor, requiredDefinitionReaderModel, requiredModelFeatures, requiredReaderModel, resolveAccess, resolveActor, resolveClientActor, resolveFieldEntry$1 as resolveFieldEntry, resolveUserAttributes, resourceAliasesToMap, resourceFromParsed, resourceGdr, retractStageGuards, sameResource, scalarValidationIssues, schemaTreeShape, sentenceCase, silentLogger, singleSubjectRequirementRefused, snapshotGuardDereference, stageAutonomyOf, startFieldsParam, startKindOf, startRefusal, stripSystemFields, subjectDenialLabels, subscriptionDocument, subscriptionDocumentsForInstance, sweepStaleClaims, tagScopeFilter, terminalState, toBareId, tryParseGdr, unboundRequirementReads, unsatisfiedTransitionSummaries, userLoginProvider, validateDefinition, validateTag, verdictGuardsForInstance, wallClock, whatIfCondition2 as whatIfCondition, withAssignment, workflow };