@sanity/workflow-engine 0.31.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.cjs CHANGED
@@ -4,7 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: !0
5
5
  });
6
6
 
7
- var invariants = require("./_chunks-cjs/invariants.cjs"), groqConditionDescribe = require("@sanity/groq-condition-describe"), groqJs = require("groq-js"), v = require("valibot");
7
+ var invariants = require("./_chunks-cjs/invariants.cjs"), groqConditionDescribe = require("@sanity/groq-condition-describe"), groqJs = require("groq-js"), idUtils = require("@sanity/id-utils"), v = require("valibot");
8
8
 
9
9
  function _interopNamespaceCompat(e) {
10
10
  if (e && typeof e == "object" && "default" in e) return e;
@@ -24,37 +24,6 @@ function _interopNamespaceCompat(e) {
24
24
 
25
25
  var v__namespace = /* @__PURE__ */ _interopNamespaceCompat(v);
26
26
 
27
- function isFilterScopedOut(entry) {
28
- return entry.status === "skipped" && entry.startedAt === void 0;
29
- }
30
-
31
- function liveSubworkflows(host) {
32
- return (host.subworkflows ?? []).filter(row => row.resolved === void 0);
33
- }
34
-
35
- function resolvedChildStatus(child) {
36
- if (!(child.completedAt === void 0 || child.completedAt === null)) return child.abortedAt !== void 0 && child.abortedAt !== null ? "aborted" : "done";
37
- }
38
-
39
- function condemnedSubworkflows(host) {
40
- return liveSubworkflows(host).filter(row => row.abortPending !== void 0);
41
- }
42
-
43
- function condemnSubworkflow(row, owed) {
44
- row.abortPending !== void 0 || row.resolved !== void 0 || (row.abortPending = {
45
- at: owed.at,
46
- reason: owed.reason
47
- });
48
- }
49
-
50
- function findOpenStageEntry(host) {
51
- return host.stages.find(s => s.name === host.currentStage && s.exitedAt === void 0);
52
- }
53
-
54
- function findCurrentActivityEntry(host, activityName) {
55
- return findOpenStageEntry(host)?.activities.find(a => a.name === activityName);
56
- }
57
-
58
27
  const WORKFLOW_INSTANCE_TYPE = "sanity.workflow.instance";
59
28
 
60
29
  function terminalState(instance) {
@@ -189,7 +158,7 @@ function fieldWindowOpen(instance, site) {
189
158
  if (site.scope !== "activity") return {
190
159
  open: !0
191
160
  };
192
- const status = findCurrentActivityEntry(instance, site.activity)?.status;
161
+ const status = invariants.findCurrentActivityEntry(instance, site.activity)?.status;
193
162
  return status === "active" ? {
194
163
  open: !0
195
164
  } : {
@@ -208,7 +177,7 @@ function resolveFieldEntry$1(instance, site) {
208
177
 
209
178
  function fieldEntryHost(instance, site) {
210
179
  if (site.scope === "workflow") return instance.fields;
211
- const stageEntry = findOpenStageEntry(instance);
180
+ const stageEntry = invariants.findOpenStageEntry(instance);
212
181
  return site.scope === "stage" ? stageEntry?.fields : stageEntry?.activities.find(t => t.name === site.activity)?.fields;
213
182
  }
214
183
 
@@ -390,7 +359,7 @@ function instanceStartedDataFor(args) {
390
359
  }
391
360
 
392
361
  function stageTransitionedData(args) {
393
- const stageIndex = name => args.definition.stages.findIndex(stage => stage.name === name), openEntry = findOpenStageEntry(args.instance), dwellMs = openEntry !== void 0 ? Date.parse(args.at) - Date.parse(openEntry.enteredAt) : Number.NaN;
362
+ const stageIndex = name => args.definition.stages.findIndex(stage => stage.name === name), openEntry = invariants.findOpenStageEntry(args.instance), dwellMs = openEntry !== void 0 ? Date.parse(args.at) - Date.parse(openEntry.enteredAt) : Number.NaN;
394
363
  return {
395
364
  ...definitionHashFragment(args.instance.pinnedContentHash),
396
365
  instanceId: args.instance._id,
@@ -472,6 +441,11 @@ function rolesGateOf(atom) {
472
441
  if (!(count.type !== "FuncCall" || count.name !== "count" || count.args.length !== 1)) return rolesFilterList(count.args[0]);
473
442
  }
474
443
 
444
+ function isAssignedGate(atom) {
445
+ const node = groqConditionDescribe.atomNode(atom);
446
+ return !atom.negated && node.type === "Parameter" && node.name === "assigned";
447
+ }
448
+
475
449
  function rolesFilterList(node) {
476
450
  if (node.type !== "Filter") return;
477
451
  const base = node.base;
@@ -1760,27 +1734,38 @@ function startContextSyntaxIssues({groq: groq, vars: vars, context: context, ski
1760
1734
  return [ ...invariants.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(", "));
1761
1735
  }
1762
1736
 
1763
- function getPath(value, path) {
1764
- let current = value;
1765
- for (const part of path.split(".")) {
1766
- if (current == null || typeof current != "object") return;
1767
- current = current[part];
1737
+ const GUARD_DOC_TYPE = "temp.system.guard";
1738
+
1739
+ class MutationGuardDeniedError extends invariants.WorkflowError {
1740
+ denied;
1741
+ documentId;
1742
+ action;
1743
+ constructor(args) {
1744
+ const ids = args.denied.map(d => d.guardId).join(", ");
1745
+ super("mutation-guard-denied", `Mutation on "${args.documentId}" (${args.action}) denied by guard(s) [${ids}]`),
1746
+ this.name = "MutationGuardDeniedError", this.denied = args.denied, this.documentId = args.documentId,
1747
+ this.action = args.action;
1748
+ }
1749
+ static fromGuards(args) {
1750
+ return new MutationGuardDeniedError({
1751
+ documentId: args.documentId,
1752
+ action: args.action,
1753
+ denied: deniedGuardRefs(args.guards)
1754
+ });
1768
1755
  }
1769
- return current;
1770
1756
  }
1771
1757
 
1772
- function isReferenceObject(value) {
1773
- return typeof value._ref == "string";
1758
+ function deniedGuardRefs(guards) {
1759
+ return guards.map(g => ({
1760
+ guardId: g._id,
1761
+ ...g.name !== void 0 ? {
1762
+ name: g.name
1763
+ } : {}
1764
+ }));
1774
1765
  }
1775
1766
 
1776
- function walkDocPath(value, path) {
1777
- let current = value;
1778
- for (const part of path.split(".")) {
1779
- if (current == null || typeof current != "object") return;
1780
- 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.`);
1781
- current = current[part];
1782
- }
1783
- return current;
1767
+ function deniedGuardLabels(denied) {
1768
+ return denied.map(d => d.name ?? d.guardId);
1784
1769
  }
1785
1770
 
1786
1771
  function mapJsonStrings(value, transform) {
@@ -1849,205 +1834,664 @@ function overlayInstanceInSnapshot(snapshot, instance) {
1849
1834
  };
1850
1835
  }
1851
1836
 
1852
- function resolveFieldRead(args) {
1853
- const {kind: kind, value: value, path: path, snapshot: snapshot, targetKind: targetKind} = args;
1854
- if (invariants.isSingleDocRefKind(kind)) {
1855
- if (!invariants.isGdr(value)) return null;
1856
- if (path === void 0 && targetKind !== void 0 && invariants.isSingleDocRefKind(targetKind)) return value;
1857
- const base = derefBase(value, snapshot);
1858
- return path === void 0 ? base : walkDocPath(base, path);
1859
- }
1860
- return path !== void 0 ? getPath(value, path) : value;
1837
+ function lakeGuardId(args) {
1838
+ return `${GUARD_DOC_TYPE}.${args.instanceDocId}.${args.guardName}`;
1861
1839
  }
1862
1840
 
1863
- function derefBase(ref, snapshot) {
1864
- return (snapshot !== void 0 ? findSnapshotDoc(snapshot, ref.id) : void 0) ?? {
1865
- _id: ref.id,
1866
- _type: ref.type
1867
- };
1841
+ function lakeActionsFor(action) {
1842
+ return action === "publish" ? [ "create", "update" ] : action === "unpublish" ? [ "delete" ] : [ action ];
1868
1843
  }
1869
1844
 
1870
- function buildParams(args) {
1871
- const {instance: instance, now: now, snapshot: snapshot, extra: extra} = args, currentActivities2 = findOpenStageEntry(instance)?.activities ?? [];
1872
- return {
1873
- self: invariants.selfGdr(instance),
1874
- fields: renderedFields(instance.fields ?? [], snapshot),
1875
- parent: parentRef(instance)?.id ?? null,
1876
- ancestors: instance.ancestors.map(a => a.id),
1877
- stage: instance.currentStage,
1878
- now: now,
1879
- context: contextMap(instance),
1880
- effects: effectOutputsMap(instance),
1881
- effectStatus: effectStatusMap(instance),
1882
- activities: currentActivities2,
1883
- subworkflows: subworkflowVarRows(instance, snapshot),
1884
- ...activityGateParams(currentActivities2),
1885
- ...extra
1886
- };
1845
+ function lakeGuardActions(actions) {
1846
+ const requested = new Set(actions.flatMap(lakeActionsFor));
1847
+ return invariants.LAKE_MUTATION_GUARD_ACTIONS.filter(action => requested.has(action));
1887
1848
  }
1888
1849
 
1889
- function subworkflowVarRows(instance, snapshot) {
1890
- const openKey = findOpenStageEntry(instance)?._key;
1891
- return (instance.subworkflows ?? []).map(row => ({
1892
- _id: invariants.toBareId(row.ref.id),
1893
- rowKey: row.rowKey,
1894
- activity: row.activity,
1895
- action: row.action,
1896
- definition: row.definition,
1897
- current: row.stageEntry === openKey,
1898
- ...childState(row, snapshot),
1899
- spawnedAt: row.spawnedAt
1900
- }));
1850
+ function publishedId(id) {
1851
+ return idUtils.getPublishedId(id);
1901
1852
  }
1902
1853
 
1903
- function childState(row, snapshot) {
1904
- if (row.resolved !== void 0) return {
1905
- stage: row.resolved.stage ?? null,
1906
- status: row.resolved.aborted === !0 ? "aborted" : "done"
1854
+ function draftId(id) {
1855
+ return idUtils.getDraftId(id);
1856
+ }
1857
+
1858
+ function versionPatternTranslation(pattern) {
1859
+ const documentSeparator = pattern.indexOf(".", 9);
1860
+ if (documentSeparator === -1) return {
1861
+ issue: `id pattern "${pattern}" does not identify a versioned document`
1907
1862
  };
1908
- const doc = snapshot === void 0 ? void 0 : findSnapshotDoc(snapshot, row.ref.id);
1909
- return doc === void 0 ? {
1910
- stage: null,
1911
- status: "active"
1863
+ const documentPattern = pattern.slice(documentSeparator + 1);
1864
+ return /^\*+$/.test(documentPattern) ? {
1865
+ issue: `id pattern "${pattern}" cannot be translated from a release version to a draft or published document without broadening it to every document`
1912
1866
  } : {
1913
- stage: doc.currentStage ?? null,
1914
- status: resolvedChildStatus(doc) ?? "active"
1867
+ documentPattern: documentPattern
1915
1868
  };
1916
1869
  }
1917
1870
 
1918
- function activityGateParams(activities) {
1919
- return {
1920
- allActivitiesDone: activities.every(activity => activity.status === "done" || activity.status === "skipped"),
1921
- anyActivityFailed: activities.some(activity => activity.status === "failed")
1922
- };
1871
+ function publishedPattern(pattern) {
1872
+ if (pattern.startsWith("drafts.")) return pattern.slice(7);
1873
+ if (!pattern.startsWith("versions.")) return pattern;
1874
+ const translated = versionPatternTranslation(pattern);
1875
+ if (translated.issue !== void 0) throw new Error(translated.issue);
1876
+ return translated.documentPattern;
1923
1877
  }
1924
1878
 
1925
- function renderedFields(entries, snapshot) {
1926
- const out = {};
1927
- for (const entry of entries) out[entry.name] = renderedValue(entry, snapshot);
1928
- return out;
1879
+ function draftPattern(pattern) {
1880
+ return `drafts.${publishedPattern(pattern)}`;
1929
1881
  }
1930
1882
 
1931
- function renderedValue(entry, snapshot) {
1932
- return resolveFieldRead({
1933
- kind: entry._type,
1934
- value: entry.value,
1935
- path: void 0,
1936
- snapshot: snapshot
1937
- });
1883
+ function idsInSpace(args) {
1884
+ const {ids: ids, space: space, sourceInstanceId: sourceInstanceId} = args;
1885
+ return space === "authored" ? ids : space === "published" ? [ ...new Set(ids.map(publishedId)) ] : [ ...new Set(ids.map(id => publishedId(id) === sourceInstanceId ? publishedId(id) : draftId(id))) ];
1938
1886
  }
1939
1887
 
1940
- function scopedFieldOverlay({instance: instance, snapshot: snapshot, activityName: activityName}) {
1941
- const stageEntry = findOpenStageEntry(instance);
1942
- if (stageEntry === void 0) return {};
1943
- const stageFields = renderedFields(stageEntry.fields ?? [], snapshot), activity = activityName ? stageEntry.activities.find(t => t.name === activityName) : void 0;
1888
+ function patternsInSpace(patterns, space) {
1889
+ if (space === "authored") return patterns;
1890
+ const convert = space === "edit" ? draftPattern : publishedPattern;
1891
+ return [ ...new Set(patterns.map(convert)) ];
1892
+ }
1893
+
1894
+ function matchInSpace(args) {
1895
+ const {match: match, actions: actions, space: space, sourceInstanceId: sourceInstanceId} = args;
1944
1896
  return {
1945
- ...stageFields,
1946
- ...renderedFields(activity?.fields ?? [], snapshot)
1897
+ ...match,
1898
+ ...match.idRefs !== void 0 ? {
1899
+ idRefs: idsInSpace({
1900
+ ids: match.idRefs,
1901
+ space: space,
1902
+ sourceInstanceId: sourceInstanceId
1903
+ })
1904
+ } : {},
1905
+ ...match.idPatterns !== void 0 ? {
1906
+ idPatterns: patternsInSpace(match.idPatterns, space)
1907
+ } : {},
1908
+ actions: lakeGuardActions(actions)
1947
1909
  };
1948
1910
  }
1949
1911
 
1950
- function assignedFor({instance: instance, activityName: activityName, actor: actor, roleAliases: roleAliases}) {
1951
- if (actor === void 0) return !1;
1952
- const entry = findCurrentActivityEntry(instance, activityName)?.fields?.find(s => s._type === "assignees");
1953
- return entry === void 0 ? !1 : entry.value.some(a => a.type === "user" ? a.id === actor.id : invariants.actorFulfillsRole({
1954
- actorRoles: actor.roles,
1955
- required: a.role,
1956
- aliases: roleAliases
1912
+ function guardPartitions(id, actions) {
1913
+ return invariants.MUTATION_GUARD_ID_SPACES.flatMap(space => {
1914
+ const matchingActions = actions.filter(action => invariants.mutationGuardActionIdSpace(action) === space);
1915
+ return matchingActions.length > 0 ? [ {
1916
+ space: space,
1917
+ actions: matchingActions
1918
+ } ] : [];
1919
+ }).map((partition, index) => ({
1920
+ ...partition,
1921
+ id: index === 0 ? id : `${id}.${partition.space}`
1957
1922
  }));
1958
1923
  }
1959
1924
 
1960
- function effectStatusMap(instance) {
1961
- const entry = findOpenStageEntry(instance);
1962
- if (entry === void 0) return {};
1963
- const out = {};
1964
- for (const run of instance.effectHistory) run.stageEntryKey === entry._key && (out[run.name] = run.status);
1965
- return out;
1925
+ function compiledGuardIds(args) {
1926
+ return guardPartitions(args.id, args.actions).map(partition => partition.id);
1966
1927
  }
1967
1928
 
1968
- function contextMap(instance) {
1969
- const out = {};
1970
- for (const entry of instance.context) out[entry.name] = entry._type === "context.json" ? parseJsonContextEntry(entry) : entry.value;
1971
- return out;
1929
+ function lakeGuardDocument(args) {
1930
+ return {
1931
+ _id: args.id,
1932
+ _type: GUARD_DOC_TYPE,
1933
+ resourceType: args.resourceType,
1934
+ resourceId: args.resourceId,
1935
+ owner: args.owner,
1936
+ sourceInstanceId: args.sourceInstanceId,
1937
+ sourceDefinition: args.sourceDefinition,
1938
+ sourceStage: args.sourceStage,
1939
+ ...args.name !== void 0 ? {
1940
+ name: args.name
1941
+ } : {},
1942
+ ...args.description !== void 0 ? {
1943
+ description: args.description
1944
+ } : {},
1945
+ match: args.match,
1946
+ predicate: args.predicate,
1947
+ metadata: args.metadata
1948
+ };
1972
1949
  }
1973
1950
 
1974
- function effectOutputsMap(instance) {
1975
- const out = {};
1976
- for (const run of instance.effectHistory) run.outputs !== void 0 && (out[run.name] = run.outputs);
1977
- return out;
1951
+ function compileGuards(args) {
1952
+ const [first, ...remaining] = guardPartitions(args.id, args.match.actions);
1953
+ if (first === void 0) throw new Error("cannot compile a guard without an action");
1954
+ const compilePartition = ({space: space, actions: actions, id: id}) => lakeGuardDocument({
1955
+ ...args,
1956
+ id: id,
1957
+ match: matchInSpace({
1958
+ match: args.match,
1959
+ actions: actions,
1960
+ space: space,
1961
+ sourceInstanceId: args.sourceInstanceId
1962
+ })
1963
+ });
1964
+ return [ compilePartition(first), ...remaining.map(compilePartition) ];
1978
1965
  }
1979
1966
 
1980
- function parseJsonContextEntry(entry) {
1981
- try {
1982
- return JSON.parse(entry.value);
1983
- } catch (err) {
1984
- invariants.rethrowWithContext(err, `context entry "${entry.name}" holds unparseable JSON`);
1985
- }
1967
+ function globMatch(pattern, value) {
1968
+ if (!pattern.includes("*")) return pattern === value;
1969
+ const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
1970
+ return new RegExp(`^${escaped}$`).test(value);
1986
1971
  }
1987
1972
 
1988
- function paramsForLake(params) {
1989
- return {
1990
- ...params,
1991
- self: typeof params.self == "string" ? invariants.toBareId(params.self) : params.self,
1992
- parent: typeof params.parent == "string" ? invariants.toBareId(params.parent) : params.parent,
1993
- ancestors: Array.isArray(params.ancestors) ? params.ancestors.map(a => typeof a == "string" ? invariants.toBareId(a) : a) : params.ancestors,
1994
- fields: stripFieldsForLake(params.fields)
1995
- };
1973
+ function guardIdPatternIssue(pattern, actions) {
1974
+ const representativeId = pattern.replaceAll("*", "x");
1975
+ if (!invariants.isBareDocumentId(representativeId)) return `id pattern "${pattern}" is not a valid resource-local document-id glob; use document-id characters (letters, digits, \`_\`, \`-\`, \`.\`) and \`*\` wildcards`;
1976
+ if (actions?.some(action => invariants.mutationGuardActionIdSpace(action) !== "authored") && pattern.startsWith("versions.")) return versionPatternTranslation(pattern).issue;
1996
1977
  }
1997
1978
 
1998
- function stripFieldsForLake(value) {
1999
- return mapJsonStrings(value, s => invariants.toBareId(s));
1979
+ function parseGuardPredicate(predicate) {
1980
+ return groqJs.parse(predicate, {
1981
+ mode: "delta"
1982
+ });
2000
1983
  }
2001
1984
 
2002
- function resolveStaticValueExpr(src, ctx) {
2003
- switch (src.type) {
2004
- case "literal":
2005
- return src.value;
2006
-
2007
- case "param":
2008
- return ctx.params?.[src.param];
1985
+ function guardPredicateSyntaxIssue(predicate) {
1986
+ try {
1987
+ parseGuardPredicate(predicate);
1988
+ return;
1989
+ } catch (error) {
1990
+ return invariants.errorMessage(error);
1991
+ }
1992
+ }
2009
1993
 
2010
- case "actor":
2011
- return ctx.actor;
1994
+ function lakeDocumentIdForAction(id, action) {
1995
+ return invariants.mutationGuardActionIdSpace(action) === "published" ? publishedId(id) : id;
1996
+ }
2012
1997
 
2013
- case "now":
2014
- return ctx.now;
1998
+ function legacyLifecycleActionFor(action, documentId) {
1999
+ if (publishedId(documentId) === documentId) {
2000
+ if (action === "create" || action === "update") return "publish";
2001
+ if (action === "delete") return "unpublish";
2015
2002
  }
2016
2003
  }
2017
2004
 
2018
- function randomKey(length = 12) {
2019
- const bytes = new Uint8Array(length);
2020
- return globalThis.crypto.getRandomValues(bytes), [ ...bytes ].map(b => b.toString(16).padStart(2, "0")).join("").slice(0, length);
2005
+ function guardMatches({guard: guard, doc: doc, action: action}) {
2006
+ const m = guard.match, documentId = lakeDocumentIdForAction(doc.id, action), legacyLifecycleAction = legacyLifecycleActionFor(action, documentId);
2007
+ 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;
2008
+ if (m.idRefs && m.idRefs.length > 0 || m.idPatterns && m.idPatterns.length > 0) {
2009
+ const byRef = m.idRefs?.includes(documentId) ?? !1, byPattern = m.idPatterns?.some(p => globMatch(p, documentId)) ?? !1;
2010
+ if (!byRef && !byPattern) return !1;
2011
+ }
2012
+ return !0;
2021
2013
  }
2022
2014
 
2023
- function instanceDocId(tag) {
2024
- return `${tag}.wf-instance.${randomKey()}`;
2015
+ function guardPredicateRoot(args) {
2016
+ return {
2017
+ document: {
2018
+ before: args.context.before,
2019
+ after: args.context.after
2020
+ },
2021
+ guard: args.guard,
2022
+ mutation: {
2023
+ action: args.context.action
2024
+ }
2025
+ };
2025
2026
  }
2026
2027
 
2027
- function mapFieldRefValues(args) {
2028
- const {entryType: entryType, value: value, fields: fields, of: of, mapRef: mapRef} = args;
2029
- return value == null ? value : invariants.isSingleDocRefKind(entryType) || entryType === "release.ref" ? mapRef(value) : entryType === "doc.refs" ? mapItems(value, mapRef) : entryType === "object" ? mapRowRefValues({
2030
- row: value,
2031
- shapes: fields ?? [],
2032
- mapRef: mapRef
2033
- }) : entryType === "array" ? mapItems(value, row => mapRowRefValues({
2034
- row: row,
2035
- shapes: of ?? [],
2036
- mapRef: mapRef
2037
- })) : value;
2028
+ function lakeShapedSnapshotDocument(doc) {
2029
+ return mapJsonStrings(doc, (value, key) => key === "_id" || key === "_ref" ? invariants.toBareId(value) : value);
2038
2030
  }
2039
2031
 
2040
- function mapItems(value, mapItem) {
2041
- return Array.isArray(value) ? value.map(mapItem) : value;
2032
+ function snapshotGuardDereference(args) {
2033
+ return ({_ref: _ref}) => {
2034
+ const lookupId = invariants.isGdrUri(_ref) ? _ref : invariants.gdrFromResource(args.resource, _ref), found = findSnapshotDoc(args.snapshot, lookupId);
2035
+ return Promise.resolve(found === void 0 ? null : lakeShapedSnapshotDocument(found));
2036
+ };
2042
2037
  }
2043
2038
 
2044
- function mapRowRefValues(args) {
2045
- const {row: row, shapes: shapes, mapRef: mapRef} = args;
2046
- if (typeof row != "object" || row === null || Array.isArray(row)) return row;
2047
- const record = row, out = {
2048
- ...record
2049
- };
2050
- for (const shape of shapes) shape.name in record && (out[shape.name] = mapFieldRefValues({
2039
+ async function evaluateMutationGuard(args) {
2040
+ const {guard: guard, context: context, dereference: dereference} = args;
2041
+ if (guard.predicate === "") return !1;
2042
+ const root = guardPredicateRoot({
2043
+ guard: guard,
2044
+ context: context
2045
+ });
2046
+ let dereferenceFailed = !1;
2047
+ const guardedDereference = dereference === void 0 ? void 0 : async ref => {
2048
+ try {
2049
+ return await dereference(ref);
2050
+ } catch (error) {
2051
+ throw dereferenceFailed = !0, error;
2052
+ }
2053
+ };
2054
+ try {
2055
+ const tree = parseGuardPredicate(guard.predicate);
2056
+ return await (await groqJs.evaluate(tree, {
2057
+ root: root,
2058
+ before: context.before,
2059
+ after: context.after,
2060
+ ...guardedDereference !== void 0 ? {
2061
+ dataset: [],
2062
+ dereference: guardedDereference
2063
+ } : {},
2064
+ ...context.identity !== void 0 ? {
2065
+ identity: context.identity
2066
+ } : {}
2067
+ })).get() === !0;
2068
+ } catch (error) {
2069
+ if (dereferenceFailed) throw error;
2070
+ return !1;
2071
+ }
2072
+ }
2073
+
2074
+ async function denyingGuards(args) {
2075
+ const {guards: guards, doc: doc, context: context, dereference: dereference} = args, denied = [];
2076
+ for (const guard of guards) guardMatches({
2077
+ guard: guard,
2078
+ doc: doc,
2079
+ action: context.action
2080
+ }) && (await evaluateMutationGuard({
2081
+ guard: guard,
2082
+ context: context,
2083
+ ...dereference !== void 0 ? {
2084
+ dereference: dereference
2085
+ } : {}
2086
+ }) || denied.push(guard));
2087
+ return denied;
2088
+ }
2089
+
2090
+ async function documentActionDenials(args) {
2091
+ const {mutation: mutation, resource: resource, guards: guards, identity: identity, dereference: dereference} = args, document = mutation.after ?? mutation.before;
2092
+ if (document === null) throw new Error("a concrete Lake mutation requires a before or after image");
2093
+ const enforceable = guards.filter(g => g.resourceType === resource.type && g.resourceId === resource.id);
2094
+ return enforceable.length === 0 ? [] : denyingGuards({
2095
+ guards: enforceable,
2096
+ doc: {
2097
+ id: document._id,
2098
+ type: document._type
2099
+ },
2100
+ context: {
2101
+ ...mutation,
2102
+ ...identity !== void 0 ? {
2103
+ identity: identity
2104
+ } : {}
2105
+ },
2106
+ ...dereference !== void 0 ? {
2107
+ dereference: dereference
2108
+ } : {}
2109
+ });
2110
+ }
2111
+
2112
+ async function instanceWriteDenials(args) {
2113
+ const {instance: instance, guards: guards, identity: identity, dereference: dereference} = args;
2114
+ return documentActionDenials({
2115
+ mutation: {
2116
+ before: instance,
2117
+ after: instance,
2118
+ action: "update"
2119
+ },
2120
+ resource: instance.workflowResource,
2121
+ guards: guards,
2122
+ dereference: dereference,
2123
+ ...identity !== void 0 ? {
2124
+ identity: identity
2125
+ } : {}
2126
+ });
2127
+ }
2128
+
2129
+ async function assertInstanceWriteAllowed(args) {
2130
+ const denied = await instanceWriteDenials(args);
2131
+ if (denied.length !== 0) throw MutationGuardDeniedError.fromGuards({
2132
+ documentId: args.instance._id,
2133
+ action: "update",
2134
+ guards: denied
2135
+ });
2136
+ }
2137
+
2138
+ const ASSIGNMENT_LIST_MODEL = 9;
2139
+
2140
+ function isAssignmentListModel(modelVersion) {
2141
+ return modelVersion >= ASSIGNMENT_LIST_MODEL;
2142
+ }
2143
+
2144
+ function definitionAssignmentModel(instance) {
2145
+ if (typeof instance.definitionSnapshot != "string") return 0;
2146
+ const modelVersion = parseDefinitionSnapshotValue({
2147
+ _id: instance._id,
2148
+ definitionSnapshot: instance.definitionSnapshot
2149
+ }).modelVersion;
2150
+ return typeof modelVersion == "number" ? modelVersion : 0;
2151
+ }
2152
+
2153
+ function writesSingularAssignmentLists(instance) {
2154
+ return isAssignmentListModel(definitionAssignmentModel(instance));
2155
+ }
2156
+
2157
+ function legacyAssigneeValue(entry) {
2158
+ return legacyAssignmentValue(entry.value, entry.name);
2159
+ }
2160
+
2161
+ function legacyAssignmentValue(value, path) {
2162
+ if (!Array.isArray(value)) return value;
2163
+ if (value.length === 0) return null;
2164
+ if (value.length === 1) return value[0];
2165
+ throw new invariants.ContractViolationError(`legacy assignee field "${path}" cannot persist ${value.length} members; redeploy the definition at the assignment-list reader model before writing a role pool`);
2166
+ }
2167
+
2168
+ function mapShapeValues(args) {
2169
+ const {value: value, shapes: shapes, mapAssignee: mapAssignee, parentPath: parentPath} = args;
2170
+ if (typeof value != "object" || value === null || Array.isArray(value)) return value;
2171
+ const source = value, next = {
2172
+ ...source
2173
+ };
2174
+ for (const shape of shapes) {
2175
+ if (!Object.hasOwn(source, shape.name)) continue;
2176
+ const path = `${parentPath}.${shape.name}`, member = source[shape.name], mapped = mappedShapeValue({
2177
+ shape: shape,
2178
+ member: member,
2179
+ mapAssignee: mapAssignee,
2180
+ path: path
2181
+ });
2182
+ mapped.found && defineOwnValue(next, {
2183
+ name: shape.name,
2184
+ value: mapped.value
2185
+ });
2186
+ }
2187
+ return next;
2188
+ }
2189
+
2190
+ function mappedShapeValue(args) {
2191
+ const {shape: shape, member: member, mapAssignee: mapAssignee, path: path} = args;
2192
+ return shape.type === "assignee" ? {
2193
+ found: !0,
2194
+ value: mapAssignee(member, path)
2195
+ } : shape.type === "object" ? {
2196
+ found: !0,
2197
+ value: mapShapeValues({
2198
+ value: member,
2199
+ shapes: shape.fields ?? [],
2200
+ mapAssignee: mapAssignee,
2201
+ parentPath: path
2202
+ })
2203
+ } : shape.type !== "array" || !Array.isArray(member) ? {
2204
+ found: !1
2205
+ } : {
2206
+ found: !0,
2207
+ value: member.map((row, index) => mapShapeValues({
2208
+ value: row,
2209
+ shapes: shape.of ?? [],
2210
+ mapAssignee: mapAssignee,
2211
+ parentPath: `${path}[${index}]`
2212
+ }))
2213
+ };
2214
+ }
2215
+
2216
+ function defineOwnValue(target, property) {
2217
+ Object.defineProperty(target, property.name, {
2218
+ configurable: !0,
2219
+ enumerable: !0,
2220
+ value: property.value,
2221
+ writable: !0
2222
+ });
2223
+ }
2224
+
2225
+ function mapNestedAssignmentValues(entry, mapAssignee) {
2226
+ return entry._type === "object" ? mapShapeValues({
2227
+ value: entry.value,
2228
+ shapes: entry.fields,
2229
+ mapAssignee: mapAssignee,
2230
+ parentPath: entry.name
2231
+ }) : entry.value.map((row, index) => mapShapeValues({
2232
+ value: row,
2233
+ shapes: entry.of,
2234
+ mapAssignee: mapAssignee,
2235
+ parentPath: `${entry.name}[${index}]`
2236
+ }));
2237
+ }
2238
+
2239
+ function normalizeNestedAssignmentValues(entry) {
2240
+ return entry._type !== "object" && entry._type !== "array" ? entry : {
2241
+ ...entry,
2242
+ value: mapNestedAssignmentValues(entry, invariants.normalizeAssignmentMembers)
2243
+ };
2244
+ }
2245
+
2246
+ function assignmentFieldsForWrite(fields, listModel) {
2247
+ return listModel ? fields : fields.map(entry => entry._type === "assignee" ? {
2248
+ ...entry,
2249
+ value: legacyAssigneeValue(entry)
2250
+ } : entry._type === "object" || entry._type === "array" ? {
2251
+ ...entry,
2252
+ value: mapNestedAssignmentValues(entry, legacyAssignmentValue)
2253
+ } : entry);
2254
+ }
2255
+
2256
+ function activityForWrite(activity, listModel) {
2257
+ return {
2258
+ ...activity,
2259
+ ...activity.fields === void 0 ? {} : {
2260
+ fields: assignmentFieldsForWrite(activity.fields, listModel)
2261
+ }
2262
+ };
2263
+ }
2264
+
2265
+ function assignmentStagesForWrite(stages, listModel) {
2266
+ return stages.map(stage => ({
2267
+ ...stage,
2268
+ fields: assignmentFieldsForWrite(stage.fields ?? [], listModel),
2269
+ activities: stage.activities.map(activity => activityForWrite(activity, listModel))
2270
+ }));
2271
+ }
2272
+
2273
+ function getPath(value, path) {
2274
+ let current = value;
2275
+ for (const part of path.split(".")) {
2276
+ if (current == null || typeof current != "object") return;
2277
+ current = current[part];
2278
+ }
2279
+ return current;
2280
+ }
2281
+
2282
+ function isReferenceObject(value) {
2283
+ return typeof value._ref == "string";
2284
+ }
2285
+
2286
+ function walkDocPath(value, path) {
2287
+ let current = value;
2288
+ for (const part of path.split(".")) {
2289
+ if (current == null || typeof current != "object") return;
2290
+ 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.`);
2291
+ current = current[part];
2292
+ }
2293
+ return current;
2294
+ }
2295
+
2296
+ function resolveFieldRead(args) {
2297
+ const {kind: kind, value: value, path: path, snapshot: snapshot, targetKind: targetKind} = args;
2298
+ if (invariants.isSingleDocRefKind(kind)) {
2299
+ if (!invariants.isGdr(value)) return null;
2300
+ if (path === void 0 && targetKind !== void 0 && invariants.isSingleDocRefKind(targetKind)) return value;
2301
+ const base = derefBase(value, snapshot);
2302
+ return path === void 0 ? base : walkDocPath(base, path);
2303
+ }
2304
+ return path !== void 0 ? getPath(value, path) : value;
2305
+ }
2306
+
2307
+ function derefBase(ref, snapshot) {
2308
+ return (snapshot !== void 0 ? findSnapshotDoc(snapshot, ref.id) : void 0) ?? {
2309
+ _id: ref.id,
2310
+ _type: ref.type
2311
+ };
2312
+ }
2313
+
2314
+ function buildParams(args) {
2315
+ const {instance: instance, now: now, snapshot: snapshot, extra: extra} = args, currentActivities2 = invariants.findOpenStageEntry(instance)?.activities ?? [];
2316
+ return {
2317
+ self: invariants.selfGdr(instance),
2318
+ fields: renderedFields(instance.fields ?? [], snapshot),
2319
+ parent: parentRef(instance)?.id ?? null,
2320
+ ancestors: instance.ancestors.map(a => a.id),
2321
+ stage: instance.currentStage,
2322
+ now: now,
2323
+ context: contextMap(instance),
2324
+ effects: effectOutputsMap(instance),
2325
+ effectStatus: effectStatusMap(instance),
2326
+ activities: currentActivities2,
2327
+ subworkflows: subworkflowVarRows(instance, snapshot),
2328
+ ...activityGateParams(currentActivities2),
2329
+ ...extra
2330
+ };
2331
+ }
2332
+
2333
+ function subworkflowVarRows(instance, snapshot) {
2334
+ const openKey = invariants.findOpenStageEntry(instance)?._key;
2335
+ return (instance.subworkflows ?? []).map(row => ({
2336
+ _id: invariants.toBareId(row.ref.id),
2337
+ rowKey: row.rowKey,
2338
+ activity: row.activity,
2339
+ action: row.action,
2340
+ definition: row.definition,
2341
+ current: row.stageEntry === openKey,
2342
+ ...childState(row, snapshot),
2343
+ spawnedAt: row.spawnedAt
2344
+ }));
2345
+ }
2346
+
2347
+ function childState(row, snapshot) {
2348
+ if (row.resolved !== void 0) return {
2349
+ stage: row.resolved.stage ?? null,
2350
+ status: row.resolved.aborted === !0 ? "aborted" : "done"
2351
+ };
2352
+ const doc = snapshot === void 0 ? void 0 : findSnapshotDoc(snapshot, row.ref.id);
2353
+ return doc === void 0 ? {
2354
+ stage: null,
2355
+ status: "active"
2356
+ } : {
2357
+ stage: doc.currentStage ?? null,
2358
+ status: invariants.resolvedChildStatus(doc) ?? "active"
2359
+ };
2360
+ }
2361
+
2362
+ function activityGateParams(activities) {
2363
+ return {
2364
+ allActivitiesDone: activities.every(activity => activity.status === "done" || activity.status === "skipped"),
2365
+ anyActivityFailed: activities.some(activity => activity.status === "failed")
2366
+ };
2367
+ }
2368
+
2369
+ function renderedFields(entries, snapshot) {
2370
+ const out = {};
2371
+ for (const entry of entries) out[entry.name] = renderedValue(entry, snapshot);
2372
+ return out;
2373
+ }
2374
+
2375
+ function renderedValue(entry, snapshot) {
2376
+ return resolveFieldRead({
2377
+ kind: entry._type,
2378
+ value: entry.value,
2379
+ path: void 0,
2380
+ snapshot: snapshot
2381
+ });
2382
+ }
2383
+
2384
+ function scopedFieldOverlay({instance: instance, snapshot: snapshot, activityName: activityName}) {
2385
+ const stageEntry = invariants.findOpenStageEntry(instance);
2386
+ if (stageEntry === void 0) return {};
2387
+ const stageFields = renderedFields(stageEntry.fields ?? [], snapshot), activity = activityName ? stageEntry.activities.find(t => t.name === activityName) : void 0;
2388
+ return {
2389
+ ...stageFields,
2390
+ ...renderedFields(activity?.fields ?? [], snapshot)
2391
+ };
2392
+ }
2393
+
2394
+ function assignedFor({instance: instance, activityName: activityName, actor: actor, roleAliases: roleAliases}) {
2395
+ if (actor === void 0) return !1;
2396
+ const activity = invariants.findCurrentActivityEntry(instance, activityName);
2397
+ return invariants.actorMatchesAssignment({
2398
+ actor: actor,
2399
+ members: invariants.assignmentMembers(activity?.fields ?? []),
2400
+ roleAliases: roleAliases
2401
+ });
2402
+ }
2403
+
2404
+ function effectStatusMap(instance) {
2405
+ const entry = invariants.findOpenStageEntry(instance);
2406
+ if (entry === void 0) return {};
2407
+ const out = {};
2408
+ for (const run of instance.effectHistory) run.stageEntryKey === entry._key && (out[run.name] = run.status);
2409
+ return out;
2410
+ }
2411
+
2412
+ function contextMap(instance) {
2413
+ const out = {};
2414
+ for (const entry of instance.context) out[entry.name] = entry._type === "context.json" ? parseJsonContextEntry(entry) : entry.value;
2415
+ return out;
2416
+ }
2417
+
2418
+ function effectOutputsMap(instance) {
2419
+ const out = {};
2420
+ for (const run of instance.effectHistory) run.outputs !== void 0 && (out[run.name] = run.outputs);
2421
+ return out;
2422
+ }
2423
+
2424
+ function parseJsonContextEntry(entry) {
2425
+ try {
2426
+ return JSON.parse(entry.value);
2427
+ } catch (err) {
2428
+ invariants.rethrowWithContext(err, `context entry "${entry.name}" holds unparseable JSON`);
2429
+ }
2430
+ }
2431
+
2432
+ function paramsForLake(params) {
2433
+ return {
2434
+ ...params,
2435
+ self: typeof params.self == "string" ? invariants.toBareId(params.self) : params.self,
2436
+ parent: typeof params.parent == "string" ? invariants.toBareId(params.parent) : params.parent,
2437
+ ancestors: Array.isArray(params.ancestors) ? params.ancestors.map(a => typeof a == "string" ? invariants.toBareId(a) : a) : params.ancestors,
2438
+ fields: stripFieldsForLake(params.fields)
2439
+ };
2440
+ }
2441
+
2442
+ function stripFieldsForLake(value) {
2443
+ return mapJsonStrings(value, s => invariants.toBareId(s));
2444
+ }
2445
+
2446
+ function resolveStaticValueExpr(src, ctx) {
2447
+ switch (src.type) {
2448
+ case "literal":
2449
+ return src.value;
2450
+
2451
+ case "param":
2452
+ return ctx.params?.[src.param];
2453
+
2454
+ case "actor":
2455
+ return ctx.actor;
2456
+
2457
+ case "now":
2458
+ return ctx.now;
2459
+ }
2460
+ }
2461
+
2462
+ function randomKey(length = 12) {
2463
+ const bytes = new Uint8Array(length);
2464
+ return globalThis.crypto.getRandomValues(bytes), [ ...bytes ].map(b => b.toString(16).padStart(2, "0")).join("").slice(0, length);
2465
+ }
2466
+
2467
+ function instanceDocId(tag) {
2468
+ return `${tag}.wf-instance.${randomKey()}`;
2469
+ }
2470
+
2471
+ function mapFieldRefValues(args) {
2472
+ const {entryType: entryType, value: value, fields: fields, of: of, mapRef: mapRef} = args;
2473
+ return value == null ? value : invariants.isSingleDocRefKind(entryType) || entryType === "release.ref" ? mapRef(value) : entryType === "doc.refs" ? mapItems(value, mapRef) : entryType === "object" ? mapRowRefValues({
2474
+ row: value,
2475
+ shapes: fields ?? [],
2476
+ mapRef: mapRef
2477
+ }) : entryType === "array" ? mapItems(value, row => mapRowRefValues({
2478
+ row: row,
2479
+ shapes: of ?? [],
2480
+ mapRef: mapRef
2481
+ })) : value;
2482
+ }
2483
+
2484
+ function mapItems(value, mapItem) {
2485
+ return Array.isArray(value) ? value.map(mapItem) : value;
2486
+ }
2487
+
2488
+ function mapRowRefValues(args) {
2489
+ const {row: row, shapes: shapes, mapRef: mapRef} = args;
2490
+ if (typeof row != "object" || row === null || Array.isArray(row)) return row;
2491
+ const record = row, out = {
2492
+ ...record
2493
+ };
2494
+ for (const shape of shapes) shape.name in record && (out[shape.name] = mapFieldRefValues({
2051
2495
  entryType: shape.type,
2052
2496
  value: record[shape.name],
2053
2497
  fields: shape.fields,
@@ -2269,10 +2713,12 @@ class WorkflowStateDivergedError extends invariants.WorkflowError {
2269
2713
  class PartialGuardDeployError extends invariants.WorkflowError {
2270
2714
  stageName;
2271
2715
  deployed;
2716
+ rollbackError;
2272
2717
  constructor(args) {
2273
2718
  super("partial-guard-deploy", `Partial guard deploy on stage "${args.stageName}": ${args.deployed} guard(s) deployed before a later one failed.`, {
2274
2719
  cause: args.cause
2275
- }), this.name = "PartialGuardDeployError", this.stageName = args.stageName, this.deployed = args.deployed;
2720
+ }), this.name = "PartialGuardDeployError", this.stageName = args.stageName, this.deployed = args.deployed,
2721
+ args.rollbackError !== void 0 && (this.rollbackError = args.rollbackError);
2276
2722
  }
2277
2723
  }
2278
2724
 
@@ -2313,7 +2759,23 @@ function staleClaimDetail(reason) {
2313
2759
  const CONCURRENT_COMMIT_MAX_ATTEMPTS = 3;
2314
2760
 
2315
2761
  function lostRaceMessage(args) {
2316
- 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.`;
2762
+ 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."}`;
2763
+ }
2764
+
2765
+ function isRetryableInstanceCommitConflict(error) {
2766
+ return !isCreateIdCollision(error) && isRevisionConflict(error);
2767
+ }
2768
+
2769
+ class InstanceCommitRevisionConflict extends Error {
2770
+ constructor(cause) {
2771
+ super("Instance commit lost its revision fence", {
2772
+ cause: cause
2773
+ }), this.name = "InstanceCommitRevisionConflict";
2774
+ }
2775
+ }
2776
+
2777
+ function isInstanceCommitRevisionConflict(error) {
2778
+ return error instanceof InstanceCommitRevisionConflict;
2317
2779
  }
2318
2780
 
2319
2781
  class ConcurrentFireActionError extends invariants.WorkflowError {
@@ -2331,6 +2793,19 @@ class ConcurrentFireActionError extends invariants.WorkflowError {
2331
2793
  }
2332
2794
  }
2333
2795
 
2796
+ class ConcurrentCascadeError extends invariants.WorkflowError {
2797
+ instanceId;
2798
+ attempts;
2799
+ constructor(args) {
2800
+ super("concurrent-cascade", lostRaceMessage({
2801
+ what: "Automatic cascade",
2802
+ instanceId: args.instanceId,
2803
+ attempts: args.attempts,
2804
+ recovery: "An earlier verb commit may already have landed. Re-read the instance and invoke tick after contention subsides."
2805
+ })), this.name = "ConcurrentCascadeError", this.instanceId = args.instanceId, this.attempts = args.attempts;
2806
+ }
2807
+ }
2808
+
2334
2809
  function isRevisionConflict(error) {
2335
2810
  if (typeof error != "object" || error === null) return !1;
2336
2811
  const {statusCode: statusCode, message: message} = error;
@@ -2613,11 +3088,15 @@ function applyFieldSet(op, ctx) {
2613
3088
  ...entryShape(entry),
2614
3089
  memberRoles: ctx.memberRoles,
2615
3090
  roleAliases: ctx.roleAliases,
2616
- ...entry._type === "assignees" || entry._type === "object" || entry._type === "array" ? {
3091
+ ...entry._type === "assignee" || entry._type === "assignees" || entry._type === "object" || entry._type === "array" ? {
2617
3092
  previousValue: entry.value
2618
3093
  } : {}
2619
3094
  });
2620
- return assertRuntimeRefsWithinSurface({
3095
+ return assertLegacySingularValue({
3096
+ entry: entry,
3097
+ value: validated,
3098
+ ctx: ctx
3099
+ }), assertRuntimeRefsWithinSurface({
2621
3100
  entryType: entry._type,
2622
3101
  entryName: entry.name,
2623
3102
  value: validated,
@@ -2634,12 +3113,12 @@ function applyFieldSet(op, ctx) {
2634
3113
  }
2635
3114
 
2636
3115
  function applyFieldSetIfMissing(op, ctx) {
2637
- const entry = locateEntry(ctx, op.target);
2638
- return invariants.isAlwaysArrayFieldKind(entry._type) && rejectFieldOpTarget({
3116
+ const entry = locateEntry(ctx, op.target), legacySingular = isLegacySingularAssignment(entry, ctx);
3117
+ return invariants.isAlwaysArrayFieldKind(entry._type) && !legacySingular && rejectFieldOpTarget({
2639
3118
  entry: entry,
2640
3119
  op: op,
2641
3120
  issue: "is always array-valued — setIfMissing applies to nullable entries only; an empty array entry already holds []"
2642
- }), entry.value !== null && entry.value !== void 0 ? {
3121
+ }), entry.value !== null && entry.value !== void 0 && !(legacySingular && Array.isArray(entry.value) && entry.value.length === 0) ? {
2643
3122
  opType: op.type,
2644
3123
  target: op.target
2645
3124
  } : {
@@ -2760,7 +3239,14 @@ function applyFieldUnset(op, ctx) {
2760
3239
  }
2761
3240
 
2762
3241
  function applyFieldAppend(op, ctx) {
2763
- const entry = locateEntry(ctx, op.target), slot = appendItemSlot(entry), item = resolveOpValue({
3242
+ const entry = locateEntry(ctx, op.target);
3243
+ if (isLegacySingularAssignment(entry, ctx)) throw new invariants.FieldValueShapeError({
3244
+ entryType: entry._type,
3245
+ entryName: entry.name,
3246
+ mode: "item",
3247
+ issues: [ "legacy singular assignee entries do not support append; redeploy at model 9" ]
3248
+ });
3249
+ const slot = appendItemSlot(entry), item = resolveOpValue({
2764
3250
  src: op.value,
2765
3251
  ctx: ctx,
2766
3252
  ...slot !== void 0 ? {
@@ -2772,7 +3258,7 @@ function applyFieldAppend(op, ctx) {
2772
3258
  item: item,
2773
3259
  types: entry._type === "doc.refs" ? entry.types : void 0,
2774
3260
  of: entry._type === "array" ? entry.of : void 0,
2775
- roles: entry._type === "assignees" ? entry.roles : void 0,
3261
+ roles: invariants.isAssignmentFieldEntry(entry) ? entry.roles : void 0,
2776
3262
  memberRoles: ctx.memberRoles,
2777
3263
  roleAliases: ctx.roleAliases
2778
3264
  });
@@ -2784,8 +3270,16 @@ function applyFieldAppend(op, ctx) {
2784
3270
  ctx: ctx,
2785
3271
  src: op.value
2786
3272
  });
2787
- const current = Array.isArray(entry.value) ? entry.value : [];
2788
- return setEntryValue(entry, [ ...current, withRowKey(validated) ]), {
3273
+ const next = [ ...Array.isArray(entry.value) ? entry.value : [], withRowKey(validated) ], value = invariants.isAssignmentFieldEntry(entry) ? invariants.validateFieldValue({
3274
+ entryType: entry._type,
3275
+ entryName: entry.name,
3276
+ value: next,
3277
+ ...assignmentEntryShape(entry),
3278
+ previousValue: entry.value,
3279
+ memberRoles: ctx.memberRoles,
3280
+ roleAliases: ctx.roleAliases
3281
+ }) : next;
3282
+ return setEntryValue(entry, value), {
2789
3283
  opType: op.type,
2790
3284
  target: op.target,
2791
3285
  resolved: {
@@ -2862,7 +3356,14 @@ async function applyFieldUpdateWhere(op, ctx) {
2862
3356
  }
2863
3357
 
2864
3358
  async function applyFieldRemoveWhere(op, ctx) {
2865
- const entry = locateEntry(ctx, op.target), rows = requireArrayValue(entry, op), matches = await rowMatches({
3359
+ const entry = locateEntry(ctx, op.target);
3360
+ if (isLegacySingularAssignment(entry, ctx)) throw new invariants.FieldValueShapeError({
3361
+ entryType: entry._type,
3362
+ entryName: entry.name,
3363
+ mode: "value",
3364
+ issues: [ "legacy singular assignee entries do not support removeWhere; redeploy at model 9" ]
3365
+ });
3366
+ const rows = requireArrayValue(entry, op), matches = await rowMatches({
2866
3367
  where: op.where,
2867
3368
  rows: rows,
2868
3369
  ctx: ctx
@@ -2873,6 +3374,20 @@ async function applyFieldRemoveWhere(op, ctx) {
2873
3374
  };
2874
3375
  }
2875
3376
 
3377
+ function assertLegacySingularValue(args) {
3378
+ const {entry: entry, value: value, ctx: ctx} = args;
3379
+ if (!(!isLegacySingularAssignment(entry, ctx) || !Array.isArray(value) || value.length <= 1)) throw new invariants.FieldValueShapeError({
3380
+ entryType: entry._type,
3381
+ entryName: entry.name,
3382
+ mode: "value",
3383
+ issues: [ "legacy singular assignee entries accept at most one total member; redeploy at model 9" ]
3384
+ });
3385
+ }
3386
+
3387
+ function isLegacySingularAssignment(entry, ctx) {
3388
+ return entry._type === "assignee" && !isAssignmentListModel(ctx.mutation.definitionModelVersion);
3389
+ }
3390
+
2876
3391
  function requireMergeableRows(entry, op) {
2877
3392
  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`);
2878
3393
  }
@@ -2893,7 +3408,7 @@ function requireArrayValue(entry, op) {
2893
3408
  }
2894
3409
 
2895
3410
  function applyStatusSet(op, ctx) {
2896
- const entry = findCurrentActivityEntry(ctx.mutation, op.activity);
3411
+ const entry = invariants.findCurrentActivityEntry(ctx.mutation, op.activity);
2897
3412
  if (entry === void 0) throw new Error(`status.set targets activity "${op.activity}" which has no entry in the open stage`);
2898
3413
  return applyActivityStatusChange({
2899
3414
  entry: entry,
@@ -2925,7 +3440,7 @@ function locateEntry(ctx, target) {
2925
3440
 
2926
3441
  function fieldHost(ctx, scope) {
2927
3442
  if (scope === "workflow") return ctx.mutation.fields;
2928
- const stageEntry = findOpenStageEntry(ctx.mutation);
3443
+ const stageEntry = invariants.findOpenStageEntry(ctx.mutation);
2929
3444
  if (scope === "stage") return stageEntry?.fields;
2930
3445
  const activity = stageEntry?.activities.find(t => t.name === ctx.activityName);
2931
3446
  return activity !== void 0 && activity.fields === void 0 && (activity.fields = []),
@@ -2997,10 +3512,10 @@ function resolveAtomicOpValue(src, ctx) {
2997
3512
  }
2998
3513
 
2999
3514
  function readEntryFromMutation(ctx, src) {
3000
- const scopes = src.scope !== void 0 ? [ src.scope ] : [ "activity", "stage", "workflow" ], stageEntry = findOpenStageEntry(ctx.mutation);
3515
+ const scopes = src.scope !== void 0 ? [ src.scope ] : [ "activity", "stage", "workflow" ], stageEntry = invariants.findOpenStageEntry(ctx.mutation);
3001
3516
  for (const scope of scopes) {
3002
3517
  let host;
3003
- scope === "workflow" ? host = ctx.mutation.fields : scope === "stage" ? host = stageEntry?.fields : host = findCurrentActivityEntry(ctx.mutation, ctx.activityName)?.fields;
3518
+ scope === "workflow" ? host = ctx.mutation.fields : scope === "stage" ? host = stageEntry?.fields : host = invariants.findCurrentActivityEntry(ctx.mutation, ctx.activityName)?.fields;
3004
3519
  const entry = host?.find(s => s.name === src.field);
3005
3520
  if (entry !== void 0) return entry;
3006
3521
  }
@@ -3023,7 +3538,7 @@ async function rowMatches({where: where, rows: rows, ctx: ctx}) {
3023
3538
  const WHERE_SCOPE_PARAM_NAMES = [ "row", "params", "actor", "now", "self", "stage", "fields", "context", "effects", "effectStatus", "activities", "allActivitiesDone", "anyActivityFailed" ];
3024
3539
 
3025
3540
  function whereParams(ctx) {
3026
- const activities = findOpenStageEntry(ctx.mutation)?.activities ?? [];
3541
+ const activities = invariants.findOpenStageEntry(ctx.mutation)?.activities ?? [];
3027
3542
  return {
3028
3543
  params: ctx.params,
3029
3544
  actor: ctx.actor ?? null,
@@ -3048,6 +3563,14 @@ function whereParams(ctx) {
3048
3563
 
3049
3564
  function validateDefinition(definition) {
3050
3565
  const v2 = createDefinitionValidator();
3566
+ validateConditionSites(v2, definition), validateStart(v2, definition);
3567
+ for (const entry of definition.fields ?? []) v2.checkEntry(entry, `workflow.fields "${entry.name}"`);
3568
+ for (const stage of definition.stages) validateStage(v2, stage);
3569
+ for (const issue of invariants.checkWorkflowInvariants(definition)) v2.errors.push(` · ${invariants.formatIssuePath(issue.path)}: ${issue.message}`);
3570
+ 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`));
3571
+ }
3572
+
3573
+ function validateConditionSites(v2, definition) {
3051
3574
  for (const site of conditionSitesOf(definition)) {
3052
3575
  const {address: address} = site;
3053
3576
  address.kind !== "editable-field" && v2.checkCondition(site.condition, conditionSiteLabel({
@@ -3055,12 +3578,11 @@ function validateDefinition(definition) {
3055
3578
  address: address
3056
3579
  }));
3057
3580
  }
3581
+ }
3582
+
3583
+ function validateStart(v2, definition) {
3058
3584
  definition.start?.filter !== void 0 && v2.report("start.filter", startFilterSyntaxIssues(definition.start.filter));
3059
3585
  for (const [index, requirement] of (definition.start?.requirements ?? []).entries()) requirement.type === "groq" && v2.report(`start.requirements[${index}].query`, startRequirementSyntaxIssues(requirement.query));
3060
- for (const entry of definition.fields ?? []) v2.checkEntry(entry, `workflow.fields "${entry.name}"`);
3061
- for (const stage of definition.stages) validateStage(v2, stage);
3062
- for (const issue of invariants.checkWorkflowInvariants(definition)) v2.errors.push(` · ${invariants.formatIssuePath(issue.path)}: ${issue.message}`);
3063
- 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`));
3064
3586
  }
3065
3587
 
3066
3588
  function createDefinitionValidator() {
@@ -3091,33 +3613,12 @@ function createDefinitionValidator() {
3091
3613
 
3092
3614
  function conditionSiteLabel({stage: stage, address: address}) {
3093
3615
  const at = `stage "${stage}"`;
3094
- switch (address.kind) {
3095
- case "predicate":
3096
- return `predicate "${address.predicate}"`;
3097
-
3098
- case "transition":
3099
- return `${at} transition "${address.transition}" when`;
3100
-
3101
- case "activity-filter":
3102
- return `${at} activity "${address.activity}".filter`;
3103
-
3104
- case "requirement":
3105
- return `${at} activity "${address.activity}".requirements "${address.requirement}"`;
3106
-
3107
- case "action":
3108
- return `${at} activity "${address.activity}" action "${address.action}".filter`;
3109
-
3110
- case "action-when":
3111
- return `${at} activity "${address.activity}" action "${address.action}".when`;
3112
-
3113
- case "editable-override":
3114
- return `${at}.editable "${address.name}"`;
3115
- }
3616
+ 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}"`;
3116
3617
  }
3117
3618
 
3118
3619
  function validateStage(v2, stage) {
3119
3620
  for (const entry of stage.fields ?? []) v2.checkEntry(entry, `stage "${stage.name}".fields "${entry.name}"`);
3120
- for (const guard of stage.guards ?? []) validateGuardReads({
3621
+ for (const guard of stage.guards ?? []) validateGuard({
3121
3622
  v: v2,
3122
3623
  stageName: stage.name,
3123
3624
  guard: guard
@@ -3129,12 +3630,28 @@ function validateStage(v2, stage) {
3129
3630
  });
3130
3631
  }
3131
3632
 
3132
- function validateGuardReads({v: v2, stageName: stageName, guard: guard}) {
3633
+ function validateGuard({v: v2, stageName: stageName, guard: guard}) {
3133
3634
  const where = `stage "${stageName}" guard "${guard.name}"`;
3134
- for (const expr of guard.match.idRefs ?? []) v2.checkGuardRead(expr, `${where} match.idRefs`);
3635
+ if (validateGuardMatch({
3636
+ v: v2,
3637
+ where: where,
3638
+ guard: guard
3639
+ }), guard.predicate !== void 0 && guard.predicate !== "") {
3640
+ const issue = guardPredicateSyntaxIssue(guard.predicate);
3641
+ issue !== void 0 && v2.report(`${where} predicate`, [ issue ]);
3642
+ }
3135
3643
  for (const [key, expr] of Object.entries(guard.metadata ?? {})) v2.checkGuardRead(expr, `${where} metadata "${key}"`);
3136
3644
  }
3137
3645
 
3646
+ function validateGuardMatch({v: v2, where: where, guard: guard}) {
3647
+ guard.match.actions.length === 0 && v2.report(`${where} match.actions`, [ invariants.GUARD_ACTIONS_REQUIRED_MESSAGE ]);
3648
+ for (const pattern of guard.match.idPatterns ?? []) {
3649
+ const issue = guardIdPatternIssue(pattern, guard.match.actions);
3650
+ issue !== void 0 && v2.report(`${where} match.idPatterns`, [ issue ]);
3651
+ }
3652
+ for (const expr of guard.match.idRefs ?? []) v2.checkGuardRead(expr, `${where} match.idRefs`);
3653
+ }
3654
+
3138
3655
  function validateActivity({v: v2, stageName: stageName, activity: activity}) {
3139
3656
  const where = `stage "${stageName}" activity "${activity.name}"`;
3140
3657
  for (const entry of activity.fields ?? []) v2.checkEntry(entry, `${where}.fields "${entry.name}"`);
@@ -3239,211 +3756,43 @@ function buildClientForGdr(args) {
3239
3756
  target: target,
3240
3757
  parsed: parsed,
3241
3758
  siblings: siblings
3242
- });
3243
- };
3244
- }
3245
-
3246
- function siblingFor(args) {
3247
- const {client: client, workflowResource: workflowResource, target: target, parsed: parsed, siblings: siblings} = args, key = `${target.type}:${target.id}`, cached = siblings.get(key);
3248
- if (cached !== void 0) return cached;
3249
- if (client.withConfig === void 0) throw new invariants.ContractViolationError(`Cannot route "${invariants.gdrFromResource(target, parsed.documentId)}": the GDR targets ${target.type} "${target.id}", not the workflow resource (${workflowResource.type} "${workflowResource.id}"), and the workflow client cannot derive a sibling client for it (no withConfig). Pass resourceClients to route foreign resources, or use a client with withConfig (e.g. @sanity/client).`);
3250
- const sibling = client.withConfig(siblingConfig(target));
3251
- return siblings.set(key, sibling), sibling;
3252
- }
3253
-
3254
- function siblingConfig(target) {
3255
- if (target.type === "dataset") {
3256
- const {projectId: projectId, dataset: dataset} = invariants.datasetResourceParts(target.id);
3257
- return {
3258
- resource: target,
3259
- projectId: projectId,
3260
- dataset: dataset
3261
- };
3262
- }
3263
- return {
3264
- resource: target
3265
- };
3266
- }
3267
-
3268
- const wallClock = () => /* @__PURE__ */ (new Date).toISOString();
3269
-
3270
- function addMs(instant, ms) {
3271
- return new Date(Date.parse(instant) + ms).toISOString();
3272
- }
3273
-
3274
- function hasPassed(instant, now) {
3275
- return Date.parse(instant) <= Date.parse(now);
3276
- }
3277
-
3278
- const GUARD_DOC_TYPE = "temp.system.guard";
3279
-
3280
- class MutationGuardDeniedError extends invariants.WorkflowError {
3281
- denied;
3282
- documentId;
3283
- action;
3284
- constructor(args) {
3285
- const ids = args.denied.map(d => d.guardId).join(", ");
3286
- super("mutation-guard-denied", `Mutation on "${args.documentId}" (${args.action}) denied by guard(s) [${ids}]`),
3287
- this.name = "MutationGuardDeniedError", this.denied = args.denied, this.documentId = args.documentId,
3288
- this.action = args.action;
3289
- }
3290
- static fromGuards(args) {
3291
- return new MutationGuardDeniedError({
3292
- documentId: args.documentId,
3293
- action: args.action,
3294
- denied: deniedGuardRefs(args.guards)
3295
- });
3296
- }
3297
- }
3298
-
3299
- function deniedGuardRefs(guards) {
3300
- return guards.map(g => ({
3301
- guardId: g._id,
3302
- ...g.name !== void 0 ? {
3303
- name: g.name
3304
- } : {}
3305
- }));
3306
- }
3307
-
3308
- function deniedGuardLabels(denied) {
3309
- return denied.map(d => d.name ?? d.guardId);
3310
- }
3311
-
3312
- function lakeGuardId(args) {
3313
- return `${GUARD_DOC_TYPE}.${args.instanceDocId}.${args.guardName}`;
3314
- }
3315
-
3316
- function compileGuard(args) {
3317
- return {
3318
- _id: args.id,
3319
- _type: GUARD_DOC_TYPE,
3320
- resourceType: args.resourceType,
3321
- resourceId: args.resourceId,
3322
- owner: args.owner,
3323
- sourceInstanceId: args.sourceInstanceId,
3324
- sourceDefinition: args.sourceDefinition,
3325
- sourceStage: args.sourceStage,
3326
- ...args.name !== void 0 ? {
3327
- name: args.name
3328
- } : {},
3329
- ...args.description !== void 0 ? {
3330
- description: args.description
3331
- } : {},
3332
- match: args.match,
3333
- predicate: args.predicate,
3334
- metadata: args.metadata
3759
+ });
3335
3760
  };
3336
3761
  }
3337
3762
 
3338
- function toGroqJsPredicate(predicate) {
3339
- return predicate.replace(new RegExp("(?<!\\$)\\bguard\\.", "g"), "$guard.").replace(new RegExp("(?<!\\$)\\bmutation\\.", "g"), "$mutation.");
3340
- }
3341
-
3342
- function globMatch(pattern, value) {
3343
- if (!pattern.includes("*")) return pattern === value;
3344
- const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
3345
- return new RegExp(`^${escaped}$`).test(value);
3763
+ function siblingFor(args) {
3764
+ const {client: client, workflowResource: workflowResource, target: target, parsed: parsed, siblings: siblings} = args, key = `${target.type}:${target.id}`, cached = siblings.get(key);
3765
+ if (cached !== void 0) return cached;
3766
+ if (client.withConfig === void 0) throw new invariants.ContractViolationError(`Cannot route "${invariants.gdrFromResource(target, parsed.documentId)}": the GDR targets ${target.type} "${target.id}", not the workflow resource (${workflowResource.type} "${workflowResource.id}"), and the workflow client cannot derive a sibling client for it (no withConfig). Pass resourceClients to route foreign resources, or use a client with withConfig (e.g. @sanity/client).`);
3767
+ const sibling = client.withConfig(siblingConfig(target));
3768
+ return siblings.set(key, sibling), sibling;
3346
3769
  }
3347
3770
 
3348
- function guardMatches({guard: guard, doc: doc, action: action}) {
3349
- const m = guard.match;
3350
- if (!m.actions.includes(action) || m.types && m.types.length > 0 && !(doc.type !== void 0 && m.types.includes(doc.type))) return !1;
3351
- if (m.idRefs && m.idRefs.length > 0 || m.idPatterns && m.idPatterns.length > 0) {
3352
- const byRef = m.idRefs?.includes(doc.id) ?? !1, byPattern = m.idPatterns?.some(p => globMatch(p, doc.id)) ?? !1;
3353
- if (!byRef && !byPattern) return !1;
3771
+ function siblingConfig(target) {
3772
+ if (target.type === "dataset") {
3773
+ const {projectId: projectId, dataset: dataset} = invariants.datasetResourceParts(target.id);
3774
+ return {
3775
+ resource: target,
3776
+ projectId: projectId,
3777
+ dataset: dataset
3778
+ };
3354
3779
  }
3355
- return !0;
3356
- }
3357
-
3358
- function guardPredicateParams(args) {
3359
3780
  return {
3360
- guard: args.guard,
3361
- mutation: {
3362
- action: args.action
3363
- }
3781
+ resource: target
3364
3782
  };
3365
3783
  }
3366
3784
 
3367
- async function evaluateMutationGuard(args) {
3368
- const {guard: guard, context: context} = args;
3369
- if (guard.predicate === "") return !1;
3370
- const params = guardPredicateParams({
3371
- guard: guard,
3372
- action: context.action
3373
- });
3374
- try {
3375
- const tree = groqJs.parse(toGroqJsPredicate(guard.predicate), {
3376
- mode: "delta",
3377
- params: params
3378
- });
3379
- return await (await groqJs.evaluate(tree, {
3380
- before: context.before,
3381
- after: context.after,
3382
- params: params,
3383
- ...context.identity !== void 0 ? {
3384
- identity: context.identity
3385
- } : {}
3386
- })).get() === !0;
3387
- } catch {
3388
- return !1;
3389
- }
3390
- }
3391
-
3392
- async function denyingGuards(args) {
3393
- const {guards: guards, doc: doc, context: context} = args, denied = [];
3394
- for (const guard of guards) guardMatches({
3395
- guard: guard,
3396
- doc: doc,
3397
- action: context.action
3398
- }) && (await evaluateMutationGuard({
3399
- guard: guard,
3400
- context: context
3401
- }) || denied.push(guard));
3402
- return denied;
3403
- }
3404
-
3405
- async function documentActionDenials(args) {
3406
- const {doc: doc, resource: resource, action: action, guards: guards, identity: identity} = args, enforceable = guards.filter(g => g.resourceType === resource.type && g.resourceId === resource.id);
3407
- return enforceable.length === 0 ? [] : denyingGuards({
3408
- guards: enforceable,
3409
- doc: {
3410
- id: doc._id,
3411
- type: doc._type
3412
- },
3413
- context: {
3414
- action: action,
3415
- before: doc,
3416
- after: doc,
3417
- ...identity !== void 0 ? {
3418
- identity: identity
3419
- } : {}
3420
- }
3421
- });
3422
- }
3785
+ const wallClock = () => /* @__PURE__ */ (new Date).toISOString();
3423
3786
 
3424
- async function instanceWriteDenials(args) {
3425
- const {instance: instance, guards: guards, identity: identity} = args;
3426
- return documentActionDenials({
3427
- doc: instance,
3428
- resource: instance.workflowResource,
3429
- action: "update",
3430
- guards: guards,
3431
- ...identity !== void 0 ? {
3432
- identity: identity
3433
- } : {}
3434
- });
3787
+ function addMs(instant, ms) {
3788
+ return new Date(Date.parse(instant) + ms).toISOString();
3435
3789
  }
3436
3790
 
3437
- async function assertInstanceWriteAllowed(args) {
3438
- const denied = await instanceWriteDenials(args);
3439
- if (denied.length !== 0) throw MutationGuardDeniedError.fromGuards({
3440
- documentId: args.instance._id,
3441
- action: "update",
3442
- guards: denied
3443
- });
3791
+ function hasPassed(instant, now) {
3792
+ return Date.parse(instant) <= Date.parse(now);
3444
3793
  }
3445
3794
 
3446
- const DATA_MODEL_VERSION = 8, DATA_MODEL_MIN_READER = 4, DATA_MODEL_MAX_READER = 8, READER_MODEL_ROLLOUT_URL = "https://www.sanity.io/docs/workflows/prerelease";
3795
+ 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";
3447
3796
 
3448
3797
  class ReaderModelAcknowledgementError extends invariants.WorkflowError {
3449
3798
  code="WORKFLOW_READER_MODEL_ACKNOWLEDGEMENT_MISMATCH";
@@ -3583,6 +3932,22 @@ const DATA_MODEL_CHANGES = Object.freeze([ Object.freeze({
3583
3932
  compatibility: "reader-floor",
3584
3933
  applicability: "detectable",
3585
3934
  summary: "Assignee fields may restrict newly assigned users and collective roles by role."
3935
+ }), Object.freeze({
3936
+ id: "split-guard-id-spaces",
3937
+ introducedInModel: 9,
3938
+ minReaderModel: 9,
3939
+ documentTypes: Object.freeze([ "definition", "instance" ]),
3940
+ compatibility: "reader-floor",
3941
+ applicability: "detectable",
3942
+ summary: "Guards spanning edit and lifecycle id spaces emit independently retractable documents."
3943
+ }), Object.freeze({
3944
+ id: "singular-assignee-lists",
3945
+ introducedInModel: 9,
3946
+ minReaderModel: 9,
3947
+ documentTypes: Object.freeze([ "definition", "instance" ]),
3948
+ compatibility: "reader-floor",
3949
+ applicability: "detectable",
3950
+ summary: "Singular assignee fields use member lists with at most one user and any number of roles."
3586
3951
  }) ]);
3587
3952
 
3588
3953
  function recordOf(value) {
@@ -3706,6 +4071,25 @@ function hasRoleConstrainedAssignments(document) {
3706
4071
  return persistedFieldEntries(document).some(entry => entryKindMatches(entry, invariants.assignmentKindAcceptsRoles) && Array.isArray(entry.roles) && entry.roles.length > 0);
3707
4072
  }
3708
4073
 
4074
+ function hasSingularAssigneeLists(document) {
4075
+ const root = recordOf(document);
4076
+ if (root === void 0) return !1;
4077
+ if (typeof root.definitionSnapshot != "string") return persistedFieldEntries(document).some(entry => entryHasKind(entry, "assignee"));
4078
+ const snapshot = parsedDefinitionSnapshot(root);
4079
+ return typeof snapshot?.modelVersion == "number" && isAssignmentListModel(snapshot.modelVersion) && persistedFieldEntries(document).some(entry => entryHasKind(entry, "assignee"));
4080
+ }
4081
+
4082
+ function isMutationGuardAction(action) {
4083
+ return typeof action == "string" && invariants.MUTATION_GUARD_ACTIONS.some(candidate => candidate === action);
4084
+ }
4085
+
4086
+ function hasSplitGuardIdSpaces(document) {
4087
+ return persistedDefinitionTree(document)?.stages.some(stage => recordsAt(stage, "guards").some(guard => {
4088
+ const actions = recordOf(guard.match)?.actions;
4089
+ return Array.isArray(actions) && invariants.mutationGuardRequiresSplitEmission(actions.filter(isMutationGuardAction));
4090
+ })) ?? !1;
4091
+ }
4092
+
3709
4093
  const featureDetectors = {
3710
4094
  "governed-model-stamps": () => !0,
3711
4095
  "subject-field-kind": document => hasFieldKind(document, "subject"),
@@ -3718,6 +4102,8 @@ const featureDetectors = {
3718
4102
  "readiness-requirements": hasReadinessRequirements,
3719
4103
  "due-date-field-kinds": document => hasFieldKind(document, "dueDate") || hasFieldKind(document, "dueDatetime"),
3720
4104
  "role-constrained-assignment-fields": hasRoleConstrainedAssignments,
4105
+ "singular-assignee-lists": hasSingularAssigneeLists,
4106
+ "split-guard-id-spaces": hasSplitGuardIdSpaces,
3721
4107
  "node-semantics": hasNodeSemantics,
3722
4108
  "field-patch-ops": document => hasPersistedOpType(document, [ "field.inc", "field.dec", "field.setIfMissing" ]),
3723
4109
  "attributes-condition-var": () => !0
@@ -3857,8 +4243,93 @@ function findActivityNode(args) {
3857
4243
  return findStageNode(args)?.activities?.find(entry => entry.name === args.activityName);
3858
4244
  }
3859
4245
 
4246
+ function requirementDescriptor(requirement) {
4247
+ return {
4248
+ name: requirement.name,
4249
+ ...requirement.title !== void 0 ? {
4250
+ title: requirement.title
4251
+ } : {},
4252
+ ...requirement.description !== void 0 ? {
4253
+ description: requirement.description
4254
+ } : {}
4255
+ };
4256
+ }
4257
+
4258
+ function subjectDenialLabels(denied) {
4259
+ return denied.map(d => `${d.permission} on ${d.subject} (${d.resource})`);
4260
+ }
4261
+
4262
+ class ActionDisabledError extends invariants.WorkflowError {
4263
+ reason;
4264
+ activity;
4265
+ action;
4266
+ constructor(args) {
4267
+ super("action-disabled", formatDisabledReason({
4268
+ activity: args.activity,
4269
+ action: args.action,
4270
+ reason: args.reason
4271
+ })), this.name = "ActionDisabledError", this.reason = args.reason, this.activity = args.activity,
4272
+ this.action = args.action;
4273
+ }
4274
+ }
4275
+
4276
+ class StartNotAllowedError extends invariants.WorkflowError {
4277
+ definition;
4278
+ unmetRequirements;
4279
+ constructor(args) {
4280
+ super("start-not-allowed", `startInstance refused definition "${args.definition}": ${args.unmetRequirements.map(requirement => requirement.title ?? requirement.name).join(", ")}. Pre-flight the verdict with evaluateStart.`),
4281
+ this.name = "StartNotAllowedError", this.definition = args.definition, this.unmetRequirements = args.unmetRequirements;
4282
+ }
4283
+ }
4284
+
4285
+ function actionRendering(action) {
4286
+ const kind = action.disabledReason?.kind;
4287
+ return kind === "filter-failed" ? "absent" : action.triggered === !0 || kind === "cascade-fired" ? "automation" : "button";
4288
+ }
4289
+
4290
+ const disabledReasonDetail = {
4291
+ "filter-failed": r => `action filter returned false${r.detail ? ` (${r.detail})` : ""}`,
4292
+ "cascade-fired": r => `the action is cascade-fired (when: ${JSON.stringify(r.when)}) — the engine fires it on truth; it cannot be invoked via fireAction`,
4293
+ "activity-not-active": r => `activity status is "${r.status}"`,
4294
+ "stage-terminal": r => `stage "${r.stage}" is terminal`,
4295
+ "instance-completed": r => `instance completed at ${r.completedAt}`,
4296
+ "instance-aborted": r => `instance aborted at ${r.abortedAt}`,
4297
+ "requirements-unmet": r => `unmet requirement(s): ${r.unmetRequirements.map(requirement => requirement.name).join(", ")}`,
4298
+ "subject-permission-denied": r => `missing subject permission(s): ${subjectDenialLabels(r.denied).join(", ")}`
4299
+ };
4300
+
4301
+ function actionDisabledDetail(reason) {
4302
+ return disabledReasonDetail[reason.kind](reason);
4303
+ }
4304
+
4305
+ function formatDisabledReason({activity: activity, action: action, reason: reason}) {
4306
+ return `Action "${activity}:${action}" is not allowed: ${actionDisabledDetail(reason)}`;
4307
+ }
4308
+
4309
+ class EditFieldDeniedError extends invariants.WorkflowError {
4310
+ reason;
4311
+ target;
4312
+ constructor(args) {
4313
+ super("edit-field-denied", formatEditDisabledReason(args.target, args.reason)),
4314
+ this.name = "EditFieldDeniedError", this.reason = args.reason, this.target = args.target;
4315
+ }
4316
+ }
4317
+
4318
+ const editDisabledReasonDetail = {
4319
+ "not-editable": () => "field is not declared editable",
4320
+ "instance-completed": r => `instance completed at ${r.completedAt}`,
4321
+ "instance-aborted": r => `instance aborted at ${r.abortedAt}`,
4322
+ "edit-window-closed": r => `edit window closed (${r.detail})`,
4323
+ "editor-not-permitted": r => `editor not permitted (${r.predicate})`
4324
+ };
4325
+
4326
+ function formatEditDisabledReason(target, reason) {
4327
+ const detail = editDisabledReasonDetail[reason.kind](reason), where = target.activity !== void 0 ? `${target.activity}.${target.field}` : target.field;
4328
+ return `Field "${target.scope}:${where}" is not editable: ${detail}`;
4329
+ }
4330
+
3860
4331
  function liveChildrenField(instance) {
3861
- const live = liveSubworkflows(instance).length;
4332
+ const live = invariants.liveSubworkflows(instance).length;
3862
4333
  return live > 0 ? {
3863
4334
  liveChildren: live
3864
4335
  } : {};
@@ -3879,11 +4350,12 @@ function diagnoseInputFromEvaluation(evaluation) {
3879
4350
  }
3880
4351
 
3881
4352
  function openStage(instance) {
3882
- return findOpenStageEntry(instance);
4353
+ return invariants.findOpenStageEntry(instance);
3883
4354
  }
3884
4355
 
3885
4356
  function assigneesOf(stage, activityName) {
3886
- return (stage?.activities.find(t => t.name === activityName)?.fields ?? []).filter(s => s._type === "assignees").flatMap(s => s.value);
4357
+ const entry = stage?.activities.find(t => t.name === activityName);
4358
+ return [ ...invariants.assignmentMembers(entry?.fields ?? []) ];
3887
4359
  }
3888
4360
 
3889
4361
  function isResolved(status) {
@@ -3916,11 +4388,16 @@ function failedActivityCause(input) {
3916
4388
 
3917
4389
  function waitingState(input) {
3918
4390
  const active = input.activities.find(t => t.status === "active" && (t.activity.actions ?? []).length > 0 && (t.unmetRequirements ?? []).length === 0);
3919
- if (active !== void 0) return {
4391
+ if (active === void 0) return;
4392
+ const actions = active.actions.filter(action => actionRendering(action) === "button" && action.allowed).map(action => action.action.name), hasManualAction = active.actions.some(action => !invariants.isCascadeFired(action.action));
4393
+ let waitingFor = "automation";
4394
+ return actions.length > 0 ? waitingFor = "caller" : hasManualAction && (waitingFor = "manual-action"),
4395
+ {
3920
4396
  state: "waiting",
3921
4397
  activity: active.activity.name,
3922
4398
  assignees: input.assignees[active.activity.name] ?? [],
3923
- actions: (active.activity.actions ?? []).filter(a => !invariants.isCascadeFired(a)).map(a => a.name)
4399
+ actions: actions,
4400
+ waitingFor: waitingFor
3924
4401
  };
3925
4402
  }
3926
4403
 
@@ -3950,7 +4427,7 @@ function stuckFromDocumentState(input) {
3950
4427
  }
3951
4428
 
3952
4429
  function documentStuckCause(args) {
3953
- const stage = findOpenStageEntry(args.instance);
4430
+ const stage = invariants.findOpenStageEntry(args.instance);
3954
4431
  if (stage !== void 0) return stuckFromDocumentState({
3955
4432
  instance: args.instance,
3956
4433
  activities: stage.activities.map(entry => ({
@@ -4046,7 +4523,7 @@ function isClaimExpired(claim, now) {
4046
4523
  return claim.leaseExpiresAt === void 0 || hasPassed(claim.leaseExpiresAt, now);
4047
4524
  }
4048
4525
 
4049
- const EFFECT_RUN_STATUSES = [ "done", "failed", "cancelled" ];
4526
+ const EFFECT_RUN_STATUSES = groqConditionDescribe._exhaustiveOptions()([ "done", "failed", "cancelled" ]);
4050
4527
 
4051
4528
  function userLoginProvider(user) {
4052
4529
  return user?.provider ?? user?.loginProvider;
@@ -4299,7 +4776,7 @@ function collectWatchRefs(instance) {
4299
4776
  }
4300
4777
 
4301
4778
  function collectSubjectRefs(instance) {
4302
- const stage = findOpenStageEntry(instance), activityFields = stage?.activities.map(activity => activity.fields) ?? [];
4779
+ const stage = invariants.findOpenStageEntry(instance), activityFields = stage?.activities.map(activity => activity.fields) ?? [];
4303
4780
  return [ ...entryDocRefs(instance.fields), ...entryDocRefs(stage?.fields), ...activityFields.flatMap(entryDocRefs), ...entryReleaseRefs(instance.fields), ...entryReleaseRefs(stage?.fields), ...activityFields.flatMap(entryReleaseRefs) ];
4304
4781
  }
4305
4782
 
@@ -4317,7 +4794,7 @@ function foreignSubjectRefs(instance) {
4317
4794
  }
4318
4795
 
4319
4796
  function liveSubworkflowRefs(instance) {
4320
- return liveSubworkflows(instance).map(row => row.ref);
4797
+ return invariants.liveSubworkflows(instance).map(row => row.ref);
4321
4798
  }
4322
4799
 
4323
4800
  function readsRaw(ref) {
@@ -4800,32 +5277,122 @@ function normalizeQueryResult({entryType: entryType, raw: raw, workflowResource:
4800
5277
  }
4801
5278
  }
4802
5279
 
4803
- function coerceGdrShape(raw, workflowResource) {
4804
- if (!("id" in raw) || !("type" in raw)) return null;
4805
- const r = raw;
4806
- return typeof r.id != "string" || typeof r.type != "string" ? null : invariants.isGdrUri(r.id) ? {
4807
- id: r.id,
4808
- type: r.type
4809
- } : workflowResource ? {
4810
- id: invariants.toPhysicalGdr(r.id, workflowResource),
4811
- type: r.type
4812
- } : null;
5280
+ function coerceGdrShape(raw, workflowResource) {
5281
+ if (!("id" in raw) || !("type" in raw)) return null;
5282
+ const r = raw;
5283
+ return typeof r.id != "string" || typeof r.type != "string" ? null : invariants.isGdrUri(r.id) ? {
5284
+ id: r.id,
5285
+ type: r.type
5286
+ } : workflowResource ? {
5287
+ id: invariants.toPhysicalGdr(r.id, workflowResource),
5288
+ type: r.type
5289
+ } : null;
5290
+ }
5291
+
5292
+ function coerceRefEnvelope(raw, workflowResource) {
5293
+ if (!("_ref" in raw)) return null;
5294
+ const r = raw;
5295
+ return typeof r._ref != "string" || !workflowResource ? null : {
5296
+ id: invariants.toPhysicalGdr(r._ref, workflowResource),
5297
+ type: "document"
5298
+ };
5299
+ }
5300
+
5301
+ function coerceToGdr(raw, workflowResource) {
5302
+ return raw == null ? null : typeof raw == "object" ? coerceGdrShape(raw, workflowResource) ?? coerceRefEnvelope(raw, workflowResource) : typeof raw == "string" && workflowResource ? {
5303
+ id: invariants.toPhysicalGdr(raw, workflowResource),
5304
+ type: "document"
5305
+ } : null;
5306
+ }
5307
+
5308
+ const NOW = {
5309
+ variable: "now",
5310
+ path: []
5311
+ }, LAST_REPRESENTABLE_MS = 864e13;
5312
+
5313
+ function readsNowIn(reads) {
5314
+ return reads.some(read => read.variable === "now");
5315
+ }
5316
+
5317
+ function readsNow(condition) {
5318
+ return readsNowIn(groqConditionDescribe.analyzeCondition(condition).reads);
5319
+ }
5320
+
5321
+ async function stageTimeSites(args) {
5322
+ const {definition: definition, stage: stage, scopeFor: scopeFor} = args, sites = [];
5323
+ for (const site of conditionSitesOf(definition)) {
5324
+ if (site.stage !== stage || site.address.kind === "editable-override" || !readsNow(site.condition)) continue;
5325
+ const activity = "activity" in site.address ? site.address.activity : void 0;
5326
+ sites.push({
5327
+ condition: site.condition,
5328
+ params: await scopeFor(activity)
5329
+ });
5330
+ }
5331
+ return sites;
5332
+ }
5333
+
5334
+ async function nextEvaluationInstant(args) {
5335
+ const {sites: sites, dataset: dataset, now: now} = args, boundaries = [];
5336
+ for (const site of sites) {
5337
+ const boundary = await siteBoundary({
5338
+ ...site,
5339
+ dataset: dataset,
5340
+ now: now
5341
+ });
5342
+ boundary !== void 0 && boundaries.push(boundary);
5343
+ }
5344
+ return [ ...boundaries ].sort(byInstant)[0];
5345
+ }
5346
+
5347
+ function byInstant(a, b) {
5348
+ return Date.parse(a) - Date.parse(b);
5349
+ }
5350
+
5351
+ async function siteBoundary(args) {
5352
+ const {condition: condition, params: params, dataset: dataset, now: now} = args, insight = await groqConditionDescribe.explainCondition({
5353
+ condition: condition,
5354
+ dataset: dataset,
5355
+ params: params
5356
+ });
5357
+ for (const candidate of await candidateInstants({
5358
+ insight: insight,
5359
+ params: params,
5360
+ dataset: dataset,
5361
+ now: now
5362
+ })) if ((await groqConditionDescribe.whatIfCondition({
5363
+ condition: condition,
5364
+ dataset: dataset,
5365
+ params: params,
5366
+ assign: {
5367
+ target: NOW,
5368
+ value: candidate
5369
+ }
5370
+ })).changed) return candidate;
5371
+ }
5372
+
5373
+ async function candidateInstants(args) {
5374
+ const {insight: insight, params: params, dataset: dataset, now: now} = args, comparands = [];
5375
+ for (const atom of insight.atoms) readsNowIn(atom.atom.reads) && comparands.push(...await comparandsOf({
5376
+ atom: atom,
5377
+ params: params,
5378
+ dataset: dataset
5379
+ }));
5380
+ const instants = comparands.filter(invariants.isParseableInstant).map(normalizeInstant).filter(instant => Date.parse(instant) < LAST_REPRESENTABLE_MS).flatMap(instant => [ instant, addMs(instant, 1) ]).filter(instant => !hasPassed(instant, now));
5381
+ return [ ...new Set(instants) ].sort(byInstant);
4813
5382
  }
4814
5383
 
4815
- function coerceRefEnvelope(raw, workflowResource) {
4816
- if (!("_ref" in raw)) return null;
4817
- const r = raw;
4818
- return typeof r._ref != "string" || !workflowResource ? null : {
4819
- id: invariants.toPhysicalGdr(r._ref, workflowResource),
4820
- type: "document"
4821
- };
5384
+ function normalizeInstant(value) {
5385
+ return new Date(Date.parse(value)).toISOString();
4822
5386
  }
4823
5387
 
4824
- function coerceToGdr(raw, workflowResource) {
4825
- return raw == null ? null : typeof raw == "object" ? coerceGdrShape(raw, workflowResource) ?? coerceRefEnvelope(raw, workflowResource) : typeof raw == "string" && workflowResource ? {
4826
- id: invariants.toPhysicalGdr(raw, workflowResource),
4827
- type: "document"
4828
- } : null;
5388
+ async function comparandsOf(args) {
5389
+ const {atom: atom, params: params, dataset: dataset} = args, requirement = atom.requirement, values = requirement !== void 0 && requirement.target.variable === "now" && "value" in requirement ? [ requirement.value ] : [];
5390
+ for (const read of atom.atom.reads) read.variable !== "now" && values.push(await groqConditionDescribe.runGroq({
5391
+ groq: groqConditionDescribe.formatRead(read),
5392
+ params: params,
5393
+ dataset: dataset
5394
+ }));
5395
+ return values;
4829
5396
  }
4830
5397
 
4831
5398
  const NonEmpty = invariants.NonEmptyString, PersistedAssignmentRolesSchema = v__namespace.pipe(v__namespace.array(invariants.NonEmptyString), v__namespace.minLength(1, "assignment roles must not be empty")), UnknownRecord = v__namespace.record(v__namespace.string(), v__namespace.unknown()), PersistedChoiceOptionsSchema = v__namespace.looseObject({
@@ -4880,7 +5447,7 @@ function fieldArm(kind, value) {
4880
5447
  };
4881
5448
  }
4882
5449
 
4883
- const OptionalRefTypes = v__namespace.exactOptional(v__namespace.array(v__namespace.string())), ResolvedFieldEntrySchema = v__namespace.variant("_type", [ v__namespace.looseObject(invariants.tolerantEntries()({
5450
+ const OptionalRefTypes = v__namespace.exactOptional(v__namespace.array(v__namespace.string())), RawResolvedFieldEntrySchema = v__namespace.variant("_type", [ v__namespace.looseObject(invariants.tolerantEntries()({
4884
5451
  ...fieldArm("doc.ref", invariants.fieldValueSchemas["doc.ref"]),
4885
5452
  types: OptionalRefTypes
4886
5453
  })), v__namespace.looseObject(invariants.tolerantEntries()({
@@ -4901,7 +5468,7 @@ const OptionalRefTypes = v__namespace.exactOptional(v__namespace.array(v__namesp
4901
5468
  })), v__namespace.looseObject(invariants.tolerantEntries()({
4902
5469
  ...fieldArm("array", v__namespace.array(UnknownRecord)),
4903
5470
  of: v__namespace.array(PersistedFieldShapeSchema)
4904
- })) ]), EffectOriginSchema = invariants.tolerantObject()({
5471
+ })) ]), ResolvedFieldEntrySchema = v__namespace.pipe(RawResolvedFieldEntrySchema, v__namespace.transform(normalizeNestedAssignmentValues)), EffectOriginSchema = invariants.tolerantObject()({
4905
5472
  kind: v__namespace.literal("action"),
4906
5473
  name: NonEmpty
4907
5474
  }), PendingEffectClaimSchema = invariants.tolerantObject()({
@@ -5762,6 +6329,25 @@ function buildInstanceBase(args) {
5762
6329
  };
5763
6330
  }
5764
6331
 
6332
+ function instanceDocumentForWrite(instance) {
6333
+ const listModel = writesSingularAssignmentLists(instance);
6334
+ return {
6335
+ ...instance,
6336
+ fields: assignmentFieldsForWrite(instance.fields, listModel),
6337
+ stages: assignmentStagesForWrite(instance.stages, listModel)
6338
+ };
6339
+ }
6340
+
6341
+ function memoizedBy(compute) {
6342
+ const cache = /* @__PURE__ */ new Map;
6343
+ return key => {
6344
+ const hit = cache.get(key);
6345
+ if (hit !== void 0) return hit;
6346
+ const value = (async () => compute(key))();
6347
+ return cache.set(key, value), value;
6348
+ };
6349
+ }
6350
+
5765
6351
  async function hydrateSnapshot(args) {
5766
6352
  const {client: client, clientForGdr: clientForGdr, instance: instance, overlay: overlay} = args, loaded = [], visited = /* @__PURE__ */ new Set;
5767
6353
  loaded.push({
@@ -5912,16 +6498,26 @@ function loadCallContext({client: client, instanceId: instanceId, options: optio
5912
6498
 
5913
6499
  async function retryOnRevisionConflict(args) {
5914
6500
  const {client: client, instanceId: instanceId, options: options, commit: commit, onExhausted: onExhausted} = args;
5915
- for (let attempt = 1; attempt <= CONCURRENT_COMMIT_MAX_ATTEMPTS; attempt++) {
5916
- const ctx = await loadCallContext({
6501
+ return retryCommit({
6502
+ load: () => loadCallContext({
5917
6503
  client: client,
5918
6504
  instanceId: instanceId,
5919
6505
  options: options
5920
- });
6506
+ }),
6507
+ commit: commit,
6508
+ shouldRetry: isInstanceCommitRevisionConflict,
6509
+ onExhausted: onExhausted
6510
+ });
6511
+ }
6512
+
6513
+ async function retryCommit(args) {
6514
+ const {load: load, commit: commit, shouldRetry: shouldRetry, onExhausted: onExhausted} = args;
6515
+ for (let attempt = 1; attempt <= CONCURRENT_COMMIT_MAX_ATTEMPTS; attempt++) {
6516
+ const ctx = await load();
5921
6517
  try {
5922
6518
  return await commit(ctx);
5923
6519
  } catch (error) {
5924
- if (!isRevisionConflict(error)) throw error;
6520
+ if (!shouldRetry(error)) throw error;
5925
6521
  }
5926
6522
  }
5927
6523
  throw onExhausted();
@@ -5969,6 +6565,22 @@ async function ctxConditionParams(ctx, opts) {
5969
6565
  }, opts);
5970
6566
  }
5971
6567
 
6568
+ async function ctxNextEvaluationAt(ctx) {
6569
+ if (ctx.instance.completedAt !== void 0) return;
6570
+ const scopeFor = memoizedBy(activityName => ctxConditionParams(ctx, activityName === void 0 ? void 0 : {
6571
+ activityName: activityName
6572
+ })), sites = await stageTimeSites({
6573
+ definition: ctx.definition,
6574
+ stage: ctx.instance.currentStage,
6575
+ scopeFor: scopeFor
6576
+ });
6577
+ if (sites.length !== 0) return nextEvaluationInstant({
6578
+ sites: sites,
6579
+ dataset: ctx.snapshot.docs,
6580
+ now: ctx.now
6581
+ });
6582
+ }
6583
+
5972
6584
  async function ctxEvaluateConditionOutcome({ctx: ctx, condition: condition, opts: opts}) {
5973
6585
  return condition === void 0 ? "satisfied" : invariants.evaluateConditionOutcome({
5974
6586
  condition: condition,
@@ -6597,7 +7209,7 @@ function resolveGuard({guard: guard, instance: instance, stageName: stageName, n
6597
7209
  if (route === null) return null;
6598
7210
  const resource = assertSingleResource(route.targets), types = resolveMatchTypes(route.targets, guard.match.types);
6599
7211
  return {
6600
- doc: compileGuard({
7212
+ docs: compileGuards({
6601
7213
  id: route.guardId,
6602
7214
  resourceType: resource.type,
6603
7215
  resourceId: resource.id,
@@ -6629,19 +7241,63 @@ function resolveGuard({guard: guard, instance: instance, stageName: stageName, n
6629
7241
  async function upsertGuard(args) {
6630
7242
  const {client: client, doc: doc, exists: exists} = args;
6631
7243
  if (!exists) try {
6632
- await client.create(doc, {
6633
- ...SYNC_COMMIT,
6634
- tag: REQUEST_TAG.guardDeploy
6635
- });
6636
- return;
7244
+ return {
7245
+ created: await client.create(doc, {
7246
+ ...SYNC_COMMIT,
7247
+ tag: REQUEST_TAG.guardDeploy
7248
+ })
7249
+ };
6637
7250
  } catch (error) {
6638
7251
  if (!isCreateIdCollision(error)) throw error;
6639
7252
  }
6640
7253
  const {_id: _id, _type: _type, _rev: _rev, _createdAt: _createdAt, _updatedAt: _updatedAt, ...body} = doc;
6641
- await client.patch(doc._id).set(body).commit({
7254
+ return await client.patch(doc._id).set(body).commit({
6642
7255
  ...SYNC_COMMIT,
6643
7256
  tag: REQUEST_TAG.guardRefresh
7257
+ }), {
7258
+ created: void 0
7259
+ };
7260
+ }
7261
+
7262
+ async function retractCreatedGuards(guards) {
7263
+ for (const {client: client, doc: doc} of guards.toReversed()) {
7264
+ if (doc._rev === void 0) return new Error(`Cannot roll back guard ${doc._id}: created document has no revision`);
7265
+ try {
7266
+ const revisionCheck = client.patch(doc._id).set({
7267
+ predicate: doc.predicate
7268
+ }).ifRevisionId(doc._rev);
7269
+ await client.transaction().patch(revisionCheck).delete(doc._id).commit({
7270
+ tag: REQUEST_TAG.guardDeploy
7271
+ });
7272
+ } catch (error) {
7273
+ return error;
7274
+ }
7275
+ }
7276
+ }
7277
+
7278
+ async function deployResolvedGuard(args) {
7279
+ const {client: client, resourceKey: resourceKey, doc: doc, guard: guard, stageArgs: stageArgs, existingByResource: existingByResource} = args, localized = await localizeGuardMetadata({
7280
+ doc: doc,
7281
+ guard: guard,
7282
+ client: client,
7283
+ instance: stageArgs.instance,
7284
+ definition: stageArgs.definition
6644
7285
  });
7286
+ return (await upsertGuard({
7287
+ client: client,
7288
+ doc: localized,
7289
+ exists: existingByResource.get(resourceKey)?.has(doc._id) ?? !1
7290
+ })).created;
7291
+ }
7292
+
7293
+ async function failGuardDeploy(args) {
7294
+ const {cause: cause, stageName: stageName, deployed: deployed, createdGuards: createdGuards, updatedExistingGuard: updatedExistingGuard} = args, rollbackError = updatedExistingGuard ? void 0 : await retractCreatedGuards(createdGuards);
7295
+ throw deployed > 0 && (updatedExistingGuard || rollbackError !== void 0) ? new PartialGuardDeployError({
7296
+ stageName: stageName,
7297
+ deployed: deployed,
7298
+ cause: cause,
7299
+ rollbackError: rollbackError
7300
+ }) : cause;
6645
7301
  }
6646
7302
 
6647
7303
  function resolvedStageGuards(args) {
@@ -6654,10 +7310,12 @@ function resolvedStageGuards(args) {
6654
7310
  now: args.now,
6655
7311
  snapshot: args.snapshot
6656
7312
  });
6657
- resolved !== null && out.push({
6658
- client: args.clientForGdr(resolved.routeGdr),
6659
- resourceKey: invariants.resourceGdr(invariants.resourceFromParsed(resolved.routeGdr)),
6660
- value: resolved.doc,
7313
+ if (resolved === null) continue;
7314
+ const client = args.clientForGdr(resolved.routeGdr), resourceKey = invariants.resourceGdr(invariants.resourceFromParsed(resolved.routeGdr));
7315
+ for (const doc of resolved.docs) out.push({
7316
+ client: client,
7317
+ resourceKey: resourceKey,
7318
+ value: doc,
6661
7319
  guard: guard
6662
7320
  });
6663
7321
  }
@@ -6673,10 +7331,15 @@ function resolvedStageGuardRoutes(args) {
6673
7331
  }, out = [];
6674
7332
  for (const guard of stage?.guards ?? []) {
6675
7333
  const route = resolveGuardRoute(guard, ctx);
6676
- route !== null && out.push({
6677
- client: args.clientForGdr(route.routeGdr),
6678
- resourceKey: invariants.resourceGdr(invariants.resourceFromParsed(route.routeGdr)),
6679
- value: route.guardId
7334
+ if (route === null) continue;
7335
+ const client = args.clientForGdr(route.routeGdr), resourceKey = invariants.resourceGdr(invariants.resourceFromParsed(route.routeGdr));
7336
+ for (const guardId of compiledGuardIds({
7337
+ id: route.guardId,
7338
+ actions: guard.match.actions
7339
+ })) out.push({
7340
+ client: client,
7341
+ resourceKey: resourceKey,
7342
+ value: guardId
6680
7343
  });
6681
7344
  }
6682
7345
  return out;
@@ -6732,26 +7395,30 @@ async function deployStageGuards(args) {
6732
7395
  if (live === void 0 || live.currentStage !== args.stageName || live.abortedAt !== void 0) return;
6733
7396
  const routed = resolvedStageGuards(args), existingByResource = await existingGuardIds(groupByResource(routed));
6734
7397
  let deployed = 0;
7398
+ const createdGuards = [];
7399
+ let updatedExistingGuard = !1;
6735
7400
  for (const {client: client, resourceKey: resourceKey, value: doc, guard: guard} of routed) {
6736
7401
  try {
6737
- const localized = await localizeGuardMetadata({
7402
+ const created = await deployResolvedGuard({
7403
+ client: client,
7404
+ resourceKey: resourceKey,
6738
7405
  doc: doc,
6739
7406
  guard: guard,
6740
- client: client,
6741
- instance: args.instance,
6742
- definition: args.definition
7407
+ stageArgs: args,
7408
+ existingByResource: existingByResource
6743
7409
  });
6744
- await upsertGuard({
7410
+ created === void 0 ? updatedExistingGuard = !0 : createdGuards.push({
6745
7411
  client: client,
6746
- doc: localized,
6747
- exists: existingByResource.get(resourceKey)?.has(doc._id) ?? !1
7412
+ doc: created
6748
7413
  });
6749
7414
  } catch (cause) {
6750
- throw deployed > 0 ? new PartialGuardDeployError({
7415
+ await failGuardDeploy({
7416
+ cause: cause,
6751
7417
  stageName: args.stageName,
6752
7418
  deployed: deployed,
6753
- cause: cause
6754
- }) : cause;
7419
+ createdGuards: createdGuards,
7420
+ updatedExistingGuard: updatedExistingGuard
7421
+ });
6755
7422
  }
6756
7423
  deployed += 1;
6757
7424
  }
@@ -6830,9 +7497,9 @@ async function commitAbort({ctx: ctx, reason: reason, requestRecord: requestReco
6830
7497
  record: requestRecord,
6831
7498
  now: ctx.now
6832
7499
  });
6833
- const at = ctx.now, openEntry = findOpenStageEntry(mutation);
7500
+ const at = ctx.now, openEntry = invariants.findOpenStageEntry(mutation);
6834
7501
  openEntry !== void 0 && (openEntry.exitedAt = at);
6835
- for (const row of liveSubworkflows(mutation)) condemnSubworkflow(row, {
7502
+ for (const row of invariants.liveSubworkflows(mutation)) invariants.condemnSubworkflow(row, {
6836
7503
  at: at,
6837
7504
  reason: "parent instance aborted"
6838
7505
  });
@@ -6983,7 +7650,8 @@ async function persistThenDeploy({ctx: ctx, mutation: mutation, deploy: deploy})
6983
7650
  committedRev: committed._rev,
6984
7651
  restore: instanceStateFields({
6985
7652
  ...ctx.instance,
6986
- minReaderModel: minReaderModelOf(ctx.instance)
7653
+ minReaderModel: minReaderModelOf(ctx.instance),
7654
+ definitionModelVersion: definitionAssignmentModel(ctx.instance)
6987
7655
  }),
6988
7656
  unset: ctx.instance.completedAt === void 0 ? [ "completedAt" ] : [],
6989
7657
  reversible: !spawned,
@@ -7039,7 +7707,7 @@ function subworkflowsOnExit(sub) {
7039
7707
 
7040
7708
  async function spawnSubworkflows({ctx: ctx, mutation: mutation, activity: activity, action: action, sub: sub, actor: actor}) {
7041
7709
  assertSpawnDepth(ctx, activity);
7042
- const openEntry = findOpenStageEntry(mutation);
7710
+ const openEntry = invariants.findOpenStageEntry(mutation);
7043
7711
  if (openEntry === void 0) throw new Error(`Mutation invariant broken: no open StageEntry while spawning on activity "${activity.name}" of ${ctx.instance._id}`);
7044
7712
  const definition = await requireChildDefinition({
7045
7713
  ctx: ctx,
@@ -7054,7 +7722,7 @@ async function spawnSubworkflows({ctx: ctx, mutation: mutation, activity: activi
7054
7722
  activity: activity.name,
7055
7723
  action: action.name,
7056
7724
  instanceId: ctx.instance._id
7057
- }), live = liveSubworkflows(mutation).filter(r => r.activity === activity.name && r.action === action.name && r.definition === sub.definition.name && r.abortPending === void 0);
7725
+ }), live = invariants.liveSubworkflows(mutation).filter(r => r.activity === activity.name && r.action === action.name && r.definition === sub.definition.name && r.abortPending === void 0);
7058
7726
  adoptRediscoveredRows({
7059
7727
  mutation: mutation,
7060
7728
  live: live,
@@ -7193,8 +7861,8 @@ function condemnOnExitCohorts({mutation: mutation, stage: stage, exitedEntry: ex
7193
7861
  action: action.name
7194
7862
  })));
7195
7863
  for (const cohort of abortCohorts) {
7196
- const owed = liveSubworkflows(mutation).filter(row => row.activity === cohort.activity && row.action === cohort.action && row.stageEntry === exitedEntry._key);
7197
- for (const row of owed) condemnSubworkflow(row, {
7864
+ const owed = invariants.liveSubworkflows(mutation).filter(row => row.activity === cohort.activity && row.action === cohort.action && row.stageEntry === exitedEntry._key);
7865
+ for (const row of owed) invariants.condemnSubworkflow(row, {
7198
7866
  at: at,
7199
7867
  reason: `stage "${stage.name}" exited with the child still live (onExit: "abort" on action "${cohort.action}" of activity "${cohort.activity}")`
7200
7868
  });
@@ -7557,7 +8225,7 @@ function emitStageTransitioned({ctx: ctx, nextStage: nextStage, at: at, via: via
7557
8225
  }
7558
8226
 
7559
8227
  function exitOpenStage({mutation: mutation, stage: stage, at: at}) {
7560
- const priorEntry = findOpenStageEntry(mutation);
8228
+ const priorEntry = invariants.findOpenStageEntry(mutation);
7561
8229
  priorEntry !== void 0 && (priorEntry.exitedAt = at, condemnOnExitCohorts({
7562
8230
  mutation: mutation,
7563
8231
  stage: stage,
@@ -8078,91 +8746,6 @@ async function callerBoundVarsForCall(args) {
8078
8746
  });
8079
8747
  }
8080
8748
 
8081
- function requirementDescriptor(requirement) {
8082
- return {
8083
- name: requirement.name,
8084
- ...requirement.title !== void 0 ? {
8085
- title: requirement.title
8086
- } : {},
8087
- ...requirement.description !== void 0 ? {
8088
- description: requirement.description
8089
- } : {}
8090
- };
8091
- }
8092
-
8093
- function subjectDenialLabels(denied) {
8094
- return denied.map(d => `${d.permission} on ${d.subject} (${d.resource})`);
8095
- }
8096
-
8097
- class ActionDisabledError extends invariants.WorkflowError {
8098
- reason;
8099
- activity;
8100
- action;
8101
- constructor(args) {
8102
- super("action-disabled", formatDisabledReason({
8103
- activity: args.activity,
8104
- action: args.action,
8105
- reason: args.reason
8106
- })), this.name = "ActionDisabledError", this.reason = args.reason, this.activity = args.activity,
8107
- this.action = args.action;
8108
- }
8109
- }
8110
-
8111
- class StartNotAllowedError extends invariants.WorkflowError {
8112
- definition;
8113
- unmetRequirements;
8114
- constructor(args) {
8115
- super("start-not-allowed", `startInstance refused definition "${args.definition}": ${args.unmetRequirements.map(requirement => requirement.title ?? requirement.name).join(", ")}. Pre-flight the verdict with evaluateStart.`),
8116
- this.name = "StartNotAllowedError", this.definition = args.definition, this.unmetRequirements = args.unmetRequirements;
8117
- }
8118
- }
8119
-
8120
- function actionRendering(action) {
8121
- const kind = action.disabledReason?.kind;
8122
- return kind === "filter-failed" ? "absent" : action.triggered === !0 || kind === "cascade-fired" ? "automation" : "button";
8123
- }
8124
-
8125
- const disabledReasonDetail = {
8126
- "filter-failed": r => `action filter returned false${r.detail ? ` (${r.detail})` : ""}`,
8127
- "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`,
8128
- "activity-not-active": r => `activity status is "${r.status}"`,
8129
- "stage-terminal": r => `stage "${r.stage}" is terminal`,
8130
- "instance-completed": r => `instance completed at ${r.completedAt}`,
8131
- "instance-aborted": r => `instance aborted at ${r.abortedAt}`,
8132
- "requirements-unmet": r => `unmet requirement(s): ${r.unmetRequirements.map(requirement => requirement.name).join(", ")}`,
8133
- "subject-permission-denied": r => `missing subject permission(s): ${subjectDenialLabels(r.denied).join(", ")}`
8134
- };
8135
-
8136
- function actionDisabledDetail(reason) {
8137
- return disabledReasonDetail[reason.kind](reason);
8138
- }
8139
-
8140
- function formatDisabledReason({activity: activity, action: action, reason: reason}) {
8141
- return `Action "${activity}:${action}" is not allowed: ${actionDisabledDetail(reason)}`;
8142
- }
8143
-
8144
- class EditFieldDeniedError extends invariants.WorkflowError {
8145
- reason;
8146
- target;
8147
- constructor(args) {
8148
- super("edit-field-denied", formatEditDisabledReason(args.target, args.reason)),
8149
- this.name = "EditFieldDeniedError", this.reason = args.reason, this.target = args.target;
8150
- }
8151
- }
8152
-
8153
- const editDisabledReasonDetail = {
8154
- "not-editable": () => "field is not declared editable",
8155
- "instance-completed": r => `instance completed at ${r.completedAt}`,
8156
- "instance-aborted": r => `instance aborted at ${r.abortedAt}`,
8157
- "edit-window-closed": r => `edit window closed (${r.detail})`,
8158
- "editor-not-permitted": r => `editor not permitted (${r.predicate})`
8159
- };
8160
-
8161
- function formatEditDisabledReason(target, reason) {
8162
- const detail = editDisabledReasonDetail[reason.kind](reason), where = target.activity !== void 0 ? `${target.activity}.${target.field}` : target.field;
8163
- return `Field "${target.scope}:${where}" is not editable: ${detail}`;
8164
- }
8165
-
8166
8749
  async function fireAction(args) {
8167
8750
  const {client: client, instanceId: instanceId, activity: activity, action: action, params: params, requestRecord: requestRecord, options: options} = args;
8168
8751
  return retryOnRevisionConflict({
@@ -8330,7 +8913,7 @@ async function commitAction({ctx: ctx, activityName: activityName, actionName: a
8330
8913
  actionName: actionName,
8331
8914
  callerParams: callerParams,
8332
8915
  options: options
8333
- }), mutation = startMutation(ctx.instance);
8916
+ }), mutation = startRevisionRetryMutation(ctx.instance);
8334
8917
  recordProcessedRequest({
8335
8918
  mutation: mutation,
8336
8919
  record: requestRecord,
@@ -8496,7 +9079,7 @@ async function primeInitialStage(args) {
8496
9079
  })
8497
9080
  });
8498
9081
  const terminal = isTerminalStage(stage), committed = await client.patch(instance._id).set({
8499
- stages: [ initialStageEntry ],
9082
+ stages: assignmentStagesForWrite([ initialStageEntry ], writesSingularAssignmentLists(instance)),
8500
9083
  lastChangedAt: now,
8501
9084
  ...terminal ? {
8502
9085
  completedAt: now
@@ -8554,32 +9137,59 @@ async function runCascadeHop({client: client, instanceId: instanceId, actor: act
8554
9137
  settlingCohorts: settlingCohorts
8555
9138
  } : {},
8556
9139
  memberRolesLoader: memberRolesLoader
8557
- }, ctx = instance === void 0 ? await loadContext({
8558
- client: client,
8559
- instanceId: instanceId,
8560
- options: contextOptions
8561
- }) : await buildEngineContext({
8562
- client: client,
8563
- instance: instance,
8564
- definition: parseDefinitionSnapshot(instance),
8565
- ...contextOptions
9140
+ };
9141
+ let preloadedInstance = instance;
9142
+ return retryCommit({
9143
+ load: async () => {
9144
+ if (preloadedInstance === void 0) return loadContext({
9145
+ client: client,
9146
+ instanceId: instanceId,
9147
+ options: contextOptions
9148
+ });
9149
+ const loaded = preloadedInstance;
9150
+ return preloadedInstance = void 0, buildEngineContext({
9151
+ client: client,
9152
+ instance: loaded,
9153
+ definition: parseDefinitionSnapshot(loaded),
9154
+ ...contextOptions
9155
+ });
9156
+ },
9157
+ commit: ctx => commitCascadeHop({
9158
+ ctx: ctx,
9159
+ actor: actor
9160
+ }),
9161
+ shouldRetry: isInstanceCommitRevisionConflict,
9162
+ onExhausted: () => new ConcurrentCascadeError({
9163
+ instanceId: instanceId,
9164
+ attempts: CONCURRENT_COMMIT_MAX_ATTEMPTS
9165
+ })
8566
9166
  });
9167
+ }
9168
+
9169
+ async function commitCascadeHop({ctx: ctx, actor: actor}) {
8567
9170
  if (isTerminal(ctx)) return {
8568
9171
  moved: !1
8569
9172
  };
8570
- const stage = findStage(ctx.definition, ctx.instance.currentStage), mutation = startMutation(ctx.instance), fired = await runTriggeredActions({
9173
+ const stage = findStage(ctx.definition, ctx.instance.currentStage), mutation = startRevisionRetryMutation(ctx.instance), fired = await runTriggeredActions({
8571
9174
  ctx: ctx,
8572
9175
  mutation: mutation,
8573
9176
  stage: stage
8574
9177
  }), hopCtx = liveViewContext(ctx, mutation), transition = await pickTransition(hopCtx, stage);
8575
- return transition === void 0 ? (fired.fires > 0 && await persistThenMaybeRefresh({
8576
- ctx: ctx,
8577
- mutation: mutation,
8578
- stageName: stage.name,
8579
- didChangeState: fired.ranFieldOps
8580
- }), {
8581
- moved: !1
8582
- }) : (await commitStageMove({
9178
+ if (transition === void 0) {
9179
+ const nextEvaluationAt = await ctxNextEvaluationAt(hopCtx);
9180
+ return fired.fires > 0 && await persistThenMaybeRefresh({
9181
+ ctx: ctx,
9182
+ mutation: mutation,
9183
+ stageName: stage.name,
9184
+ didChangeState: fired.ranFieldOps
9185
+ }), {
9186
+ moved: !1,
9187
+ ...nextEvaluationAt !== void 0 ? {
9188
+ nextEvaluationAt: nextEvaluationAt
9189
+ } : {}
9190
+ };
9191
+ }
9192
+ return await commitStageMove({
8583
9193
  ctx: ctx,
8584
9194
  mutation: mutation,
8585
9195
  fromStage: stage,
@@ -8589,7 +9199,7 @@ async function runCascadeHop({client: client, instanceId: instanceId, actor: act
8589
9199
  actor: actor
8590
9200
  }), {
8591
9201
  moved: !0
8592
- });
9202
+ };
8593
9203
  }
8594
9204
 
8595
9205
  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}) {
@@ -8605,8 +9215,7 @@ async function cascadeAutoTransitions({client: client, instanceId: instanceId, a
8605
9215
  clock: clock,
8606
9216
  executionContext: executionContext,
8607
9217
  telemetry: telemetry
8608
- });
8609
- if (!(await runCascadeHop({
9218
+ }), hop = await runCascadeHop({
8610
9219
  client: client,
8611
9220
  instanceId: instanceId,
8612
9221
  actor: actor,
@@ -8621,7 +9230,13 @@ async function cascadeAutoTransitions({client: client, instanceId: instanceId, a
8621
9230
  ...drained.drained ? {} : {
8622
9231
  instance: drained.instance
8623
9232
  }
8624
- })).moved) return count;
9233
+ });
9234
+ if (!hop.moved) return {
9235
+ cascaded: count,
9236
+ ...hop.nextEvaluationAt !== void 0 ? {
9237
+ nextEvaluationAt: hop.nextEvaluationAt
9238
+ } : {}
9239
+ };
8625
9240
  if (count++, count >= CASCADE_LIMIT) throw new CascadeLimitError({
8626
9241
  instanceId: instanceId,
8627
9242
  limit: CASCADE_LIMIT
@@ -8640,7 +9255,7 @@ async function drainCondemnedChildren({client: client, instanceId: instanceId, a
8640
9255
  instance: void 0,
8641
9256
  drained: !1
8642
9257
  };
8643
- const condemned = condemnedSubworkflows(instance);
9258
+ const condemned = invariants.condemnedSubworkflows(instance);
8644
9259
  if (condemned.length === 0) return {
8645
9260
  instance: instance,
8646
9261
  drained: !1
@@ -8754,7 +9369,7 @@ async function loadInstancesById(client, ids) {
8754
9369
  }
8755
9370
 
8756
9371
  function terminalResolution(child) {
8757
- const status = resolvedChildStatus(child);
9372
+ const status = invariants.resolvedChildStatus(child);
8758
9373
  if (!(status === void 0 || child.completedAt === void 0 || child.completedAt === null)) return {
8759
9374
  at: child.completedAt,
8760
9375
  stage: child.currentStage,
@@ -8921,6 +9536,8 @@ async function recordOrphanedPropagation({ctx: ctx, mutation: mutation, child: c
8921
9536
  function startMutation(instance) {
8922
9537
  return {
8923
9538
  minReaderModel: minReaderModelOf(instance),
9539
+ definitionSnapshot: instance.definitionSnapshot,
9540
+ definitionModelVersion: definitionAssignmentModel(instance),
8924
9541
  currentStage: instance.currentStage,
8925
9542
  fields: (instance.fields ?? []).map(s => ({
8926
9543
  ...s
@@ -8968,11 +9585,17 @@ function startMutation(instance) {
8968
9585
  };
8969
9586
  }
8970
9587
 
9588
+ function startRevisionRetryMutation(instance) {
9589
+ const mutation = startMutation(instance);
9590
+ return mutation.commitErrorMapper = error => isRetryableInstanceCommitConflict(error) ? new InstanceCommitRevisionConflict(error) : error,
9591
+ mutation;
9592
+ }
9593
+
8971
9594
  function instanceStateFields(src) {
8972
- const state = {
9595
+ const listModel = isAssignmentListModel(src.definitionModelVersion), state = {
8973
9596
  currentStage: src.currentStage,
8974
- fields: src.fields,
8975
- stages: src.stages,
9597
+ fields: assignmentFieldsForWrite(src.fields, listModel),
9598
+ stages: assignmentStagesForWrite(src.stages, listModel),
8976
9599
  subworkflows: src.subworkflows ?? [],
8977
9600
  pendingEffects: src.pendingEffects,
8978
9601
  effectHistory: src.effectHistory,
@@ -8989,7 +9612,10 @@ function instanceStateFields(src) {
8989
9612
  return {
8990
9613
  ...modelStampFor({
8991
9614
  documentType: "instance",
8992
- document: state,
9615
+ document: {
9616
+ ...state,
9617
+ definitionSnapshot: src.definitionSnapshot
9618
+ },
8993
9619
  storedMinReaderModel: src.minReaderModel
8994
9620
  }),
8995
9621
  ...state
@@ -9024,11 +9650,11 @@ async function persist(ctx, mutation) {
9024
9650
  ...instanceStateFields(mutation),
9025
9651
  lastChangedAt: ctx.now
9026
9652
  }, pendingCreates = mutation.pendingCreates;
9027
- if (pendingCreates.length === 0) return ctx.client.patch(ctx.instance._id).set(set).ifRevisionId(ctx.instance._rev).commit(SYNC_COMMIT);
9653
+ if (pendingCreates.length === 0) return commitInstanceWrite(() => ctx.client.patch(ctx.instance._id).set(set).ifRevisionId(ctx.instance._rev).commit(SYNC_COMMIT), mutation.commitErrorMapper);
9028
9654
  const tx = ctx.client.transaction();
9029
- for (const {body: body} of pendingCreates) tx.create(body);
9655
+ for (const {body: body} of pendingCreates) tx.create(instanceDocumentForWrite(body));
9030
9656
  tx.patch(ctx.client.patch(ctx.instance._id).set(set).ifRevisionId(ctx.instance._rev)),
9031
- await tx.commit(), mutation.pendingCreates = [];
9657
+ await commitInstanceWrite(() => tx.commit(), mutation.commitErrorMapper), mutation.pendingCreates = [];
9032
9658
  const actorForPriming = ctx.actor, settlingCohorts = new Set(ctx.settlingCohorts).add(ctx.instance._id);
9033
9659
  for (const {body: body} of pendingCreates) try {
9034
9660
  const stepArgs = spawnStepArgs(ctx, {
@@ -9055,6 +9681,14 @@ async function persist(ctx, mutation) {
9055
9681
  return reloaded;
9056
9682
  }
9057
9683
 
9684
+ async function commitInstanceWrite(write, mapError) {
9685
+ try {
9686
+ return await write();
9687
+ } catch (error) {
9688
+ throw mapError?.(error) ?? error;
9689
+ }
9690
+ }
9691
+
9058
9692
  async function settleSpawnBatch(ctx, args) {
9059
9693
  try {
9060
9694
  await propagateSpawnBatch({
@@ -9096,7 +9730,7 @@ function stampExecutionContext(ctx, mutation) {
9096
9730
  }
9097
9731
 
9098
9732
  function currentStageEntry(mutation) {
9099
- const entry = findOpenStageEntry(mutation);
9733
+ const entry = invariants.findOpenStageEntry(mutation);
9100
9734
  if (entry === void 0) throw new Error(`Mutation invariant broken: no current (un-exited) StageEntry for currentStage "${mutation.currentStage}"`);
9101
9735
  return entry;
9102
9736
  }
@@ -9121,7 +9755,7 @@ function requireMutationActivityEntry(mutation, activity) {
9121
9755
  }
9122
9756
 
9123
9757
  function findCurrentStageEntry(instance) {
9124
- return findOpenStageEntry(instance);
9758
+ return invariants.findOpenStageEntry(instance);
9125
9759
  }
9126
9760
 
9127
9761
  function findCurrentActivities(instance) {
@@ -9317,7 +9951,7 @@ async function commitCompleteEffect({ctx: ctx, effectKey: effectKey, status: sta
9317
9951
  outputs: outputs,
9318
9952
  memberRoles: ctx.memberRoles,
9319
9953
  roleAliases: ctx.definition.roleAliases
9320
- }), mutation = startMutation(ctx.instance);
9954
+ }), mutation = startRevisionRetryMutation(ctx.instance);
9321
9955
  recordProcessedRequest({
9322
9956
  mutation: mutation,
9323
9957
  record: requestRecord,
@@ -9406,7 +10040,7 @@ async function queueEffects({ctx: ctx, mutation: mutation, effects: effects, ori
9406
10040
  const now = ctx.now, liveCtx = {
9407
10041
  ...ctx,
9408
10042
  instance: materializeInstance(ctx.instance, mutation)
9409
- }, stageEntryKey = opts?.stageEntryKey ?? findOpenStageEntry(liveCtx.instance)?._key, params = await ctxConditionParams(liveCtx, {
10043
+ }, stageEntryKey = opts?.stageEntryKey ?? invariants.findOpenStageEntry(liveCtx.instance)?._key, params = await ctxConditionParams(liveCtx, {
9410
10044
  ...opts?.activityName !== void 0 ? {
9411
10045
  activityName: opts.activityName
9412
10046
  } : {},
@@ -9496,7 +10130,7 @@ async function commitReport({ctx: ctx, effectKey: effectKey, claimToken: claimTo
9496
10130
  effect: pending.name,
9497
10131
  issues: [ "a mid-dispatch report must carry at least one field op — there is nothing to commit" ]
9498
10132
  });
9499
- const mutation = startMutation(ctx.instance);
10133
+ const mutation = startRevisionRetryMutation(ctx.instance);
9500
10134
  return recordProcessedRequest({
9501
10135
  mutation: mutation,
9502
10136
  record: requestRecord,
@@ -9532,7 +10166,7 @@ async function commitReport({ctx: ctx, effectKey: effectKey, claimToken: claimTo
9532
10166
  };
9533
10167
  }
9534
10168
 
9535
- const RESET_ACTIVITY_TARGETS = [ "active", "skipped" ];
10169
+ const RESET_ACTIVITY_TARGETS = groqConditionDescribe._exhaustiveOptions()([ "active", "skipped" ]);
9536
10170
 
9537
10171
  function isResetActivityTarget(value) {
9538
10172
  return RESET_ACTIVITY_TARGETS.includes(value);
@@ -9561,7 +10195,7 @@ async function commitResetActivity({ctx: ctx, activity: activity, to: to, reques
9561
10195
  }), isTerminal(ctx)) return {
9562
10196
  fired: !1
9563
10197
  };
9564
- const mutation = startMutation(ctx.instance), openStage2 = findOpenStageEntry(mutation), entry = findCurrentActivityEntry(mutation, activity);
10198
+ const mutation = startMutation(ctx.instance), openStage2 = invariants.findOpenStageEntry(mutation), entry = invariants.findCurrentActivityEntry(mutation, activity);
9565
10199
  if (openStage2 === void 0 || entry === void 0) throw new invariants.ContractViolationError(`resetActivity: activity "${activity}" is not in the current stage of instance "${ctx.instance._id}"`);
9566
10200
  const from = entry.status;
9567
10201
  if (from === to) return {
@@ -9640,10 +10274,14 @@ async function whatIfFireAction(args) {
9640
10274
  function replayable(args) {
9641
10275
  const {instance: instance, stage: stage, action: action} = args;
9642
10276
  if (terminalState(instance) !== "in-flight" || (action.params ?? []).length > 0 || action.spawn !== void 0) return !1;
9643
- const primed = new Set((findOpenStageEntry(instance)?.activities ?? []).map(e => e.name));
10277
+ const primed = new Set((invariants.findOpenStageEntry(instance)?.activities ?? []).map(e => e.name));
9644
10278
  return (stage.activities ?? []).every(declared => primed.has(declared.name)) ? !(stage.activities ?? []).flatMap(declared => declared.actions ?? []).some(sibling => invariants.isCascadeFired(sibling) && sibling.spawn !== void 0) : !1;
9645
10279
  }
9646
10280
 
10281
+ function clientGuardDereference(client) {
10282
+ return async ({_ref: _ref}) => await client.getDocument(invariants.toBareId(_ref)) ?? null;
10283
+ }
10284
+
9647
10285
  async function buildFieldInsights({sites: sites, snapshot: snapshot}) {
9648
10286
  const insights = [];
9649
10287
  for (const field of fieldsReadAcross(sites)) {
@@ -9799,6 +10437,7 @@ async function evaluateInstance(args) {
9799
10437
  guards: guards,
9800
10438
  now: now,
9801
10439
  resourceGrants: resourceGrants,
10440
+ guardDereference: clientGuardDereference(client),
9802
10441
  ...localPrincipalId !== void 0 ? {
9803
10442
  localPrincipalId: localPrincipalId
9804
10443
  } : {},
@@ -9811,16 +10450,6 @@ async function evaluateInstance(args) {
9811
10450
  });
9812
10451
  }
9813
10452
 
9814
- function memoizedByName(render) {
9815
- const rendered = /* @__PURE__ */ new Map;
9816
- return activityName => {
9817
- const hit = rendered.get(activityName);
9818
- if (hit !== void 0) return hit;
9819
- const scope = render(activityName);
9820
- return rendered.set(activityName, scope), scope;
9821
- };
9822
- }
9823
-
9824
10453
  async function callerBoundProjectionScopes(args) {
9825
10454
  const can = await advisoryCan({
9826
10455
  instance: args.instance,
@@ -9841,13 +10470,17 @@ async function callerBoundProjectionScopes(args) {
9841
10470
  };
9842
10471
  return {
9843
10472
  scope: await renderConditionScope(args.scopeSource, opts),
9844
- scopeForActivity: memoizedByName(activityName => renderConditionScope(args.scopeSource, {
10473
+ scopeForActivity: memoizedBy(activityName => renderConditionScope(args.scopeSource, {
9845
10474
  activityName: activityName,
9846
10475
  ...opts
9847
10476
  }))
9848
10477
  };
9849
10478
  }
9850
10479
 
10480
+ function clockSitesOf(instance, sites) {
10481
+ return terminalState(instance) !== "in-flight" ? [] : sites.filter(entry => readsNowIn(entry.insight.analysis.reads));
10482
+ }
10483
+
9851
10484
  function currentStageOf(instance, definition) {
9852
10485
  try {
9853
10486
  return findStage(definition, instance.currentStage);
@@ -9897,12 +10530,13 @@ async function evaluateFromSnapshot(args) {
9897
10530
  grants: grants,
9898
10531
  attributes: attributes,
9899
10532
  identity: anchorIdentity
9900
- }), cascadeScopeForActivity = memoizedByName(activityName => renderConditionScope(scopeSource, {
10533
+ }), cascadeScopeForActivity = memoizedBy(activityName => renderConditionScope(scopeSource, {
9901
10534
  activityName: activityName
9902
- })), currentActivityEntries = findOpenStageEntry(instance)?.activities ?? [], guardDenial = await instanceGuardReason({
10535
+ })), currentActivityEntries = invariants.findOpenStageEntry(instance)?.activities ?? [], guardDenial = await instanceGuardReason({
9903
10536
  instance: instance,
9904
10537
  identity: anchorIdentity,
9905
- guards: args.guards
10538
+ guards: args.guards,
10539
+ dereference: args.guardDereference
9906
10540
  }), subjectDenials = await forecastSubjectDenials({
9907
10541
  instance: instance,
9908
10542
  snapshot: snapshot,
@@ -9959,6 +10593,10 @@ async function evaluateFromSnapshot(args) {
9959
10593
  scopeForActivity: scopeForActivity,
9960
10594
  guardDenial: editGuardDenial,
9961
10595
  sites: sites
10596
+ }), nextEvaluationAt = await nextEvaluationInstant({
10597
+ sites: clockSitesOf(instance, sites),
10598
+ dataset: snapshot.docs,
10599
+ now: now
9962
10600
  });
9963
10601
  return {
9964
10602
  instance: instance,
@@ -9973,7 +10611,10 @@ async function evaluateFromSnapshot(args) {
9973
10611
  sites: sites,
9974
10612
  snapshot: snapshot
9975
10613
  }),
9976
- autonomy: autonomy
10614
+ autonomy: autonomy,
10615
+ ...nextEvaluationAt !== void 0 ? {
10616
+ nextEvaluationAt: nextEvaluationAt
10617
+ } : {}
9977
10618
  };
9978
10619
  }
9979
10620
 
@@ -10121,7 +10762,8 @@ async function evaluateActivity(args) {
10121
10762
  subjectPermissionReason: subjectPermissionReason,
10122
10763
  requirementsReason: requirementsReason,
10123
10764
  sites: sites,
10124
- fireConsequence: a => fireConsequence(activity, a)
10765
+ fireConsequence: a => fireConsequence(activity, a),
10766
+ assignment: invariants.assignmentMembers(statusEntry?.fields ?? [])
10125
10767
  }));
10126
10768
  return {
10127
10769
  activity: activity,
@@ -10131,7 +10773,7 @@ async function evaluateActivity(args) {
10131
10773
  classification: invariants.deriveExecutorClassification(activity),
10132
10774
  autonomy: autonomy,
10133
10775
  pendingOnActor: status === "active" && assigned,
10134
- scopedOut: isFilterScopedOut({
10776
+ scopedOut: invariants.isFilterScopedOut({
10135
10777
  status: status,
10136
10778
  startedAt: statusEntry?.startedAt
10137
10779
  }),
@@ -10278,19 +10920,31 @@ function fireableActionVerdict({args: args, insights: insights}) {
10278
10920
  reason: args.requirementsReason,
10279
10921
  ...insights
10280
10922
  });
10281
- const {insight: insight} = insights;
10282
- return action.filter !== void 0 && insight !== void 0 && insight.outcome !== "satisfied" ? disabled({
10923
+ const filterFailure = filterFailureVerdict(args, insights);
10924
+ return filterFailure !== void 0 ? filterFailure : {
10925
+ ...actionEvaluationIdentity(action),
10926
+ allowed: !0,
10927
+ ...insights
10928
+ };
10929
+ }
10930
+
10931
+ function filterFailureVerdict(args, insights) {
10932
+ const {action: action} = args, {insight: insight} = insights;
10933
+ if (action.filter === void 0 || insight === void 0 || insight.outcome === "satisfied") return;
10934
+ const holderGate = insight.blockedBy.some(entry => isAssignedGate(entry.atom)) ? {
10935
+ holders: [ ...invariants.activeAssignmentMembers(args.assignment) ]
10936
+ } : void 0;
10937
+ return disabled({
10283
10938
  action: action,
10284
10939
  reason: {
10285
10940
  kind: "filter-failed",
10286
10941
  filter: action.filter
10287
10942
  },
10288
- ...insights
10289
- }) : {
10290
- ...actionEvaluationIdentity(action),
10291
- allowed: !0,
10292
- ...insights
10293
- };
10943
+ ...insights,
10944
+ ...holderGate === void 0 ? {} : {
10945
+ holderGate: holderGate
10946
+ }
10947
+ });
10294
10948
  }
10295
10949
 
10296
10950
  function actionEvaluationIdentity(action) {
@@ -10306,12 +10960,13 @@ function semanticsOf(node) {
10306
10960
  };
10307
10961
  }
10308
10962
 
10309
- async function instanceGuardReason({instance: instance, identity: identity, guards: guards}) {
10963
+ async function instanceGuardReason({instance: instance, identity: identity, guards: guards, dereference: dereference}) {
10310
10964
  if (guards === void 0 || guards.length === 0) return;
10311
10965
  const denied = await instanceWriteDenials({
10312
10966
  instance: instance,
10313
10967
  guards: guards,
10314
- identity: identity
10968
+ identity: identity,
10969
+ dereference: dereference ?? (() => Promise.resolve(null))
10315
10970
  });
10316
10971
  if (denied.length !== 0) return {
10317
10972
  kind: "mutation-guard-denied",
@@ -10342,10 +10997,29 @@ function disabled(args) {
10342
10997
  } : {},
10343
10998
  ...args.whenInsight !== void 0 ? {
10344
10999
  whenInsight: args.whenInsight
11000
+ } : {},
11001
+ ...args.holderGate !== void 0 ? {
11002
+ holderGate: args.holderGate
10345
11003
  } : {}
10346
11004
  };
10347
11005
  }
10348
11006
 
11007
+ const ASSIGNMENT_KINDS = '["assignee", "assignees"]', HAS_USER = '(count(value[@.type == "user"]) > 0 || value.type == "user")', HAS_MEMBER = "(count(value) > 0 || defined(value.type))";
11008
+
11009
+ function assignmentActivityArm(state) {
11010
+ const assignmentFields = `fields[_type in ${ASSIGNMENT_KINDS}]`;
11011
+ 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`;
11012
+ }
11013
+
11014
+ function assignmentPrefilter(assignment, params) {
11015
+ if (assignment.userId.length === 0) throw new invariants.ContractViolationError("instancesQuery: assignment.userId must be non-empty");
11016
+ const states = [ ...new Set(assignment.states ?? [ "unrouted", "routed", "held" ]) ];
11017
+ if (states.length === 0) throw new invariants.ContractViolationError("instancesQuery: assignment.states must not be empty");
11018
+ if (states.some(state => state !== "unrouted" && state !== "routed" && state !== "held")) throw new invariants.ContractViolationError("instancesQuery: assignment.states contains an unknown state");
11019
+ return params.assignmentUserId = assignment.userId, params.assignmentRoles = [ ...new Set(assignment.roles ?? []) ].sort(),
11020
+ `count(stages[!defined(exitedAt)].activities[status == "active" && (${states.map(assignmentActivityArm).join(" || ")})]) > 0`;
11021
+ }
11022
+
10349
11023
  function inFlightFilter() {
10350
11024
  return "!defined(completedAt)";
10351
11025
  }
@@ -10385,7 +11059,7 @@ function compiledConditions(filter, params) {
10385
11059
  const conditions = [ `_type == "${WORKFLOW_INSTANCE_TYPE}"`, invariants.tagScopeFilter() ];
10386
11060
  filter.includeCompleted !== !0 && conditions.push(inFlightFilter()), filter.definition !== void 0 && (conditions.push("definition == $definition"),
10387
11061
  params.definition = filter.definition), filter.stage !== void 0 && (conditions.push("currentStage == $stage"),
10388
- params.stage = filter.stage);
11062
+ params.stage = filter.stage), filter.assignment !== void 0 && conditions.push(assignmentPrefilter(filter.assignment, params));
10389
11063
  const cursor = beforeArm(filter, params);
10390
11064
  cursor !== void 0 && conditions.push(cursor);
10391
11065
  const arms = [ documentArm(filter, params), idsArm(filter, params) ].filter(arm => arm !== void 0);
@@ -10488,6 +11162,222 @@ function stripClaim(entry) {
10488
11162
  return rest;
10489
11163
  }
10490
11164
 
11165
+ function isProjectRole(value) {
11166
+ return invariants.isRecord(value) && typeof value.name == "string";
11167
+ }
11168
+
11169
+ async function projectRoleNames(request, projectId) {
11170
+ const result = await request({
11171
+ uri: `/projects/${encodeURIComponent(projectId)}/roles`
11172
+ });
11173
+ if (!Array.isArray(result) || !result.every(isProjectRole)) throw new Error(`Project role catalog response is invalid for project "${projectId}"`);
11174
+ return new Set(result.map(role => role.name));
11175
+ }
11176
+
11177
+ function memberRoleNames(member, projectId) {
11178
+ 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}"`);
11179
+ return member.roles.map(role => role.name);
11180
+ }
11181
+
11182
+ function heldRoleNames(members, projectId) {
11183
+ return new Set(members.flatMap(member => member.isRobot === !0 ? [] : memberRoleNames(member, projectId)));
11184
+ }
11185
+
11186
+ function addRoles(collector, args) {
11187
+ for (const role of args.roles ?? []) collector.references.push({
11188
+ definition: collector.definition,
11189
+ path: args.path,
11190
+ role: role
11191
+ });
11192
+ }
11193
+
11194
+ function visitAssigneeValue(collector, args) {
11195
+ const members = Array.isArray(args.value) ? args.value : [ args.value ];
11196
+ for (const [index, member] of members.entries()) !invariants.isRecord(member) || member.type !== "role" || typeof member.role != "string" || collector.references.push({
11197
+ definition: collector.definition,
11198
+ path: `${args.path}[${index}].role`,
11199
+ role: member.role
11200
+ });
11201
+ }
11202
+
11203
+ function visitObjectValue(collector, args) {
11204
+ for (const shape of args.fields ?? []) visitShapeValue(collector, {
11205
+ path: `${args.path}.${shape.name}`,
11206
+ shape: shape,
11207
+ value: args.value[shape.name]
11208
+ });
11209
+ }
11210
+
11211
+ function visitArrayValue(collector, args) {
11212
+ for (const [index, row] of args.rows.entries()) if (invariants.isRecord(row)) for (const shape of args.shapes ?? []) visitShapeValue(collector, {
11213
+ path: `${args.path}[${index}].${shape.name}`,
11214
+ shape: shape,
11215
+ value: row[shape.name]
11216
+ });
11217
+ }
11218
+
11219
+ function visitAssignmentValue(collector, args) {
11220
+ args.kind === "assignee" || args.kind === "assignees" ? visitAssigneeValue(collector, args) : args.kind === "object" && invariants.isRecord(args.value) ? visitObjectValue(collector, {
11221
+ ...args,
11222
+ value: args.value
11223
+ }) : args.kind === "array" && Array.isArray(args.value) && visitArrayValue(collector, {
11224
+ path: args.path,
11225
+ rows: args.value,
11226
+ shapes: args.of
11227
+ });
11228
+ }
11229
+
11230
+ function visitShapeValue(collector, args) {
11231
+ visitAssignmentValue(collector, {
11232
+ path: args.path,
11233
+ kind: args.shape.type,
11234
+ value: args.value,
11235
+ fields: args.shape.fields,
11236
+ of: args.shape.of
11237
+ });
11238
+ }
11239
+
11240
+ function visitField(collector, args) {
11241
+ addRoles(collector, {
11242
+ path: `${args.path}.roles`,
11243
+ roles: args.field.roles
11244
+ });
11245
+ for (const shape of args.field.fields ?? args.field.of ?? []) visitFieldShape(collector, {
11246
+ parentPath: args.path,
11247
+ shape: shape
11248
+ });
11249
+ args.field.initialValue?.type === "literal" && visitAssignmentValue(collector, {
11250
+ path: `${args.path}.initialValue.value`,
11251
+ kind: args.field.type,
11252
+ value: args.field.initialValue.value,
11253
+ fields: args.field.fields,
11254
+ of: args.field.of
11255
+ });
11256
+ }
11257
+
11258
+ function visitFieldShape(collector, args) {
11259
+ const path = `${args.parentPath}.${args.shape.name}`;
11260
+ addRoles(collector, {
11261
+ path: `${path}.roles`,
11262
+ roles: args.shape.roles
11263
+ });
11264
+ for (const child of args.shape.fields ?? args.shape.of ?? []) visitFieldShape(collector, {
11265
+ parentPath: path,
11266
+ shape: child
11267
+ });
11268
+ }
11269
+
11270
+ function rolesFromConditions(definition) {
11271
+ return conditionSitesOf(definition).flatMap(site => {
11272
+ try {
11273
+ return groqConditionDescribe.analyzeCondition(site.condition).atoms.flatMap(atom => rolesGateOf(atom) ?? []);
11274
+ } catch {
11275
+ return [];
11276
+ }
11277
+ });
11278
+ }
11279
+
11280
+ function visitRoleAliases(collector, aliases) {
11281
+ for (const [role, fulfillers] of Object.entries(aliases ?? {})) role !== invariants.UNIVERSAL_ROLE_ALIAS_KEY && collector.references.push({
11282
+ definition: collector.definition,
11283
+ path: `roleAliases.${role}`,
11284
+ role: role
11285
+ }), addRoles(collector, {
11286
+ path: `roleAliases.${role}`,
11287
+ roles: fulfillers
11288
+ });
11289
+ }
11290
+
11291
+ function visitFields(collector, args) {
11292
+ for (const [index, field] of (args.fields ?? []).entries()) visitField(collector, {
11293
+ path: `${args.parentPath}[${index}]`,
11294
+ field: field
11295
+ });
11296
+ }
11297
+
11298
+ function visitAction(collector, args) {
11299
+ addRoles(collector, {
11300
+ path: `${args.path}.roles`,
11301
+ roles: args.action.roles
11302
+ });
11303
+ for (const [effectIndex, effect] of (args.action.effects ?? []).entries()) for (const shape of effect.outputs ?? []) visitFieldShape(collector, {
11304
+ parentPath: `${args.path}.effects[${effectIndex}].outputs`,
11305
+ shape: shape
11306
+ });
11307
+ }
11308
+
11309
+ function visitStage(collector, args) {
11310
+ const stagePath = `stages[${args.stageIndex}]`;
11311
+ visitFields(collector, {
11312
+ fields: args.stage.fields,
11313
+ parentPath: `${stagePath}.fields`
11314
+ });
11315
+ for (const [activityIndex, activity] of (args.stage.activities ?? []).entries()) {
11316
+ const activityPath = `${stagePath}.activities[${activityIndex}]`;
11317
+ visitFields(collector, {
11318
+ fields: activity.fields,
11319
+ parentPath: `${activityPath}.fields`
11320
+ });
11321
+ for (const [actionIndex, action] of (activity.actions ?? []).entries()) visitAction(collector, {
11322
+ action: action,
11323
+ path: `${activityPath}.actions[${actionIndex}]`
11324
+ });
11325
+ }
11326
+ }
11327
+
11328
+ function addConditionReferences(collector, definition) {
11329
+ for (const role of rolesFromConditions(definition)) collector.references.push({
11330
+ definition: collector.definition,
11331
+ path: "generated role condition",
11332
+ role: role
11333
+ });
11334
+ }
11335
+
11336
+ function definitionRoleReferences(definition) {
11337
+ const collector = {
11338
+ references: [],
11339
+ definition: definition.name
11340
+ };
11341
+ visitRoleAliases(collector, definition.roleAliases), visitFields(collector, {
11342
+ fields: definition.fields,
11343
+ parentPath: "fields"
11344
+ });
11345
+ for (const [stageIndex, stage] of definition.stages.entries()) visitStage(collector, {
11346
+ stage: stage,
11347
+ stageIndex: stageIndex
11348
+ });
11349
+ return addConditionReferences(collector, definition), collector.references;
11350
+ }
11351
+
11352
+ function definitionRoleNames(definition) {
11353
+ return [ ...new Set(definitionRoleReferences(definition).map(reference => reference.role)) ];
11354
+ }
11355
+
11356
+ function roleCatalogProjectId(client, resource) {
11357
+ if (resource.type === "dataset") return invariants.datasetResourceParts(resource.id).projectId;
11358
+ const projectId = client.config?.().projectId;
11359
+ if (typeof projectId == "string" && projectId.length > 0) return projectId;
11360
+ 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`);
11361
+ }
11362
+
11363
+ async function validateDefinitionRoles(args) {
11364
+ const references = args.definitions.flatMap(definitionRoleReferences);
11365
+ if (references.length === 0) /* @__PURE__ */ return new Map;
11366
+ if (args.client.request === void 0) throw new Error("workflow.deployDefinitions: definitions reference project roles, but the client cannot read the project role catalog");
11367
+ const projectId = roleCatalogProjectId(args.client, args.workflowResource), [known, members] = await Promise.all([ projectRoleNames(args.client.request, projectId), requestProjectMembers(args.client.request, projectId) ]), unknown = references.filter(reference => !known.has(reference.role));
11368
+ if (unknown.length > 0) {
11369
+ const lines = unknown.map(({definition: definition, path: path, role: role}) => ` - ${definition}: ${path} references unknown role "${role}"`);
11370
+ throw new Error(`workflow.deployDefinitions: unknown project roles for project "${projectId}":\n${lines.join(`\n`)}`);
11371
+ }
11372
+ const held = heldRoleNames(members, projectId), warnings = /* @__PURE__ */ new Map;
11373
+ for (const {definition: definition, role: role} of references) {
11374
+ if (held.has(role)) continue;
11375
+ const message = `Project role "${role}" has no current user holder; work routed to it has no recipient.`, definitionWarnings = warnings.get(definition) ?? [];
11376
+ definitionWarnings.includes(message) || definitionWarnings.push(message), warnings.set(definition, definitionWarnings);
11377
+ }
11378
+ return warnings;
11379
+ }
11380
+
10491
11381
  async function sortByDependencies({client: client, definitions: definitions, tag: tag}) {
10492
11382
  const byName = /* @__PURE__ */ new Map;
10493
11383
  for (const def of definitions) {
@@ -10788,7 +11678,7 @@ async function commitEdit({ctx: ctx, target: target, mode: mode, value: value, r
10788
11678
  site: site,
10789
11679
  mode: mode,
10790
11680
  value: value
10791
- }), mutation = startMutation(ctx.instance);
11681
+ }), mutation = startRevisionRetryMutation(ctx.instance);
10792
11682
  recordProcessedRequest({
10793
11683
  mutation: mutation,
10794
11684
  record: requestRecord,
@@ -10932,14 +11822,14 @@ async function runCommitVerb(params) {
10932
11822
  client: client,
10933
11823
  instanceId: instanceId,
10934
11824
  tag: tag
10935
- });
11825
+ }), now = clock();
10936
11826
  return runDeduped({
10937
11827
  client: client,
10938
11828
  tag: tag,
10939
11829
  instanceId: instanceId,
10940
11830
  record: record,
10941
11831
  before: before,
10942
- now: clock(),
11832
+ now: now,
10943
11833
  actor: context.actor,
10944
11834
  clientForGdr: context.clientForGdr,
10945
11835
  refSurface: context.refSurface,
@@ -10950,6 +11840,7 @@ async function runCommitVerb(params) {
10950
11840
  run: () => run({
10951
11841
  ...context,
10952
11842
  clock: clock,
11843
+ now: now,
10953
11844
  record: record,
10954
11845
  before: before
10955
11846
  })
@@ -10973,7 +11864,7 @@ async function runDeduped(args) {
10973
11864
  client: client,
10974
11865
  instanceId: instanceId,
10975
11866
  tag: tag
10976
- }), {cascaded: cascaded, instance: instance} = await cascadeAndReload({
11867
+ }), settled = await cascadeAndReload({
10977
11868
  client: client,
10978
11869
  tag: tag,
10979
11870
  instanceId: instanceId,
@@ -10985,11 +11876,10 @@ async function runDeduped(args) {
10985
11876
  executionContext: executionContext,
10986
11877
  telemetry: telemetry
10987
11878
  });
10988
- return {
10989
- instance: instance,
10990
- cascaded: cascaded,
10991
- changed: instance._rev !== pre._rev
10992
- };
11879
+ return settledResult({
11880
+ ...settled,
11881
+ changed: settled.instance._rev !== pre._rev
11882
+ });
10993
11883
  };
10994
11884
  if (record !== void 0 && before !== void 0 && findProcessedRequest({
10995
11885
  instance: before,
@@ -11005,7 +11895,7 @@ async function runDeduped(args) {
11005
11895
  }
11006
11896
 
11007
11897
  async function cascade({client: client, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, executionContext: executionContext, telemetry: telemetry, overlay: overlay}) {
11008
- const count = await cascadeAutoTransitions({
11898
+ const settled = await cascadeAutoTransitions({
11009
11899
  client: client,
11010
11900
  instanceId: instanceId,
11011
11901
  actor: actor,
@@ -11041,13 +11931,13 @@ async function cascade({client: client, instanceId: instanceId, actor: actor, cl
11041
11931
  ...telemetry !== void 0 ? {
11042
11932
  telemetry: telemetry
11043
11933
  } : {}
11044
- }), count;
11934
+ }), settled;
11045
11935
  }
11046
11936
 
11047
11937
  async function cascadeAndReload(args) {
11048
11938
  const {client: client, tag: tag, instanceId: instanceId, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock} = args;
11049
11939
  return {
11050
- cascaded: await cascade({
11940
+ ...await cascade({
11051
11941
  client: client,
11052
11942
  instanceId: instanceId,
11053
11943
  actor: actor,
@@ -11070,6 +11960,84 @@ async function cascadeAndReload(args) {
11070
11960
  };
11071
11961
  }
11072
11962
 
11963
+ async function unchangedNextEvaluationAt(args) {
11964
+ const {client: client, instance: instance, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, now: now} = args;
11965
+ if (terminalState(instance) !== "in-flight") return;
11966
+ const ctx = await buildEngineContext({
11967
+ client: client,
11968
+ instance: instance,
11969
+ definition: parseDefinitionSnapshot(instance),
11970
+ clientForGdr: clientForGdr,
11971
+ refSurface: refSurface,
11972
+ memberRolesLoader: memberRolesLoader,
11973
+ clock: () => now,
11974
+ ...actor !== void 0 ? {
11975
+ actor: actor
11976
+ } : {},
11977
+ ...args.executionContext !== void 0 ? {
11978
+ executionContext: args.executionContext
11979
+ } : {},
11980
+ ...args.telemetry !== void 0 ? {
11981
+ telemetry: args.telemetry
11982
+ } : {}
11983
+ });
11984
+ return ctxNextEvaluationAt(ctx);
11985
+ }
11986
+
11987
+ async function adminOverrideResult(args) {
11988
+ const {client: client, tag: tag, instanceId: instanceId, fired: fired, now: now, executionContext: executionContext, telemetry: telemetry, ...scope} = args, optional = {
11989
+ ...executionContext !== void 0 ? {
11990
+ executionContext: executionContext
11991
+ } : {},
11992
+ ...telemetry !== void 0 ? {
11993
+ telemetry: telemetry
11994
+ } : {}
11995
+ }, settled = fired ? await cascade({
11996
+ client: client,
11997
+ instanceId: instanceId,
11998
+ ...scope,
11999
+ ...optional
12000
+ }) : {
12001
+ cascaded: 0
12002
+ }, instance = await reload({
12003
+ client: client,
12004
+ instanceId: instanceId,
12005
+ tag: tag
12006
+ });
12007
+ return settledResult({
12008
+ ...settled,
12009
+ instance: instance,
12010
+ changed: fired,
12011
+ ...fired ? {} : {
12012
+ nextEvaluationAt: await unchangedNextEvaluationAt({
12013
+ client: client,
12014
+ instance: instance,
12015
+ now: now,
12016
+ actor: scope.actor,
12017
+ clientForGdr: scope.clientForGdr,
12018
+ refSurface: scope.refSurface,
12019
+ memberRolesLoader: scope.memberRolesLoader,
12020
+ ...optional
12021
+ })
12022
+ }
12023
+ });
12024
+ }
12025
+
12026
+ function settledResult(args) {
12027
+ const {instance: instance, cascaded: cascaded, changed: changed, ranOps: ranOps, nextEvaluationAt: nextEvaluationAt} = args;
12028
+ return {
12029
+ instance: instance,
12030
+ cascaded: cascaded,
12031
+ changed: changed,
12032
+ ...ranOps !== void 0 ? {
12033
+ ranOps: ranOps
12034
+ } : {},
12035
+ ...nextEvaluationAt !== void 0 ? {
12036
+ nextEvaluationAt: nextEvaluationAt
12037
+ } : {}
12038
+ };
12039
+ }
12040
+
11073
12041
  async function settleStart(args) {
11074
12042
  const runArgs = {
11075
12043
  client: args.client,
@@ -11131,7 +12099,7 @@ async function resumeStart(args) {
11131
12099
  instance: existing,
11132
12100
  initialFieldCount: initialFieldCount,
11133
12101
  viaSpawn: !1
11134
- })), cascaded = await settleStart({
12102
+ })), settled = await settleStart({
11135
12103
  ...settlementArgs,
11136
12104
  instanceId: existing._id,
11137
12105
  ...completesStart ? {
@@ -11142,11 +12110,11 @@ async function resumeStart(args) {
11142
12110
  instanceId: existing._id,
11143
12111
  tag: args.tag
11144
12112
  });
11145
- return {
12113
+ return settledResult({
12114
+ ...settled,
11146
12115
  instance: instance,
11147
- cascaded: cascaded,
11148
12116
  changed: instance._rev !== existing._rev
11149
- };
12117
+ });
11150
12118
  }
11151
12119
 
11152
12120
  function engineOptionsForActor({actor: actor, clock: clock, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, executionContext: executionContext, telemetry: telemetry}) {
@@ -11245,7 +12213,7 @@ async function dispatchGatedWrite(args) {
11245
12213
  ...resourceClients !== void 0 ? {
11246
12214
  resourceClients: resourceClients
11247
12215
  } : {}
11248
- }), ranOps = await apply(args.before, evaluation), {cascaded: cascaded, instance: instance} = await cascadeAndReload({
12216
+ }), ranOps = await apply(args.before, evaluation), settled = await cascadeAndReload({
11249
12217
  client: client,
11250
12218
  tag: tag,
11251
12219
  instanceId: instanceId,
@@ -11257,14 +12225,13 @@ async function dispatchGatedWrite(args) {
11257
12225
  executionContext: args.executionContext,
11258
12226
  telemetry: args.telemetry
11259
12227
  });
11260
- return {
11261
- instance: instance,
11262
- cascaded: cascaded,
12228
+ return settledResult({
12229
+ ...settled,
11263
12230
  changed: !0,
11264
12231
  ...ranOps !== void 0 ? {
11265
12232
  ranOps: ranOps
11266
12233
  } : {}
11267
- };
12234
+ });
11268
12235
  }
11269
12236
 
11270
12237
  function guardDeniedError(instanceId, reason) {
@@ -11478,6 +12445,10 @@ async function abortInstances(args) {
11478
12445
  return aborted;
11479
12446
  }
11480
12447
 
12448
+ function lintDeadActivityInputSeeds(definition) {
12449
+ 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.`)));
12450
+ }
12451
+
11481
12452
  function projectStartSliceRow(instance) {
11482
12453
  const subject = (instance.fields ?? []).find(field => field._type === "subject")?.value;
11483
12454
  return {
@@ -11505,14 +12476,18 @@ const workflow = {
11505
12476
  definitions.forEach(validateDefinition), assertReaderModelAcknowledgement(args.expectedMinReaderModel, {
11506
12477
  requiredMinReaderModel: requiredDefinitionReaderModel(definitions)
11507
12478
  });
11508
- const ordered = await sortByDependencies({
12479
+ const roleWarnings = await validateDefinitionRoles({
12480
+ client: client,
12481
+ definitions: definitions,
12482
+ workflowResource: workflowResource
12483
+ }), ordered = await sortByDependencies({
11509
12484
  client: client,
11510
12485
  definitions: definitions,
11511
12486
  tag: tag
11512
12487
  }), results = [], deployed = [], tx = client.transaction();
11513
12488
  let hasWrites = !1;
11514
12489
  for (const def of ordered) {
11515
- const lint = lintEffectOutputs(def), warnings = lint.length > 0 ? {
12490
+ const lint = [ ...lintEffectOutputs(def), ...lintDeadActivityInputSeeds(def), ...roleWarnings.get(def.name) ?? [] ], warnings = lint.length > 0 ? {
11516
12491
  warnings: lint
11517
12492
  } : {}, latest = await loadLatestDeployed({
11518
12493
  client: client,
@@ -11605,12 +12580,27 @@ const workflow = {
11605
12580
  return runCommitVerb({
11606
12581
  args: args,
11607
12582
  op: "fireAction",
11608
- run: async ({access: access, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, record: record, before: before}) => {
11609
- if (findCurrentActivityEntry(before, activity) === void 0 && idempotent === !0) return {
12583
+ run: async ({access: access, actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, now: now, record: record, before: before}) => {
12584
+ if (invariants.findCurrentActivityEntry(before, activity) === void 0 && idempotent === !0) return settledResult({
11610
12585
  instance: before,
11611
12586
  cascaded: 0,
11612
- changed: !1
11613
- };
12587
+ changed: !1,
12588
+ nextEvaluationAt: await unchangedNextEvaluationAt({
12589
+ client: client,
12590
+ instance: before,
12591
+ actor: actor,
12592
+ clientForGdr: clientForGdr,
12593
+ refSurface: refSurface,
12594
+ memberRolesLoader: memberRolesLoader,
12595
+ now: now,
12596
+ ...executionContext !== void 0 ? {
12597
+ executionContext: executionContext
12598
+ } : {},
12599
+ ...args.telemetry !== void 0 ? {
12600
+ telemetry: args.telemetry
12601
+ } : {}
12602
+ })
12603
+ });
11614
12604
  const result = await dispatchGatedWrite({
11615
12605
  client: client,
11616
12606
  tag: tag,
@@ -11747,7 +12737,7 @@ const workflow = {
11747
12737
  });
11748
12738
  },
11749
12739
  completeEffect: async rawArgs => {
11750
- 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({
12740
+ 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({
11751
12741
  idempotencyKey: args.idempotencyKey,
11752
12742
  op: "completeEffect",
11753
12743
  idempotencyTtlMs: args.idempotencyTtlMs
@@ -11757,7 +12747,7 @@ const workflow = {
11757
12747
  tag: tag,
11758
12748
  instanceId: instanceId,
11759
12749
  record: record,
11760
- now: clock(),
12750
+ now: now,
11761
12751
  ...operationContext,
11762
12752
  clock: clock,
11763
12753
  executionContext: executionContext,
@@ -11792,7 +12782,7 @@ const workflow = {
11792
12782
  executionContext: executionContext,
11793
12783
  telemetry: args.telemetry
11794
12784
  })
11795
- }), {cascaded: cascaded, instance: instance} = await cascadeAndReload({
12785
+ }), settled = await cascadeAndReload({
11796
12786
  client: client,
11797
12787
  tag: tag,
11798
12788
  instanceId: instanceId,
@@ -11802,22 +12792,21 @@ const workflow = {
11802
12792
  telemetry: args.telemetry
11803
12793
  });
11804
12794
  return resolveTelemetry(args.telemetry).log(WorkflowEffectCompleted, {
11805
- ...definitionHashFragment(instance.pinnedContentHash),
12795
+ ...definitionHashFragment(settled.instance.pinnedContentHash),
11806
12796
  instanceId: instanceId,
11807
12797
  effect: completion.effect,
11808
12798
  status: status,
11809
12799
  origin: completion.origin,
11810
- cascaded: cascaded
11811
- }), {
11812
- instance: instance,
11813
- cascaded: cascaded,
12800
+ cascaded: settled.cascaded
12801
+ }), settledResult({
12802
+ ...settled,
11814
12803
  changed: !0
11815
- };
12804
+ });
11816
12805
  }
11817
12806
  });
11818
12807
  },
11819
12808
  commitEffectOps: async rawArgs => {
11820
- 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({
12809
+ 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({
11821
12810
  idempotencyKey: args.idempotencyKey,
11822
12811
  op: "commitEffectOps",
11823
12812
  idempotencyTtlMs: args.idempotencyTtlMs
@@ -11828,7 +12817,7 @@ const workflow = {
11828
12817
  tag: tag,
11829
12818
  instanceId: instanceId,
11830
12819
  record: record,
11831
- now: clock(),
12820
+ now: now,
11832
12821
  ...operationContext,
11833
12822
  clock: clock,
11834
12823
  executionContext: executionContext,
@@ -11848,7 +12837,7 @@ const workflow = {
11848
12837
  executionContext: executionContext,
11849
12838
  telemetry: args.telemetry
11850
12839
  })
11851
- }), {cascaded: cascaded, instance: instance} = await cascadeAndReload({
12840
+ }), settled = await cascadeAndReload({
11852
12841
  client: client,
11853
12842
  tag: tag,
11854
12843
  instanceId: instanceId,
@@ -11858,15 +12847,14 @@ const workflow = {
11858
12847
  telemetry: args.telemetry
11859
12848
  });
11860
12849
  return resolveTelemetry(args.telemetry).log(WorkflowEffectStateReported, {
11861
- ...definitionHashFragment(instance.pinnedContentHash),
12850
+ ...definitionHashFragment(settled.instance.pinnedContentHash),
11862
12851
  instanceId: instanceId,
11863
12852
  effect: report.effect,
11864
- cascaded: cascaded
11865
- }), {
11866
- instance: instance,
11867
- cascaded: cascaded,
12853
+ cascaded: settled.cascaded
12854
+ }), settledResult({
12855
+ ...settled,
11868
12856
  changed: !0
11869
- };
12857
+ });
11870
12858
  }
11871
12859
  });
11872
12860
  },
@@ -11879,9 +12867,10 @@ const workflow = {
11879
12867
  await assertInstanceWriteAllowed({
11880
12868
  instance: current,
11881
12869
  guards: guards,
11882
- identity: invariants.lakePrincipalId(access)
12870
+ identity: invariants.lakePrincipalId(access),
12871
+ dereference: clientGuardDereference(client)
11883
12872
  });
11884
- const {cascaded: cascaded, instance: instance} = await cascadeAndReload({
12873
+ const settled = await cascadeAndReload({
11885
12874
  client: client,
11886
12875
  tag: tag,
11887
12876
  instanceId: instanceId,
@@ -11891,21 +12880,20 @@ const workflow = {
11891
12880
  telemetry: args.telemetry
11892
12881
  });
11893
12882
  return resolveTelemetry(args.telemetry).log(WorkflowInstanceTicked, {
11894
- ...definitionHashFragment(instance.pinnedContentHash),
12883
+ ...definitionHashFragment(settled.instance.pinnedContentHash),
11895
12884
  instanceId: instanceId,
11896
- cascaded: cascaded
11897
- }), {
11898
- instance: instance,
11899
- cascaded: cascaded,
11900
- changed: instance._rev !== current._rev
11901
- };
12885
+ cascaded: settled.cascaded
12886
+ }), settledResult({
12887
+ ...settled,
12888
+ changed: settled.instance._rev !== current._rev
12889
+ });
11902
12890
  },
11903
12891
  setStage: async rawArgs => {
11904
12892
  const args = taggedScope(rawArgs, REQUEST_TAG.setStage), {client: client, tag: tag, instanceId: instanceId, targetStage: targetStage, reason: reason, executionContext: executionContext} = args;
11905
12893
  return runCommitVerb({
11906
12894
  args: args,
11907
12895
  op: "setStage",
11908
- run: async ({actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, record: record, before: before}) => {
12896
+ run: async ({actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, now: now, record: record, before: before}) => {
11909
12897
  const result = await setStage({
11910
12898
  client: client,
11911
12899
  instanceId: instanceId,
@@ -11925,34 +12913,29 @@ const workflow = {
11925
12913
  executionContext: executionContext,
11926
12914
  telemetry: args.telemetry
11927
12915
  })
11928
- }), cascaded = result.fired ? await cascade({
12916
+ });
12917
+ return resolveTelemetry(args.telemetry).log(WorkflowStageSet, {
12918
+ ...definitionHashFragment(before.pinnedContentHash),
12919
+ instanceId: instanceId,
12920
+ changed: result.fired
12921
+ }), adminOverrideResult({
11929
12922
  client: client,
12923
+ tag: tag,
11930
12924
  instanceId: instanceId,
12925
+ fired: result.fired,
11931
12926
  actor: actor,
11932
12927
  clientForGdr: clientForGdr,
11933
12928
  refSurface: refSurface,
11934
12929
  memberRolesLoader: memberRolesLoader,
11935
12930
  clock: clock,
12931
+ now: now,
11936
12932
  ...executionContext !== void 0 ? {
11937
12933
  executionContext: executionContext
11938
12934
  } : {},
11939
12935
  ...args.telemetry !== void 0 ? {
11940
12936
  telemetry: args.telemetry
11941
12937
  } : {}
11942
- }) : 0;
11943
- return resolveTelemetry(args.telemetry).log(WorkflowStageSet, {
11944
- ...definitionHashFragment(before.pinnedContentHash),
11945
- instanceId: instanceId,
11946
- changed: result.fired
11947
- }), {
11948
- instance: await reload({
11949
- client: client,
11950
- instanceId: instanceId,
11951
- tag: tag
11952
- }),
11953
- cascaded: cascaded,
11954
- changed: result.fired
11955
- };
12938
+ });
11956
12939
  }
11957
12940
  });
11958
12941
  },
@@ -11985,7 +12968,7 @@ const workflow = {
11985
12968
  ...definitionHashFragment(before.pinnedContentHash),
11986
12969
  instanceId: instanceId,
11987
12970
  changed: changed
11988
- }), {
12971
+ }), settledResult({
11989
12972
  instance: await reload({
11990
12973
  client: client,
11991
12974
  instanceId: instanceId,
@@ -11993,7 +12976,7 @@ const workflow = {
11993
12976
  }),
11994
12977
  cascaded: 0,
11995
12978
  changed: changed
11996
- };
12979
+ });
11997
12980
  }
11998
12981
  });
11999
12982
  },
@@ -12003,7 +12986,7 @@ const workflow = {
12003
12986
  return runCommitVerb({
12004
12987
  args: args,
12005
12988
  op: "resetActivity",
12006
- run: async ({actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, record: record, before: before}) => {
12989
+ run: async ({actor: actor, clientForGdr: clientForGdr, refSurface: refSurface, memberRolesLoader: memberRolesLoader, clock: clock, now: now, record: record, before: before}) => {
12007
12990
  const result = await resetActivity({
12008
12991
  client: client,
12009
12992
  instanceId: instanceId,
@@ -12021,34 +13004,29 @@ const workflow = {
12021
13004
  executionContext: executionContext,
12022
13005
  telemetry: args.telemetry
12023
13006
  })
12024
- }), cascaded = result.fired ? await cascade({
13007
+ });
13008
+ return resolveTelemetry(args.telemetry).log(WorkflowActivityReset, {
13009
+ ...definitionHashFragment(before.pinnedContentHash),
13010
+ instanceId: instanceId,
13011
+ changed: result.fired
13012
+ }), adminOverrideResult({
12025
13013
  client: client,
13014
+ tag: tag,
12026
13015
  instanceId: instanceId,
13016
+ fired: result.fired,
12027
13017
  actor: actor,
12028
13018
  clientForGdr: clientForGdr,
12029
13019
  refSurface: refSurface,
12030
13020
  memberRolesLoader: memberRolesLoader,
12031
13021
  clock: clock,
13022
+ now: now,
12032
13023
  ...executionContext !== void 0 ? {
12033
13024
  executionContext: executionContext
12034
13025
  } : {},
12035
13026
  ...args.telemetry !== void 0 ? {
12036
13027
  telemetry: args.telemetry
12037
13028
  } : {}
12038
- }) : 0;
12039
- return resolveTelemetry(args.telemetry).log(WorkflowActivityReset, {
12040
- ...definitionHashFragment(before.pinnedContentHash),
12041
- instanceId: instanceId,
12042
- changed: result.fired
12043
- }), {
12044
- instance: await reload({
12045
- client: client,
12046
- instanceId: instanceId,
12047
- tag: tag
12048
- }),
12049
- cascaded: cascaded,
12050
- changed: result.fired
12051
- };
13029
+ });
12052
13030
  }
12053
13031
  });
12054
13032
  },
@@ -12381,8 +13359,8 @@ async function startFreshInstance(args) {
12381
13359
  extraHistory: fieldDiscards
12382
13360
  } : {}
12383
13361
  });
12384
- await client.create(base, SYNC_COMMIT);
12385
- const cascaded = await settleStart({
13362
+ await client.create(instanceDocumentForWrite(base), SYNC_COMMIT);
13363
+ const settled = await settleStart({
12386
13364
  client: client,
12387
13365
  tag: tag,
12388
13366
  instanceId: id,
@@ -12404,15 +13382,15 @@ async function startFreshInstance(args) {
12404
13382
  viaSpawn: !1
12405
13383
  }))
12406
13384
  });
12407
- return {
13385
+ return settledResult({
13386
+ ...settled,
12408
13387
  instance: await reload({
12409
13388
  client: client,
12410
13389
  instanceId: id,
12411
13390
  tag: tag
12412
13391
  }),
12413
- cascaded: cascaded,
12414
13392
  changed: !0
12415
- };
13393
+ });
12416
13394
  }
12417
13395
 
12418
13396
  function assertInitialStageExists(definition) {
@@ -12587,7 +13565,7 @@ function missingHandlerMessage(info) {
12587
13565
  }
12588
13566
 
12589
13567
  async function verifyDeployedDefinitionsInternal(args) {
12590
- const {tag: tag, effectHandlers: effectHandlers, missingHandler: missingHandler, logger: logger} = args, client = withRequestTag(args.client, REQUEST_TAG.verifyDefinitions);
13568
+ const {tag: tag, handlers: handlers, missingHandler: missingHandler, logger: logger} = args, client = withRequestTag(args.client, REQUEST_TAG.verifyDefinitions);
12591
13569
  invariants.validateTag(tag);
12592
13570
  const log = logger("verifyDeployedDefinitions"), definitions = (await client.fetch(definitionsListGroq("asc"), {
12593
13571
  tag: tag
@@ -12597,7 +13575,7 @@ async function verifyDeployedDefinitionsInternal(args) {
12597
13575
  version: def.version,
12598
13576
  _id: def._id
12599
13577
  }), walkEffectNames(def, (name, location) => {
12600
- if (effectHandlers[name] !== void 0) return;
13578
+ if (handlers[name] !== void 0) return;
12601
13579
  const key = `${name}@@${def._id}`;
12602
13580
  let bucket = missingByName.get(key);
12603
13581
  bucket === void 0 && (bucket = {
@@ -12656,7 +13634,7 @@ function isCancelledCompletion(data) {
12656
13634
  }
12657
13635
 
12658
13636
  async function drainEffectsInternal(args) {
12659
- 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({
13637
+ const {tag: tag, workflowResource: workflowResource, instanceId: instanceId, handlers: handlers, missingHandler: missingHandler, logger: logger, handlerClient: rawHandlerClient, handlerResourceClients: rawHandlerResourceClients} = args, cascadeTelemetry = drainCascadeTelemetry(args.telemetry), {client: client, resourceClients: resourceClients} = taggedScope(args, REQUEST_TAG.drain), leaseMs = args.leaseMs ?? DEFAULT_EFFECT_LEASE_MS, clock = args.clock ?? wallClock, handlerClient = effectHandlerClient(rawHandlerClient), handlerResourceClients = effectHandlerResolver(rawHandlerResourceClients), routeGdr = buildClientForGdr({
12660
13638
  client: handlerClient,
12661
13639
  workflowResource: workflowResource,
12662
13640
  resourceClients: handlerResourceClients
@@ -12676,7 +13654,7 @@ async function drainEffectsInternal(args) {
12676
13654
  resourceClients: resourceClients
12677
13655
  } : {},
12678
13656
  instanceId: instanceId,
12679
- effectHandlers: effectHandlers,
13657
+ handlers: handlers,
12680
13658
  missingHandler: missingHandler,
12681
13659
  logger: logger,
12682
13660
  log: log,
@@ -12708,7 +13686,7 @@ async function drainOneCandidate(pass) {
12708
13686
  skippedKeys: skippedKeys
12709
13687
  });
12710
13688
  if (candidate === void 0) return !1;
12711
- const handler = pass.effectHandlers[candidate.name];
13689
+ const handler = pass.handlers[candidate.name];
12712
13690
  if (handler === void 0) return await assertSkippableOrThrow({
12713
13691
  missingHandler: pass.missingHandler,
12714
13692
  candidate: candidate,
@@ -13077,7 +14055,7 @@ function createInstanceSession(args) {
13077
14055
  } : {}
13078
14056
  };
13079
14057
  previewOp(site, preview);
13080
- const visit = site.scope === "workflow" ? void 0 : findOpenStageEntry(instance)?._key, row = {
14058
+ const visit = site.scope === "workflow" ? void 0 : invariants.findOpenStageEntry(instance)?._key, row = {
13081
14059
  preview: preview,
13082
14060
  ...visit !== void 0 ? {
13083
14061
  visit: visit
@@ -13086,7 +14064,7 @@ function createInstanceSession(args) {
13086
14064
  return previews.set(key, preview.mode === "append" ? [ ...rows, row ] : [ row ]),
13087
14065
  key;
13088
14066
  }, optimisticSelf = () => {
13089
- const visit = findOpenStageEntry(instance)?._key;
14067
+ const visit = invariants.findOpenStageEntry(instance)?._key;
13090
14068
  for (const [key, rows] of previews) {
13091
14069
  const live = rows.filter(row => row.visit === void 0 || row.visit === visit), target = live[0]?.preview.target;
13092
14070
  target === void 0 || previewSiteOf({
@@ -13117,6 +14095,7 @@ function createInstanceSession(args) {
13117
14095
  snapshot: snapshotFrom(held, normalizedSelf),
13118
14096
  guards: guards,
13119
14097
  resourceGrants: resourceGrants,
14098
+ guardDereference: clientGuardDereference(evalScope.client),
13120
14099
  ...localPrincipalId !== void 0 ? {
13121
14100
  localPrincipalId: localPrincipalId
13122
14101
  } : {},
@@ -13131,7 +14110,7 @@ function createInstanceSession(args) {
13131
14110
  } : {}
13132
14111
  });
13133
14112
  }, settleAfterApply = async ({actor: actor, held: held, ranOps: ranOps, scope: scope, memberRolesLoader: memberRolesLoader}) => {
13134
- const cascaded = await cascadeHeld({
14113
+ const settled = await cascadeHeld({
13135
14114
  scope: scope,
13136
14115
  actor: actor,
13137
14116
  held: held,
@@ -13141,14 +14120,14 @@ function createInstanceSession(args) {
13141
14120
  client: scope.client,
13142
14121
  instanceId: instance._id,
13143
14122
  tag: tag
13144
- }), {
14123
+ }), settledResult({
14124
+ ...settled,
13145
14125
  instance: instance,
13146
- cascaded: cascaded,
13147
14126
  changed: !0,
13148
14127
  ...ranOps !== void 0 ? {
13149
14128
  ranOps: ranOps
13150
14129
  } : {}
13151
- };
14130
+ });
13152
14131
  }, cascadeHeld = ({scope: scope, actor: actor, held: held, memberRolesLoader: memberRolesLoader}) => cascade({
13153
14132
  client: scope.client,
13154
14133
  instanceId: instance._id,
@@ -13259,13 +14238,14 @@ function createInstanceSession(args) {
13259
14238
  identity: invariants.lakePrincipalId({
13260
14239
  actor: actor,
13261
14240
  localPrincipalId: localPrincipalId
13262
- })
14241
+ }),
14242
+ dereference: clientGuardDereference(tickScope.client)
13263
14243
  });
13264
14244
  const before = await reload({
13265
14245
  client: tickScope.client,
13266
14246
  instanceId: instance._id,
13267
14247
  tag: tag
13268
- }), cascaded = await cascadeHeld({
14248
+ }), settled = await cascadeHeld({
13269
14249
  scope: tickScope,
13270
14250
  actor: actor,
13271
14251
  held: held,
@@ -13278,12 +14258,12 @@ function createInstanceSession(args) {
13278
14258
  }), telemetry.log(WorkflowInstanceTicked, {
13279
14259
  ...definitionHashFragment(instance.pinnedContentHash),
13280
14260
  instanceId: instance._id,
13281
- cascaded: cascaded
13282
- }), {
14261
+ cascaded: settled.cascaded
14262
+ }), settledResult({
14263
+ ...settled,
13283
14264
  instance: instance,
13284
- cascaded: cascaded,
13285
14265
  changed: instance._rev !== before._rev
13286
- };
14266
+ });
13287
14267
  });
13288
14268
  },
13289
14269
  fireAction({activity: activity, action: action, params: params}) {
@@ -13433,7 +14413,10 @@ const silentLogger = {
13433
14413
  function createEngine(args) {
13434
14414
  const {workflowResource: workflowResource, clock: clock, tag: tag, executionContext: executionContext} = args, {client: client, resourceClients: resourceClients} = taggedScope(args, REQUEST_TAG.engine);
13435
14415
  invariants.validateTag(tag);
13436
- const effectHandlers = args.effectHandlers ?? {}, missingHandler = args.missingHandler ?? "fail", logger = args.loggerFactory ?? defaultLoggerFactory, telemetry = resolveTelemetry(args.telemetry), EMPTY_DRAIN_LOG_INTERVAL_MS = 6e4;
14416
+ const effects = {
14417
+ handlers: args.effects?.handlers ?? {},
14418
+ missingHandler: args.effects?.missingHandler ?? "fail"
14419
+ }, logger = args.loggerFactory ?? defaultLoggerFactory, telemetry = resolveTelemetry(args.telemetry), EMPTY_DRAIN_LOG_INTERVAL_MS = 6e4;
13437
14420
  let lastEmptyDrainLoggedAt;
13438
14421
  const emptyDrainDue = () => {
13439
14422
  const now = Date.parse((clock ?? wallClock)());
@@ -13464,8 +14447,7 @@ function createEngine(args) {
13464
14447
  client: client,
13465
14448
  tag: tag,
13466
14449
  workflowResource: workflowResource,
13467
- effectHandlers: effectHandlers,
13468
- missingHandler: missingHandler,
14450
+ effects: effects,
13469
14451
  logger: logger,
13470
14452
  telemetry: telemetry,
13471
14453
  resolveActor: rest => resolveClientActor(client, rest),
@@ -13519,12 +14501,11 @@ function createEngine(args) {
13519
14501
  workflowResource: workflowResource,
13520
14502
  ...optionalScope,
13521
14503
  instanceId: instanceId,
13522
- effectHandlers: effectHandlers,
13523
- missingHandler: missingHandler,
14504
+ ...effects,
13524
14505
  logger: logger,
13525
14506
  telemetry: telemetry,
13526
- ...args.effectLeaseMs !== void 0 ? {
13527
- leaseMs: args.effectLeaseMs
14507
+ ...args.effects?.leaseMs !== void 0 ? {
14508
+ leaseMs: args.effects.leaseMs
13528
14509
  } : {}
13529
14510
  }), drained = result.drained;
13530
14511
  return (drained.length > 0 || emptyDrainDue()) && telemetry.log(WorkflowEffectsDrained, {
@@ -13537,8 +14518,7 @@ function createEngine(args) {
13537
14518
  verifyDeployedDefinitions: () => verifyDeployedDefinitionsInternal({
13538
14519
  client: client,
13539
14520
  tag: tag,
13540
- effectHandlers: effectHandlers,
13541
- missingHandler: missingHandler,
14521
+ ...effects,
13542
14522
  logger: logger
13543
14523
  })
13544
14524
  };
@@ -14093,6 +15073,8 @@ exports.DECISION_SEMANTICS = invariants.DECISION_SEMANTICS;
14093
15073
 
14094
15074
  exports.DEFAULT_TRANSITION_WHEN = invariants.DEFAULT_TRANSITION_WHEN;
14095
15075
 
15076
+ exports.DOCUMENT_VALUE_PERMISSIONS = invariants.DOCUMENT_VALUE_PERMISSIONS;
15077
+
14096
15078
  exports.DRIVER_KINDS = invariants.DRIVER_KINDS;
14097
15079
 
14098
15080
  exports.DefinitionInUseError = invariants.DefinitionInUseError;
@@ -14131,8 +15113,20 @@ exports.WORKFLOW_DEFINITION_TYPE = invariants.WORKFLOW_DEFINITION_TYPE;
14131
15113
 
14132
15114
  exports.WorkflowError = invariants.WorkflowError;
14133
15115
 
15116
+ exports.activeAssignmentMembers = invariants.activeAssignmentMembers;
15117
+
14134
15118
  exports.actorFulfillsRole = invariants.actorFulfillsRole;
14135
15119
 
15120
+ exports.actorMatchesAssignment = invariants.actorMatchesAssignment;
15121
+
15122
+ exports.assignmentMatch = invariants.assignmentMatch;
15123
+
15124
+ exports.assignmentMembers = invariants.assignmentMembers;
15125
+
15126
+ exports.assignmentState = invariants.assignmentState;
15127
+
15128
+ exports.assignmentStateCounts = invariants.assignmentStateCounts;
15129
+
14136
15130
  exports.classifyPrincipalId = invariants.classifyPrincipalId;
14137
15131
 
14138
15132
  exports.clientConfigFromResource = invariants.clientConfigFromResource;
@@ -14151,6 +15145,10 @@ exports.errorMessage = invariants.errorMessage;
14151
15145
 
14152
15146
  exports.extractDocumentId = invariants.extractDocumentId;
14153
15147
 
15148
+ exports.findCurrentActivityEntry = invariants.findCurrentActivityEntry;
15149
+
15150
+ exports.findOpenStageEntry = invariants.findOpenStageEntry;
15151
+
14154
15152
  exports.gdrFromResource = invariants.gdrFromResource;
14155
15153
 
14156
15154
  exports.gdrRef = invariants.gdrRef;
@@ -14159,8 +15157,14 @@ exports.gdrUri = invariants.gdrUri;
14159
15157
 
14160
15158
  exports.groupMembershipNames = invariants.groupMembershipNames;
14161
15159
 
15160
+ exports.identityMatchesAssignment = invariants.identityMatchesAssignment;
15161
+
15162
+ exports.instanceAssignmentStateCounts = invariants.instanceAssignmentStateCounts;
15163
+
14162
15164
  exports.isCascadeFired = invariants.isCascadeFired;
14163
15165
 
15166
+ exports.isFilterScopedOut = invariants.isFilterScopedOut;
15167
+
14164
15168
  exports.isGdr = invariants.isGdr;
14165
15169
 
14166
15170
  exports.isInputSourced = invariants.isInputSourced;
@@ -14183,6 +15187,8 @@ exports.isTodoListItem = invariants.isTodoListItem;
14183
15187
 
14184
15188
  exports.lakePrincipalId = invariants.lakePrincipalId;
14185
15189
 
15190
+ exports.openActivityAssignments = invariants.openActivityAssignments;
15191
+
14186
15192
  exports.parseGdr = invariants.parseGdr;
14187
15193
 
14188
15194
  exports.parseResourceGdr = invariants.parseResourceGdr;
@@ -14337,6 +15343,8 @@ exports.CONTEXT_ENTRY_DISPLAY = CONTEXT_ENTRY_DISPLAY;
14337
15343
 
14338
15344
  exports.CascadeLimitError = CascadeLimitError;
14339
15345
 
15346
+ exports.ConcurrentCascadeError = ConcurrentCascadeError;
15347
+
14340
15348
  exports.ConcurrentCommitEffectOpsError = ConcurrentCommitEffectOpsError;
14341
15349
 
14342
15350
  exports.ConcurrentCompleteEffectError = ConcurrentCompleteEffectError;
@@ -14475,6 +15483,8 @@ exports.assertReadableModel = assertReadableModel;
14475
15483
 
14476
15484
  exports.assertReaderModelAcknowledgement = assertReaderModelAcknowledgement;
14477
15485
 
15486
+ exports.assignmentPrefilter = assignmentPrefilter;
15487
+
14478
15488
  exports.autonomySummary = autonomySummary;
14479
15489
 
14480
15490
  exports.availableActions = availableActions;
@@ -14485,9 +15495,11 @@ exports.buildSnapshot = buildSnapshot;
14485
15495
 
14486
15496
  exports.checklistLines = checklistLines;
14487
15497
 
15498
+ exports.clientGuardDereference = clientGuardDereference;
15499
+
14488
15500
  exports.clientProjectUserDirectory = clientProjectUserDirectory;
14489
15501
 
14490
- exports.compileGuard = compileGuard;
15502
+ exports.compileGuards = compileGuards;
14491
15503
 
14492
15504
  exports.computeDiffEntries = computeDiffEntries;
14493
15505
 
@@ -14511,6 +15523,8 @@ exports.definitionDeployedData = definitionDeployedData;
14511
15523
 
14512
15524
  exports.definitionLookupGroq = definitionLookupGroq;
14513
15525
 
15526
+ exports.definitionRoleNames = definitionRoleNames;
15527
+
14514
15528
  exports.definitionTagsGroq = definitionTagsGroq;
14515
15529
 
14516
15530
  exports.definitionsListGroq = definitionsListGroq;
@@ -14577,10 +15591,6 @@ exports.fieldTreeShape = fieldTreeShape;
14577
15591
 
14578
15592
  exports.findActivityNode = findActivityNode;
14579
15593
 
14580
- exports.findCurrentActivityEntry = findCurrentActivityEntry;
14581
-
14582
- exports.findOpenStageEntry = findOpenStageEntry;
14583
-
14584
15594
  exports.findStageNode = findStageNode;
14585
15595
 
14586
15596
  exports.groupSitesOf = groupSitesOf;
@@ -14621,8 +15631,6 @@ exports.isClientProjectUser = isClientProjectUser;
14621
15631
 
14622
15632
  exports.isDefinitionApplicable = isDefinitionApplicable;
14623
15633
 
14624
- exports.isFilterScopedOut = isFilterScopedOut;
14625
-
14626
15634
  exports.isProjectUserNotFoundError = isProjectUserNotFoundError;
14627
15635
 
14628
15636
  exports.isRevisionConflict = isRevisionConflict;
@@ -14703,6 +15711,8 @@ exports.silentLogger = silentLogger;
14703
15711
 
14704
15712
  exports.singleSubjectRequirementRefused = singleSubjectRequirementRefused;
14705
15713
 
15714
+ exports.snapshotGuardDereference = snapshotGuardDereference;
15715
+
14706
15716
  exports.stageAutonomyOf = stageAutonomyOf;
14707
15717
 
14708
15718
  exports.startFieldsParam = startFieldsParam;