@sanity/workflow-studio-plugin 0.5.0 → 0.20.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.
@@ -1,5 +1,3 @@
1
- import { tryParseGdr, resourceFromParsed, latestDeployedDefinitions, gdrRef, isSubjectEntry, isInputSourced, missingRequiredInputs, initialFieldIssues, releaseRef, refKindAcceptsTypes, rejectedRefTypes, ActionDisabledError, MutationGuardDeniedError, errorMessage, findOpenStageEntry, actionRendering, terminalState, ENGINE_API_VERSION, WORKFLOW_INSTANCE_TYPE, WORKFLOW_DEFINITION_TYPE, GUARD_DOC_TYPE, isTodoListEntry, isTodoListItem, isTerminalActivityStatus, describeCondition, sentenceCase, checklistLines, findCurrentActivityEntry, scalarValidationIssues, extractDocumentId, isSingleDocRefEntry, isGdr, resolveFieldEntry, isSingleDocRefKind, isNotesEntry, parseDefinitionSnapshot, StartNotSettledError, StartNotAllowedError, isUnprimed, unboundAllowedReads, explainStartAllowed, evaluateStartFilter, startKindOf, readsRootDocument, instancesQuery, assertReadableModel, isTerminalStage, DEFAULT_TRANSITION_WHEN, isStartableDefinition, acceptsDocumentType, definitionsListGroq, gdrFromResource, instanceWatchesDocument, aclPathForResource, definitionLookupGroq, instanceDocId, documentActionDenials } from "@sanity/workflow-engine";
2
-
3
1
  import { jsx, jsxs, Fragment } from "react/jsx-runtime";
4
2
 
5
3
  import { TimelineIcon } from "@sanity/icons/Timeline";
@@ -10,6 +8,12 @@ import { createContext, useContext, useEffect, useSyncExternalStore, useCallback
10
8
 
11
9
  import { usePaneRouter } from "sanity/structure";
12
10
 
11
+ import { tryParseGdr, resourceFromParsed, latestDeployedDefinitions, isStartableDefinition, isSubjectEntry, isInputSourced, acceptsDocumentType, missingRequiredInputs, initialFieldIssues, gdrRef, releaseRef, refKindAcceptsTypes, rejectedRefTypes, ActionDisabledError, MutationGuardDeniedError, errorMessage, findOpenStageEntry, actionRendering, terminalState, ENGINE_API_VERSION, WORKFLOW_INSTANCE_TYPE, WORKFLOW_DEFINITION_TYPE, GUARD_DOC_TYPE, isTodoListEntry, isTodoListItem, isTerminalActivityStatus, describeCondition, sentenceCase, checklistLines, findCurrentActivityEntry, scalarValidationIssues, toBareId, isSingleDocRefEntry, isGdr, resolveFieldEntry, isSingleDocRefKind, isNotesEntry, parseDefinitionSnapshot, isUnprimed, StartNotSettledError, StartNotAllowedError, unboundAllowedReads, explainStartAllowed, evaluateStartFilter, startKindOf, readsRootDocument, instancesQuery, assertReadableModel, isTerminalStage, DEFAULT_TRANSITION_WHEN, definitionsListGroq, gdrFromResource, subscriptionDocumentsForInstance, aclPathForResource, definitionLookupGroq, instanceDocId, documentActionDenials } from "@sanity/workflow-engine";
12
+
13
+ import { CheckmarkIcon } from "@sanity/icons/Checkmark";
14
+
15
+ import { CloseIcon } from "@sanity/icons/Close";
16
+
13
17
  import { formatDistanceToNow } from "date-fns/formatDistanceToNow";
14
18
 
15
19
  import { getPublishedId } from "@sanity/client/csm";
@@ -24,14 +28,10 @@ import { ClearableDatePicker, MemberPicker as MemberPicker$1, AssigneePicker as
24
28
 
25
29
  import { UserIcon } from "@sanity/icons/User";
26
30
 
27
- import { CloseIcon } from "@sanity/icons/Close";
28
-
29
31
  import { SearchIcon } from "@sanity/icons/Search";
30
32
 
31
33
  import { ChevronRightIcon } from "@sanity/icons/ChevronRight";
32
34
 
33
- import { CheckmarkIcon } from "@sanity/icons/Checkmark";
34
-
35
35
  import { EditIcon } from "@sanity/icons/Edit";
36
36
 
37
37
  import { randomKey } from "@sanity/util/content";
@@ -87,20 +87,58 @@ function gdrLocality(id, contentResource) {
87
87
  };
88
88
  }
89
89
 
90
- function mappingsForDocType(mappings, docType) {
91
- return mappings.filter(m => m.docType === docType);
90
+ function assertUniqueWorkflowMappings(mappings) {
91
+ const keys = /* @__PURE__ */ new Set;
92
+ for (const mapping of mappings) {
93
+ const key = mappingKey(mapping);
94
+ if (keys.has(key)) throw new Error(`Duplicate workflow mapping for ${key}`);
95
+ keys.add(key);
96
+ }
97
+ }
98
+
99
+ function discoverWorkflowMappings(args) {
100
+ assertUniqueWorkflowMappings(args.overrides ?? []);
101
+ const discovered = latestDeployedDefinitions(args.definitions).flatMap(definition => {
102
+ if (!isStartableDefinition(definition)) return [];
103
+ const subject = (definition.fields ?? []).find(isSubjectEntry);
104
+ if (subject === void 0 || !isInputSourced(subject)) return [];
105
+ const source = {
106
+ fields: [ ...definition.fields ?? [] ]
107
+ };
108
+ return [ ...args.schemaContentTypes ].filter(docType => acceptsDocumentType(source, docType)).map(docType => ({
109
+ docType: docType,
110
+ definition: definition.name,
111
+ label: definition.title ?? definition.name
112
+ }));
113
+ }), byKey = new Map(discovered.map(mapping => [ mappingKey(mapping), mapping ]));
114
+ for (const override of args.overrides ?? []) {
115
+ const key = mappingKey(override);
116
+ byKey.set(key, override);
117
+ }
118
+ return [ ...byKey.values() ];
92
119
  }
93
120
 
94
- function mappingForDocType(mappings, docType) {
95
- return mappingsForDocType(mappings, docType)[0];
121
+ function mappingAutoStartMap(mappings) {
122
+ const byType = /* @__PURE__ */ Object.create(null);
123
+ for (const mapping of mappings) {
124
+ if (!mapping.autoStart) continue;
125
+ const definitions = byType[mapping.docType] ?? [];
126
+ byType[mapping.docType] = [ ...definitions, mapping.definition ];
127
+ }
128
+ return byType;
129
+ }
130
+
131
+ function mappingsForDocType(mappings, docType) {
132
+ return mappings.filter(m => m.docType === docType);
96
133
  }
97
134
 
98
135
  function mappingKey(mapping) {
99
136
  return `${mapping.docType}::${mapping.definition}`;
100
137
  }
101
138
 
102
- function workflowDocTypes(mappings) {
103
- return new Set(mappings.map(m => m.docType));
139
+ function documentSubjectEntry(definition) {
140
+ const fields = definition.fields ?? [];
141
+ return fields.find(isSubjectEntry) ?? fields.find(entry => entry.type === "doc.ref" && entry.name === "subject");
104
142
  }
105
143
 
106
144
  function mappingStartBlocks(args) {
@@ -421,7 +459,7 @@ function stateByActivity(instance) {
421
459
  }
422
460
 
423
461
  function evaluationFreshness(args) {
424
- return args.evaluation ? args.committed.lastChangedAt > args.evaluation.instance.lastChangedAt ? "stale" : "current" : "pending";
462
+ return args.evaluation ? Date.parse(args.committed.lastChangedAt) > Date.parse(args.evaluation.instance.lastChangedAt) ? "stale" : "current" : "pending";
425
463
  }
426
464
 
427
465
  function isEvaluationCurrent(args) {
@@ -468,12 +506,27 @@ function useWorkflowContext() {
468
506
  }
469
507
 
470
508
  function useWorkflowsForDocument(rawId) {
471
- const {register: register, unregister: unregister, entries: entries} = useWorkflowContext(), docId = rawId ? getPublishedId(rawId) : void 0;
509
+ return useDocumentWorkflowEntries(rawId, !0);
510
+ }
511
+
512
+ function useCommittedWorkflowsForDocument(rawId) {
513
+ return useDocumentWorkflowEntries(rawId, !1);
514
+ }
515
+
516
+ function useDocumentWorkflowEntries(rawId, evaluate) {
517
+ const {register: register, unregister: unregister, requestEvaluation: requestEvaluation, releaseEvaluation: releaseEvaluation, entries: entries} = useWorkflowContext(), docId = rawId ? getPublishedId(rawId) : void 0;
472
518
  useEffect(() => {
473
519
  if (docId) return register(docId), () => unregister(docId);
474
520
  }, [ docId, register, unregister ]);
475
- const list = useSyncExternalStore(entries.subscribe, useCallback(() => docId ? entries.getDocument(docId) : EMPTY_ENTRIES, [ entries, docId ]));
476
- return {
521
+ const list = useSyncExternalStore(entries.subscribe, useCallback(() => docId ? entries.getDocument(docId) : EMPTY_ENTRIES, [ entries, docId ])), evaluationKey = evaluate ? list.map(entry => entry.instance._id).join("\0") : "";
522
+ return useEffect(() => {
523
+ if (!evaluate) return;
524
+ const ids = evaluationKey === "" ? [] : evaluationKey.split("\0");
525
+ for (const id of ids) requestEvaluation(id);
526
+ return () => {
527
+ for (const id of ids) releaseEvaluation(id);
528
+ };
529
+ }, [ evaluate, evaluationKey, requestEvaluation, releaseEvaluation ]), {
477
530
  docId: docId,
478
531
  entries: list
479
532
  };
@@ -484,11 +537,13 @@ function useHeldWorkflowEntry(instanceId) {
484
537
  return useSyncExternalStore(entries.subscribe, useCallback(() => instanceId ? entries.getInstance(instanceId) : void 0, [ entries, instanceId ]));
485
538
  }
486
539
 
487
- function useWorkflowInstanceEntry(instanceId) {
488
- const {requestInstance: requestInstance, releaseInstance: releaseInstance} = useWorkflowContext();
540
+ function useWorkflowInstanceEntry(instanceId, options = {}) {
541
+ const {requestInstance: requestInstance, releaseInstance: releaseInstance, requestEvaluation: requestEvaluation, releaseEvaluation: releaseEvaluation} = useWorkflowContext(), evaluate = options.evaluate ?? !0;
489
542
  return useEffect(() => {
490
543
  if (instanceId) return requestInstance(instanceId), () => releaseInstance(instanceId);
491
- }, [ instanceId, requestInstance, releaseInstance ]), useHeldWorkflowEntry(instanceId);
544
+ }, [ instanceId, requestInstance, releaseInstance ]), useEffect(() => {
545
+ if (!(!instanceId || !evaluate)) return requestEvaluation(instanceId), () => releaseEvaluation(instanceId);
546
+ }, [ instanceId, evaluate, requestEvaluation, releaseEvaluation ]), useHeldWorkflowEntry(instanceId);
492
547
  }
493
548
 
494
549
  const isTodoDone = item => item.status === "done", toggledTodoStatus = item => isTodoDone(item) ? "open" : "done";
@@ -761,13 +816,29 @@ function concludesHere(actionEval, activityName) {
761
816
  return changesOwnStatus(actionEval.action, activityName) || actionEval.firing?.exitsStage === !0;
762
817
  }
763
818
 
764
- function actionButtonTone(args) {
765
- return concludesHere(args.actionEval, args.activityName) ? {
766
- mode: "default",
767
- tone: actionAdvanceStatus(args.actionEval.action, args.activityName) === "failed" ? "critical" : "default"
768
- } : {
769
- mode: "ghost",
770
- tone: "default"
819
+ const SEMANTIC_FACE = {
820
+ "decision.accept": {
821
+ tone: "positive",
822
+ icon: CheckmarkIcon
823
+ },
824
+ "decision.decline": {
825
+ tone: "caution",
826
+ icon: CloseIcon
827
+ }
828
+ };
829
+
830
+ function actionButtonFace(args) {
831
+ const mode = concludesHere(args.actionEval, args.activityName) ? "default" : "ghost";
832
+ if (actionAdvanceStatus(args.actionEval.action, args.activityName) === "failed") return {
833
+ mode: mode,
834
+ tone: "critical",
835
+ icon: void 0
836
+ };
837
+ const [semantic] = args.actionEval.semantics ?? [], face = semantic === void 0 ? void 0 : SEMANTIC_FACE[semantic];
838
+ return {
839
+ mode: mode,
840
+ tone: face?.tone ?? "default",
841
+ icon: face?.icon
771
842
  };
772
843
  }
773
844
 
@@ -1022,10 +1093,19 @@ function personActor(user) {
1022
1093
  };
1023
1094
  }
1024
1095
 
1096
+ const memberIndexes = /* @__PURE__ */ new WeakMap;
1097
+
1098
+ function memberIndex(members) {
1099
+ const cached = memberIndexes.get(members);
1100
+ if (cached) return cached;
1101
+ const created = new Map(members.map(member => [ member.id, member ]));
1102
+ return memberIndexes.set(members, created), created;
1103
+ }
1104
+
1025
1105
  const selfUserFor = (id, me) => me != null && me.id === id ? me : void 0;
1026
1106
 
1027
1107
  function userDisplayFor(args) {
1028
- const member = args.members.find(m => m.id === args.id), self = selfUserFor(args.id, args.me);
1108
+ const member = memberIndex(args.members).get(args.id), self = selfUserFor(args.id, args.me);
1029
1109
  return {
1030
1110
  id: args.id,
1031
1111
  name: member?.displayName ?? self?.name ?? args.id,
@@ -1035,19 +1115,20 @@ function userDisplayFor(args) {
1035
1115
 
1036
1116
  function useUserDisplays(ids) {
1037
1117
  const {members: members} = useProjectMembers(), me = useCurrentUser();
1038
- return ids.map(id => userDisplayFor({
1118
+ return useMemo(() => ids.map(id => userDisplayFor({
1039
1119
  id: id,
1040
1120
  members: members,
1041
1121
  me: me
1042
- }));
1122
+ })), [ ids, me, members ]);
1043
1123
  }
1044
1124
 
1045
1125
  function useUserDisplay(id) {
1046
- const [display] = useUserDisplays([ id ]);
1047
- return display ?? {
1048
- name: id,
1049
- imageUrl: void 0
1050
- };
1126
+ const {members: members} = useProjectMembers(), me = useCurrentUser();
1127
+ return useMemo(() => userDisplayFor({
1128
+ id: id,
1129
+ members: members,
1130
+ me: me
1131
+ }), [ id, me, members ]);
1051
1132
  }
1052
1133
 
1053
1134
  function useAssignmentIdentity() {
@@ -1058,8 +1139,12 @@ function useAssignmentIdentity() {
1058
1139
  };
1059
1140
  }
1060
1141
 
1142
+ const projectMembersByUsers = /* @__PURE__ */ new WeakMap;
1143
+
1061
1144
  function projectMembersFrom(users) {
1062
- return users.map(({membership: membership, profile: profile}) => ({
1145
+ const cached = projectMembersByUsers.get(users);
1146
+ if (cached) return cached;
1147
+ const members = users.map(({membership: membership, profile: profile}) => ({
1063
1148
  id: membership.id,
1064
1149
  displayName: profile?.displayName ?? membership.id,
1065
1150
  ...profile?.email ? {
@@ -1070,6 +1155,7 @@ function projectMembersFrom(users) {
1070
1155
  } : {},
1071
1156
  roles: (membership.roles ?? []).map(role => role.name)
1072
1157
  }));
1158
+ return projectMembersByUsers.set(users, members), members;
1073
1159
  }
1074
1160
 
1075
1161
  function useProjectMembers() {
@@ -1511,7 +1597,7 @@ function DocPicker({value: value, onChange: onChange, types: types, readOnly: re
1511
1597
  children: /* @__PURE__ */ jsx(Box, {
1512
1598
  flex: 1,
1513
1599
  children: /* @__PURE__ */ jsx(PreviewOrId, {
1514
- id: extractDocumentId(value.id),
1600
+ id: toBareId(value.id),
1515
1601
  type: value.type
1516
1602
  })
1517
1603
  })
@@ -1607,7 +1693,7 @@ function DocRefsInput({value: value, onChange: onChange, types: types}) {
1607
1693
  /* @__PURE__ */ jsx(Box, {
1608
1694
  flex: 1,
1609
1695
  children: /* @__PURE__ */ jsx(PreviewOrId, {
1610
- id: extractDocumentId(ref.id),
1696
+ id: toBareId(ref.id),
1611
1697
  type: ref.type
1612
1698
  })
1613
1699
  }),
@@ -1761,6 +1847,15 @@ function useFireAction(args) {
1761
1847
  };
1762
1848
  }
1763
1849
 
1850
+ const ACTION_ICON_GAP = 2;
1851
+
1852
+ function actionIconProps(icon) {
1853
+ return icon ? {
1854
+ gap: ACTION_ICON_GAP,
1855
+ icon: icon
1856
+ } : {};
1857
+ }
1858
+
1764
1859
  function stopRowMouseDown(e) {
1765
1860
  e.preventDefault(), e.stopPropagation();
1766
1861
  }
@@ -1788,7 +1883,7 @@ function RowClickShield({active: active, children: children}) {
1788
1883
  });
1789
1884
  }
1790
1885
 
1791
- function FireButton({instanceId: instanceId, activity: activity, action: action, label: label, mode: mode = "default", chrome: chrome, tone: tone = "default"}) {
1886
+ function FireButton({instanceId: instanceId, activity: activity, action: action, label: label, mode: mode = "default", chrome: chrome, tone: tone = "default", icon: icon}) {
1792
1887
  const {fire: fire, pending: pending} = useFireAction({
1793
1888
  instanceId: instanceId,
1794
1889
  activity: activity,
@@ -1800,6 +1895,7 @@ function FireButton({instanceId: instanceId, activity: activity, action: action,
1800
1895
  /* @__PURE__ */
1801
1896
  return jsx(Button, {
1802
1897
  fontSize: 1,
1898
+ ...actionIconProps(icon),
1803
1899
  loading: pending,
1804
1900
  mode: chrome?.mode ?? mode,
1805
1901
  onClick: handleClick,
@@ -1817,6 +1913,7 @@ function ActivityActionRow({actionEval: actionEval, instanceId: instanceId, acti
1817
1913
  const rowKind = actionRowKind(actionEval);
1818
1914
  return rowKind.kind === "disabled" ? /* @__PURE__ */ jsx(DisabledActionButton, {
1819
1915
  actionEval: actionEval,
1916
+ activity: activity,
1820
1917
  chrome: chrome
1821
1918
  }) : rowKind.kind === "plain" ? /* @__PURE__ */ jsx(PlainActionButton, {
1822
1919
  actionEval: actionEval,
@@ -1831,13 +1928,18 @@ function ActivityActionRow({actionEval: actionEval, instanceId: instanceId, acti
1831
1928
  });
1832
1929
  }
1833
1930
 
1834
- function DisabledActionButton({actionEval: actionEval, chrome: chrome}) {
1835
- const label = actionTriggerLabel(actionEval), button = /* @__PURE__ */ jsx(Button, {
1931
+ function DisabledActionButton({actionEval: actionEval, activity: activity, chrome: chrome}) {
1932
+ const label = actionTriggerLabel(actionEval), {tone: tone, icon: icon} = actionButtonFace({
1933
+ actionEval: actionEval,
1934
+ activityName: activity
1935
+ }), button = /* @__PURE__ */ jsx(Button, {
1836
1936
  fontSize: 1,
1937
+ ...actionIconProps(icon),
1837
1938
  mode: chrome?.mode ?? "ghost",
1838
1939
  onClick: e => e.stopPropagation(),
1839
1940
  padding: 2,
1840
1941
  text: label,
1942
+ tone: tone,
1841
1943
  ...spanTriggerProps(chrome),
1842
1944
  disabled: !0
1843
1945
  });
@@ -1848,11 +1950,12 @@ function DisabledActionButton({actionEval: actionEval, chrome: chrome}) {
1848
1950
  }
1849
1951
 
1850
1952
  function PlainActionButton({actionEval: actionEval, instanceId: instanceId, activity: activity, chrome: chrome}) {
1851
- const {action: action} = actionEval, label = actionLabel(action), btn = actionButtonTone({
1953
+ const {action: action} = actionEval, label = actionLabel(action), btn = actionButtonFace({
1852
1954
  actionEval: actionEval,
1853
1955
  activityName: activity
1854
1956
  }), button = /* @__PURE__ */ jsx(FireButton, {
1855
1957
  action: action.name,
1958
+ icon: btn.icon,
1856
1959
  instanceId: instanceId,
1857
1960
  label: label,
1858
1961
  mode: btn.mode,
@@ -1927,7 +2030,7 @@ function ParamsActionDialog({actionEval: actionEval, instanceId: instanceId, act
1927
2030
  }
1928
2031
 
1929
2032
  function ParamsActionButton({actionEval: actionEval, instanceId: instanceId, activity: activity, chrome: chrome}) {
1930
- const [open, setOpen] = useState(!1), btn = actionButtonTone({
2033
+ const [open, setOpen] = useState(!1), btn = actionButtonFace({
1931
2034
  actionEval: actionEval,
1932
2035
  activityName: activity
1933
2036
  });
@@ -1936,6 +2039,7 @@ function ParamsActionButton({actionEval: actionEval, instanceId: instanceId, act
1936
2039
  children: [
1937
2040
  /* @__PURE__ */ jsx(Button, {
1938
2041
  fontSize: 1,
2042
+ ...actionIconProps(btn.icon),
1939
2043
  mode: chrome?.mode ?? btn.mode,
1940
2044
  onClick: e => {
1941
2045
  chrome?.inButtonCard === !0 && e.stopPropagation(), setOpen(!0);
@@ -2761,7 +2865,7 @@ function ReadOnlyDocRefs({entry: entry}) {
2761
2865
  padding: 2,
2762
2866
  radius: 2,
2763
2867
  children: /* @__PURE__ */ jsx(PreviewOrId, {
2764
- id: extractDocumentId(ref.id),
2868
+ id: toBareId(ref.id),
2765
2869
  type: ref.type
2766
2870
  })
2767
2871
  }, ref.id))
@@ -4125,7 +4229,12 @@ const ACTIONS_MENU_TRIGGER = {
4125
4229
  };
4126
4230
 
4127
4231
  function ActionMenuItem({actionEval: actionEval, instanceId: instanceId, activity: activity, onCollectParams: onCollectParams}) {
4128
- const rowKind = actionRowKind(actionEval), label = actionTriggerLabel(actionEval), {fire: fire, pending: pending} = useFireAction({
4232
+ const rowKind = actionRowKind(actionEval), label = actionTriggerLabel(actionEval), {tone: tone, icon: icon} = actionButtonFace({
4233
+ actionEval: actionEval,
4234
+ activityName: activity
4235
+ }), iconProp = icon ? {
4236
+ icon: icon
4237
+ } : {}, {fire: fire, pending: pending} = useFireAction({
4129
4238
  instanceId: instanceId,
4130
4239
  activity: activity,
4131
4240
  action: actionEval.action.name,
@@ -4133,17 +4242,23 @@ function ActionMenuItem({actionEval: actionEval, instanceId: instanceId, activit
4133
4242
  });
4134
4243
  return rowKind.kind === "disabled" ? /* @__PURE__ */ jsx(MenuItem, {
4135
4244
  disabled: !0,
4136
- text: label
4245
+ ...iconProp,
4246
+ text: label,
4247
+ tone: tone
4137
4248
  }) : rowKind.kind === "plain" ? /* @__PURE__ */ jsx(MenuItem, {
4138
4249
  disabled: pending,
4250
+ ...iconProp,
4139
4251
  onClick: () => {
4140
4252
  fire();
4141
4253
  },
4142
- text: label
4254
+ text: label,
4255
+ tone: tone
4143
4256
  }) : /* @__PURE__ */ jsx(MenuItem, {
4144
4257
  disabled: pending,
4258
+ ...iconProp,
4145
4259
  onClick: onCollectParams,
4146
- text: label
4260
+ text: label,
4261
+ tone: tone
4147
4262
  });
4148
4263
  }
4149
4264
 
@@ -4256,6 +4371,7 @@ function FittedActions({actions: actions, activity: activity, instanceId: instan
4256
4371
  children: [
4257
4372
  /* @__PURE__ */ jsx(MeasureRow, {
4258
4373
  actions: actions,
4374
+ activity: activity,
4259
4375
  onElement: setMeasureEl
4260
4376
  }),
4261
4377
  /* @__PURE__ */ jsxs(FooterRow, {
@@ -4275,7 +4391,7 @@ function FittedActions({actions: actions, activity: activity, instanceId: instan
4275
4391
  });
4276
4392
  }
4277
4393
 
4278
- function MeasureRow({actions: actions, onElement: onElement}) {
4394
+ function MeasureRow({actions: actions, activity: activity, onElement: onElement}) {
4279
4395
  /* @__PURE__ */
4280
4396
  return jsx("div", {
4281
4397
  "aria-hidden": !0,
@@ -4289,12 +4405,20 @@ function MeasureRow({actions: actions, onElement: onElement}) {
4289
4405
  display: "inline-flex",
4290
4406
  whiteSpace: "nowrap"
4291
4407
  },
4292
- children: [ actions.map(a => /* @__PURE__ */ jsx(Button, {
4293
- as: "span",
4294
- fontSize: 1,
4295
- padding: 2,
4296
- text: actionTriggerLabel(a)
4297
- }, a.action.name)),
4408
+ children: [ actions.map(a => {
4409
+ const {icon: icon} = actionButtonFace({
4410
+ actionEval: a,
4411
+ activityName: activity
4412
+ });
4413
+ /* @__PURE__ */
4414
+ return jsx(Button, {
4415
+ as: "span",
4416
+ fontSize: 1,
4417
+ ...actionIconProps(icon),
4418
+ padding: 2,
4419
+ text: actionTriggerLabel(a)
4420
+ }, a.action.name);
4421
+ }),
4298
4422
  /* @__PURE__ */ jsx(Button, {
4299
4423
  ...ACTIONS_MENU_TRIGGER,
4300
4424
  as: "span",
@@ -4576,7 +4700,26 @@ function CountedLabel({count: count, label: label}) {
4576
4700
  });
4577
4701
  }
4578
4702
 
4579
- function GroupHeading({count: count, title: title, end: end}) {
4703
+ function SpinnerSlot({busy: busy, size: size}) {
4704
+ /* @__PURE__ */
4705
+ return jsx(Flex, {
4706
+ align: "center",
4707
+ flex: "none",
4708
+ justify: "center",
4709
+ style: {
4710
+ width: 19,
4711
+ height: 19
4712
+ },
4713
+ children: busy ? /* @__PURE__ */ jsx(Spinner, {
4714
+ muted: !0,
4715
+ ...size !== void 0 ? {
4716
+ size: size
4717
+ } : {}
4718
+ }) : null
4719
+ });
4720
+ }
4721
+
4722
+ function GroupHeading({busy: busy, count: count, title: title, end: end}) {
4580
4723
  /* @__PURE__ */
4581
4724
  return jsxs(Flex, {
4582
4725
  align: "center",
@@ -4591,6 +4734,9 @@ function GroupHeading({count: count, title: title, end: end}) {
4591
4734
  weight: "semibold",
4592
4735
  children: title
4593
4736
  })
4737
+ }), busy === void 0 ? null : /* @__PURE__ */ jsx(SpinnerSlot, {
4738
+ busy: busy,
4739
+ size: 1
4594
4740
  }), end === void 0 ? null : /* @__PURE__ */ jsxs(Fragment, {
4595
4741
  children: [
4596
4742
  /* @__PURE__ */ jsx(Flex, {
@@ -4713,6 +4859,18 @@ function TabSwitch({ariaControls: ariaControls, idPrefix: idPrefix, options: opt
4713
4859
  });
4714
4860
  }
4715
4861
 
4862
+ function useDelayedFlag(active, delayMs) {
4863
+ const [held, setHeld] = useState(!1);
4864
+ return useEffect(() => {
4865
+ if (!active) {
4866
+ setHeld(!1);
4867
+ return;
4868
+ }
4869
+ const timer = setTimeout(() => setHeld(!0), delayMs);
4870
+ return () => clearTimeout(timer);
4871
+ }, [ active, delayMs ]), held && active;
4872
+ }
4873
+
4716
4874
  function instanceTitle(instance, definition) {
4717
4875
  return definition?.title ?? instance.definition;
4718
4876
  }
@@ -5551,9 +5709,16 @@ function instanceNotice(entry) {
5551
5709
  size: 1,
5552
5710
  children: "This workflow didn’t finish starting — use Start workflow to continue it"
5553
5711
  })
5554
- }) : pending ? /* @__PURE__ */ jsx(LoadingRow, {
5555
- label: "Loading…",
5556
- padding: 4
5712
+ }) : pending ? /* @__PURE__ */ jsx(WorkRow, {
5713
+ lead: /* @__PURE__ */ jsx(Spinner, {
5714
+ muted: !0,
5715
+ size: 1
5716
+ }),
5717
+ children: /* @__PURE__ */ jsx(Text, {
5718
+ muted: !0,
5719
+ size: 1,
5720
+ children: "Loading…"
5721
+ })
5557
5722
  }) : null;
5558
5723
  }
5559
5724
 
@@ -6617,20 +6782,7 @@ function InstanceDebugMenu({entry: entry}) {
6617
6782
  }
6618
6783
 
6619
6784
  function InstanceAccordion({entry: entry, children: children, defaultOpen: defaultOpen}) {
6620
- const definition = useDefinition(entry), {instance: instance, ready: ready} = entry, settled = !isLiveEntry(entry), title = instanceTitle(instance, definition), badgeTrim = useBadgeCapTrim(), status = /* @__PURE__ */ jsxs(Flex, {
6621
- align: "center",
6622
- gap: 2,
6623
- style: badgeTrim,
6624
- children: [
6625
- /* @__PURE__ */ jsx(AbortedBadge, {
6626
- instance: instance
6627
- }), settled ? null : /* @__PURE__ */ jsx(OlderDefinitionBadge, {
6628
- instance: instance
6629
- }), !ready && entry.invalid === void 0 ? /* @__PURE__ */ jsx(Spinner, {
6630
- muted: !0,
6631
- size: 1
6632
- }) : null ]
6633
- });
6785
+ const definition = useDefinition(entry), {instance: instance} = entry, settled = !isLiveEntry(entry), title = instanceTitle(instance, definition), badgeTrim = useBadgeCapTrim();
6634
6786
  /* @__PURE__ */
6635
6787
  return jsx(CollapsibleBand, {
6636
6788
  defaultOpen: defaultOpen ?? !settled,
@@ -6653,7 +6805,18 @@ function InstanceAccordion({entry: entry, children: children, defaultOpen: defau
6653
6805
  textOverflow: "ellipsis",
6654
6806
  weight: "medium",
6655
6807
  children: title
6656
- }), status ]
6808
+ }),
6809
+ /* @__PURE__ */ jsxs(Flex, {
6810
+ align: "center",
6811
+ gap: 2,
6812
+ style: badgeTrim,
6813
+ children: [
6814
+ /* @__PURE__ */ jsx(AbortedBadge, {
6815
+ instance: instance
6816
+ }), settled ? null : /* @__PURE__ */ jsx(OlderDefinitionBadge, {
6817
+ instance: instance
6818
+ }) ]
6819
+ }) ]
6657
6820
  }),
6658
6821
  /* @__PURE__ */ jsx(TrailingHairline, {}),
6659
6822
  /* @__PURE__ */ jsx(InstanceDebugMenu, {
@@ -6671,7 +6834,7 @@ function InstanceAccordion({entry: entry, children: children, defaultOpen: defau
6671
6834
  }
6672
6835
 
6673
6836
  function PartOfBand({instance: instance}) {
6674
- const parentRef = instance.ancestors.at(-1), parentId = parentRef ? extractDocumentId(parentRef.id) : null, parentEntry = useWorkflowInstanceEntry(parentId), parentDefinition = useDefinition(parentEntry);
6837
+ const parentRef = instance.ancestors.at(-1), parentId = parentRef ? toBareId(parentRef.id) : null, parentEntry = useWorkflowInstanceEntry(parentId), parentDefinition = useDefinition(parentEntry);
6675
6838
  if (!parentId) return null;
6676
6839
  const title = parentEntry ? instanceTitle(parentEntry.instance, parentDefinition) : parentId;
6677
6840
  /* @__PURE__ */
@@ -7228,11 +7391,9 @@ const VIEW_TAB_CODEC = workflowsTabCodec([ "overview", "for-me" ]), WorkflowsVie
7228
7391
  };
7229
7392
 
7230
7393
  function DiscoverySpinner() {
7231
- const [visible, setVisible] = useState(!1);
7232
- return useEffect(() => {
7233
- const timer = setTimeout(() => setVisible(!0), 1e3);
7234
- return () => clearTimeout(timer);
7235
- }, []), /* @__PURE__ */ jsx(Flex, {
7394
+ const visible = useDelayedFlag(!0, 1e3);
7395
+ /* @__PURE__ */
7396
+ return jsx(Flex, {
7236
7397
  align: "center",
7237
7398
  height: "fill",
7238
7399
  justify: "center",
@@ -7242,7 +7403,7 @@ function DiscoverySpinner() {
7242
7403
  });
7243
7404
  }
7244
7405
 
7245
- const GROUP_GAP = 1;
7406
+ const BOOT_CUE_DELAY_MS = 400, GROUP_GAP = 1;
7246
7407
 
7247
7408
  function useWorkflowsTab() {
7248
7409
  const {params: params, setParams: setParams} = usePaneRouter();
@@ -7267,7 +7428,7 @@ function useOpenActivity(entries) {
7267
7428
  }
7268
7429
 
7269
7430
  function WorkflowsPanel({entries: entries, documentId: documentId, docTypeMappings: docTypeMappings, initialValue: initialValue}) {
7270
- const identity = useAssignmentIdentity(), panelId = useId(), {params: params} = usePaneRouter(), {view: view, setView: setView} = useWorkflowsTab(), {openActivity: openActivity, setOpenActivity: setOpenActivity, dialogEntry: dialogEntry} = useOpenActivity(entries), active = entries.filter(isLiveEntry), finished = entries.filter(e => !isLiveEntry(e)), forMe = forMeWorkOf(active, identity), [landing] = useState(() => focusedLanding(params?.[WORKFLOWS_FOCUS_PARAM], entries.map(e => e.instance._id))), assignedWorkUnknown = active.some(e => e.invalid !== void 0), sectionFor = e => /* @__PURE__ */ jsx(WorkflowInstanceSection, {
7431
+ const identity = useAssignmentIdentity(), panelId = useId(), {params: params} = usePaneRouter(), {view: view, setView: setView} = useWorkflowsTab(), {openActivity: openActivity, setOpenActivity: setOpenActivity, dialogEntry: dialogEntry} = useOpenActivity(entries), active = entries.filter(isLiveEntry), finished = entries.filter(e => !isLiveEntry(e)), forMe = forMeWorkOf(active, identity), [landing] = useState(() => focusedLanding(params?.[WORKFLOWS_FOCUS_PARAM], entries.map(e => e.instance._id))), assignedWorkUnknown = active.some(e => e.invalid !== void 0), booting = useDelayedFlag(active.some(e => !e.ready && e.invalid === void 0), BOOT_CUE_DELAY_MS), sectionFor = e => /* @__PURE__ */ jsx(WorkflowInstanceSection, {
7271
7432
  defaultOpen: landingDefaultOpen(landing, e.instance._id),
7272
7433
  entry: e,
7273
7434
  onOpenActivity: activityName => setOpenActivity({
@@ -7288,6 +7449,7 @@ function WorkflowsPanel({entries: entries, documentId: documentId, docTypeMappin
7288
7449
  padding: 3,
7289
7450
  children: [
7290
7451
  /* @__PURE__ */ jsx(GroupHeading, {
7452
+ busy: booting,
7291
7453
  count: active.length,
7292
7454
  end:
7293
7455
  /* @__PURE__ */ jsx(Box, {
@@ -7423,10 +7585,8 @@ function workflowsView(S) {
7423
7585
  return S.view.component(WorkflowsView).id(WORKFLOWS_VIEW_ID).title("Workflows");
7424
7586
  }
7425
7587
 
7426
- function workflowDefaultDocumentNode(options) {
7427
- return (S, ctx) => {
7428
- if (mappingForDocType(options.mappings, ctx.schemaType)) return S.document().views([ S.view.form(), workflowsView(S) ]);
7429
- };
7588
+ function workflowDefaultDocumentNode() {
7589
+ return S => S.document().views([ S.view.form(), workflowsView(S) ]);
7430
7590
  }
7431
7591
 
7432
7592
  function lockTitleFor(args) {
@@ -7439,26 +7599,12 @@ function lockTitleFor(args) {
7439
7599
  return workflows.length === 1 ? `The ${workflowList} workflow is holding this document.` : `The ${workflowList} workflows are holding this document.`;
7440
7600
  }
7441
7601
 
7442
- const EMPTY = {
7443
- byType: /* @__PURE__ */ new Map,
7444
- definitions: /* @__PURE__ */ new Map,
7445
- warnings: []
7446
- };
7447
-
7448
7602
  function resolveAutoStart(args) {
7449
- const {autoStart: autoStart, context: context, knownDocTypes: knownDocTypes, definitions: definitions} = args;
7450
- if (autoStart === void 0) return EMPTY;
7451
- const evaluated = evaluateMap(autoStart, context);
7452
- if ("warning" in evaluated) return {
7453
- byType: /* @__PURE__ */ new Map,
7454
- definitions: /* @__PURE__ */ new Map,
7455
- warnings: [ evaluated.warning ]
7456
- };
7457
- const map = evaluated.map, latestByName = new Map(latestDeployedDefinitions(definitions).map(d => [ d.name, d ])), byType = /* @__PURE__ */ new Map, resolvedDefinitions = /* @__PURE__ */ new Map, warnings = [];
7458
- for (const [docType, spec] of Object.entries(map)) {
7603
+ const {autoStart: autoStart, knownDocTypes: knownDocTypes, definitions: definitions} = args, latestByName = new Map(latestDeployedDefinitions(definitions).map(d => [ d.name, d ])), byType = /* @__PURE__ */ new Map, resolvedDefinitions = /* @__PURE__ */ new Map, warnings = [];
7604
+ for (const [docType, names] of Object.entries(autoStart)) {
7459
7605
  const entry = resolveEntry({
7460
7606
  docType: docType,
7461
- spec: spec,
7607
+ names: names,
7462
7608
  knownDocTypes: knownDocTypes,
7463
7609
  latestByName: latestByName
7464
7610
  });
@@ -7474,35 +7620,15 @@ function resolveAutoStart(args) {
7474
7620
  };
7475
7621
  }
7476
7622
 
7477
- function evaluateMap(autoStart, context) {
7478
- let map;
7479
- try {
7480
- map = typeof autoStart == "function" ? autoStart(context) : autoStart;
7481
- } catch (err) {
7482
- return {
7483
- warning: `autoStart: config function threw — ignoring it (${describeError(err)}).`
7484
- };
7485
- }
7486
- return typeof map != "object" || map === null ? {
7487
- warning: "autoStart: config did not resolve to an object — ignoring it."
7488
- } : {
7489
- map: map
7490
- };
7491
- }
7492
-
7493
- function specNames(spec) {
7494
- return typeof spec == "string" ? [ spec ] : Array.isArray(spec) ? spec : [];
7495
- }
7496
-
7497
7623
  function resolveEntry(args) {
7498
- const {docType: docType, spec: spec, knownDocTypes: knownDocTypes, latestByName: latestByName} = args;
7624
+ const {docType: docType, names: names, knownDocTypes: knownDocTypes, latestByName: latestByName} = args;
7499
7625
  if (!knownDocTypes.has(docType)) return {
7500
7626
  valid: [],
7501
7627
  definitions: [],
7502
7628
  warnings: [ `autoStart: no schema type "${docType}" — entry skipped.` ]
7503
7629
  };
7504
7630
  const valid = [], definitions = [], warnings = [];
7505
- for (const name of specNames(spec)) {
7631
+ for (const name of names) {
7506
7632
  const definition = latestByName.get(name), issue = workflowIssue({
7507
7633
  name: name,
7508
7634
  docType: docType,
@@ -7528,11 +7654,9 @@ function workflowIssue(args) {
7528
7654
  const {name: name, docType: docType, definition: definition} = args;
7529
7655
  if (definition === void 0) return `autoStart: "${docType}" → workflow "${name}" is not deployed — skipped.`;
7530
7656
  if (!isStartableDefinition(definition)) return `autoStart: "${docType}" → workflow "${name}" is spawn-only, not startable — skipped.`;
7531
- if (!acceptsDocumentType({
7532
- fields: [ ...definition.fields ?? [] ]
7533
- }, docType)) return `autoStart: "${docType}" → workflow "${name}" has no subject accepting "${docType}" — skipped.`;
7534
- const subject = (definition.fields ?? []).find(isSubjectEntry);
7535
- if (subject !== void 0 && !isInputSourced(subject)) return `autoStart: "${docType}" → workflow "${name}" has a self-filling subject (not caller-provided), so the document can't be its subject — skipped.`;
7657
+ const subject = documentSubjectEntry(definition), acceptsType = subject?.types === void 0 || subject.types.includes(docType);
7658
+ if (subject === void 0 || !acceptsType) return `autoStart: "${docType}" workflow "${name}" has no document subject accepting "${docType}" — skipped.`;
7659
+ if (!isInputSourced(subject)) return `autoStart: "${docType}" → workflow "${name}" has a self-filling subject (not caller-provided), so the document can't be its subject — skipped.`;
7536
7660
  }
7537
7661
 
7538
7662
  function contentDocumentTypes(schema) {
@@ -7552,20 +7676,31 @@ const EMPTY_SNAPSHOT = {
7552
7676
  };
7553
7677
 
7554
7678
  function useDiscoveryState(args) {
7555
- const {registered: registered, requested: requested, contentResource: contentResource} = args, watching = registered.length + requested.length > 0, [latest, setLatest] = useState(EMPTY_SNAPSHOT), [resolved, setResolved] = useState(EMPTY_SNAPSHOT), onSnapshot = useCallback(snapshot => {
7556
- setLatest(snapshot), snapshot.instances !== void 0 && setResolved(snapshot);
7557
- }, []);
7558
- useEffect(() => {
7559
- watching || (setLatest(EMPTY_SNAPSHOT), setResolved(EMPTY_SNAPSHOT));
7560
- }, [ watching ]);
7561
- const filter = useMemo(() => ({
7562
- documents: registered.map(id => gdrFromResource(contentResource, id)),
7679
+ const {observed: observed, requested: requested, contentResource: contentResource} = args, documentWatching = observed.length > 0, requestedWatching = requested.length > 0, documents = useDiscoveryStream(documentWatching), explicit = useDiscoveryStream(requestedWatching), documentFilter = useMemo(() => documentWatching ? {
7680
+ documents: observed.map(id => gdrFromResource(contentResource, id)),
7681
+ includeCompleted: !0
7682
+ } : void 0, [ documentWatching, observed, contentResource ]), requestedFilter = useMemo(() => requestedWatching ? {
7563
7683
  ids: requested,
7564
7684
  includeCompleted: !0
7565
- }), [ registered, requested, contentResource ]);
7685
+ } : void 0, [ requestedWatching, requested ]), resolved = useMemo(() => mergeResolved(documents.resolved, explicit.resolved), [ documents.resolved, explicit.resolved ]);
7566
7686
  return {
7567
- watching: watching,
7568
- filter: filter,
7687
+ documentFilter: documentFilter,
7688
+ requestedFilter: requestedFilter,
7689
+ loading: documents.loading || explicit.loading,
7690
+ invalid: documents.invalid ?? explicit.invalid,
7691
+ resolved: resolved,
7692
+ onDocumentSnapshot: documents.onSnapshot,
7693
+ onRequestedSnapshot: explicit.onSnapshot
7694
+ };
7695
+ }
7696
+
7697
+ function useDiscoveryStream(watching) {
7698
+ const [latest, setLatest] = useState(EMPTY_SNAPSHOT), [resolved, setResolved] = useState(EMPTY_SNAPSHOT), onSnapshot = useCallback(snapshot => {
7699
+ setLatest(snapshot), snapshot.instances !== void 0 && setResolved(snapshot);
7700
+ }, []);
7701
+ return useEffect(() => {
7702
+ watching || (setLatest(EMPTY_SNAPSHOT), setResolved(EMPTY_SNAPSHOT));
7703
+ }, [ watching ]), {
7569
7704
  loading: watching && latest.instances === void 0 && latest.invalid === void 0,
7570
7705
  invalid: watching ? latest.invalid : void 0,
7571
7706
  resolved: resolved,
@@ -7573,18 +7708,62 @@ function useDiscoveryState(args) {
7573
7708
  };
7574
7709
  }
7575
7710
 
7576
- function indexDiscoveredInstances(args) {
7577
- const {instances: instances, registered: registered, contentResource: contentResource} = args, ordered = [ ...instances ].sort((a, b) => b.startedAt.localeCompare(a.startedAt)), index = /* @__PURE__ */ new Map;
7578
- for (const bareId of registered) {
7579
- const uri = gdrFromResource(contentResource, bareId), watching = ordered.filter(inst => instanceWatchesDocument(inst, uri));
7580
- watching.length > 0 && index.set(bareId, watching.map(inst => inst._id));
7711
+ function mergeResolved(documents, explicit) {
7712
+ const rows = [ ...documents.instances ?? [], ...explicit.instances ?? [] ], freshest = /* @__PURE__ */ new Map;
7713
+ for (const instance of rows) {
7714
+ const current = freshest.get(instance._id);
7715
+ (current === void 0 || Date.parse(instance.lastChangedAt) > Date.parse(current.lastChangedAt)) && freshest.set(instance._id, instance);
7581
7716
  }
7717
+ return {
7718
+ instances: documents.instances === void 0 && explicit.instances === void 0 ? void 0 : [ ...freshest.values() ],
7719
+ invalid: documents.invalid ?? explicit.invalid,
7720
+ registered: documents.registered
7721
+ };
7722
+ }
7723
+
7724
+ function createDiscoveryIndexer(deriveWatchSet = subscriptionDocumentsForInstance) {
7725
+ const cached = /* @__PURE__ */ new Map;
7726
+ return {
7727
+ index(args) {
7728
+ const liveIds = new Set(args.instances.map(instance => instance._id));
7729
+ for (const id of cached.keys()) liveIds.has(id) || cached.delete(id);
7730
+ const watchedById = /* @__PURE__ */ new Map;
7731
+ for (const instance of args.instances) {
7732
+ let row = cached.get(instance._id);
7733
+ row?.source !== instance && (row = {
7734
+ source: instance,
7735
+ globalDocumentIds: [ ...new Set(deriveWatchSet(instance).documents.map(document => document.globalDocumentId)) ]
7736
+ }, cached.set(instance._id, row)), watchedById.set(instance._id, row.globalDocumentIds);
7737
+ }
7738
+ return buildDiscoveryIndex(args, watchedById);
7739
+ }
7740
+ };
7741
+ }
7742
+
7743
+ function buildDiscoveryIndex(args, watchedById) {
7744
+ const {instances: instances, registered: registered, contentResource: contentResource} = args, ordered = [ ...instances ].sort((a, b) => b.startedAt.localeCompare(a.startedAt)), index = /* @__PURE__ */ new Map, registeredByUri = new Map(registered.map(bareId => [ gdrFromResource(contentResource, bareId), bareId ]));
7745
+ for (const instance of ordered) appendInstanceWatches({
7746
+ index: index,
7747
+ registeredByUri: registeredByUri,
7748
+ instanceId: instance._id,
7749
+ watched: watchedById.get(instance._id) ?? []
7750
+ });
7582
7751
  return {
7583
7752
  instances: new Map(ordered.map(inst => [ inst._id, inst ])),
7584
7753
  index: index
7585
7754
  };
7586
7755
  }
7587
7756
 
7757
+ function appendInstanceWatches(args) {
7758
+ const {index: index, registeredByUri: registeredByUri, instanceId: instanceId, watched: watched} = args;
7759
+ for (const uri of watched) {
7760
+ const bareId = registeredByUri.get(uri);
7761
+ if (bareId === void 0) continue;
7762
+ const ids = index.get(bareId);
7763
+ ids === void 0 ? index.set(bareId, [ instanceId ]) : ids.push(instanceId);
7764
+ }
7765
+ }
7766
+
7588
7767
  function sameInstance(a, b) {
7589
7768
  return a === b || a._id === b._id && a._rev === b._rev;
7590
7769
  }
@@ -7608,7 +7787,7 @@ function entryOf(inst, report) {
7608
7787
  instance: report?.evaluation?.instance ?? inst,
7609
7788
  committed: inst,
7610
7789
  evaluation: report?.evaluation,
7611
- ready: report?.ready ?? !1,
7790
+ ready: report?.ready ?? terminalState(inst) !== "in-flight",
7612
7791
  invalid: report?.invalid,
7613
7792
  guards: report?.guards
7614
7793
  };
@@ -7701,7 +7880,19 @@ function createEntriesStore() {
7701
7880
  };
7702
7881
  }
7703
7882
 
7704
- const notReady = (..._args) => Promise.reject(new Error("workflow session not ready yet"));
7883
+ function sessionPlan(args) {
7884
+ const demandedIds = new Set(args.demanded), evaluated = [ ...args.discovered.values() ].filter(inst => demandedIds.has(inst._id));
7885
+ return {
7886
+ evaluated: evaluated,
7887
+ guardScope: evaluated.map(inst => inst._id).sort()
7888
+ };
7889
+ }
7890
+
7891
+ function useDefaultLayout(props) {
7892
+ return useMemo(() => props.renderDefault(props), [ props ]);
7893
+ }
7894
+
7895
+ const notReady = (..._args) => Promise.reject(new Error("workflow session not ready yet")), NO_REGISTERED_DOCUMENTS = [], DEMAND_RELEASE_GRACE_MS = 500;
7705
7896
 
7706
7897
  function workspaceParts(config) {
7707
7898
  if (!config.projectId || !config.dataset) throw new Error("workflow plugin requires a workspace with projectId and dataset");
@@ -7729,10 +7920,11 @@ class InstanceEvaluatorBoundary extends Component {
7729
7920
  }
7730
7921
 
7731
7922
  function InstanceEvaluator(props) {
7732
- const {engine: engine, instanceId: instanceId, grantsFromPath: grantsFromPath, onReport: onReport} = props, {evaluation: evaluation, ready: ready, invalid: invalid, guards: guards, fireAction: fireAction, editField: editField, previewField: previewField, discardFieldPreview: discardFieldPreview} = useWorkflowSession({
7923
+ const {engine: engine, instanceId: instanceId, grantsFromPath: grantsFromPath, guardScope: guardScope, onReport: onReport} = props, {evaluation: evaluation, ready: ready, invalid: invalid, guards: guards, fireAction: fireAction, editField: editField, previewField: previewField, discardFieldPreview: discardFieldPreview} = useWorkflowSession({
7733
7924
  engine: engine,
7734
7925
  instanceId: instanceId,
7735
- grantsFromPath: grantsFromPath
7926
+ grantsFromPath: grantsFromPath,
7927
+ guardScope: guardScope
7736
7928
  });
7737
7929
  return useEffect(() => {
7738
7930
  onReport(instanceId, {
@@ -7775,9 +7967,9 @@ function engineOptionsFrom(config) {
7775
7967
  }
7776
7968
 
7777
7969
  function WorkflowProvider(props) {
7778
- const {config: config} = props, studioClient = useClient({
7970
+ const {config: config} = props, defaultLayout = useDefaultLayout(props), studioClient = useClient({
7779
7971
  apiVersion: WORKFLOW_API_VERSION
7780
- }), schema = useSchema(), {name: workspaceName} = useWorkspace(), {projectId: projectId, dataset: contentDataset} = workspaceParts(studioClient.config()), workflowDataset = config.workflowDataset ?? contentDataset, binding = useMemo(() => ({
7972
+ }), schema = useSchema(), {projectId: projectId, dataset: contentDataset} = workspaceParts(studioClient.config()), workflowDataset = config.workflowDataset ?? contentDataset, binding = useMemo(() => ({
7781
7973
  engineResource: {
7782
7974
  type: "dataset",
7783
7975
  id: `${projectId}.${workflowDataset}`
@@ -7795,7 +7987,15 @@ function WorkflowProvider(props) {
7795
7987
  }), grantsFromPath = aclPathForResource({
7796
7988
  type: "dataset",
7797
7989
  id: `${projectId}.${workflowDataset}`
7798
- }), {ids: registered, acquire: register, release: unregister} = useRefcountedIds(), {ids: requested, acquire: requestInstance, release: releaseInstance} = useRefcountedIds(), [seeded, setSeeded] = useState(/* @__PURE__ */ new Map), seedInstance = useCallback(instance => {
7990
+ }), {ids: registered, acquire: registerActive, release: unregisterActive} = useRefcountedIds(), {ids: observed, acquire: retainRegistration, release: releaseRegistration} = useRefcountedIds({
7991
+ releaseDelayMs: DEMAND_RELEASE_GRACE_MS
7992
+ }), register = useCallback(id => {
7993
+ registerActive(id), retainRegistration(id);
7994
+ }, [ registerActive, retainRegistration ]), unregister = useCallback(id => {
7995
+ unregisterActive(id), releaseRegistration(id);
7996
+ }, [ unregisterActive, releaseRegistration ]), {ids: requested, acquire: requestInstance, release: releaseInstance} = useRefcountedIds(), {ids: evaluationDemand, acquire: requestEvaluation, release: releaseEvaluation} = useRefcountedIds({
7997
+ releaseDelayMs: DEMAND_RELEASE_GRACE_MS
7998
+ }), [seeded, setSeeded] = useState(/* @__PURE__ */ new Map), seedInstance = useCallback(instance => {
7799
7999
  setSeeded(prev => new Map(prev).set(instance._id, instance));
7800
8000
  }, []), [startRequest2, setStartRequest] = useState(void 0), openStartDialog = useCallback(r => setStartRequest(r), []), closeStartDialog = useCallback(() => setStartRequest(void 0), []), [entriesStore] = useState(createEntriesStore);
7801
8001
  useEffect(() => () => entriesStore.dispose(), [ entriesStore ]);
@@ -7846,15 +8046,18 @@ function WorkflowProvider(props) {
7846
8046
  });
7847
8047
  }, [ engine ]);
7848
8048
  drainRef.current = drainEffectsFor;
7849
- const {watching: watching, filter: filter, loading: loading, invalid: discoveryInvalid, resolved: resolved, onSnapshot: onSnapshot} = useDiscoveryState({
7850
- registered: registered,
8049
+ const {documentFilter: documentFilter, requestedFilter: requestedFilter, loading: loading, invalid: discoveryInvalid, resolved: resolved, onDocumentSnapshot: onDocumentSnapshot, onRequestedSnapshot: onRequestedSnapshot} = useDiscoveryState({
8050
+ observed: observed,
7851
8051
  requested: requested,
7852
8052
  contentResource: binding.contentResource
7853
- }), {instances: discovered, index: docIndex} = useMemo(() => indexDiscoveredInstances({
8053
+ }), discoveryIndexer = useMemo(createDiscoveryIndexer, []), {instances: discovered, index: docIndex} = useMemo(() => discoveryIndexer.index({
7854
8054
  instances: resolved.instances ?? [],
7855
8055
  registered: resolved.registered,
7856
8056
  contentResource: binding.contentResource
7857
- }), [ resolved, binding.contentResource ]), resolvedDocs = useMemo(() => new Set(resolved.registered), [ resolved.registered ]);
8057
+ }), [ discoveryIndexer, resolved, binding.contentResource ]), {evaluated: evaluated, guardScope: guardScope} = useMemo(() => sessionPlan({
8058
+ discovered: discovered,
8059
+ demanded: evaluationDemand
8060
+ }), [ discovered, evaluationDemand ]), resolvedDocs = useMemo(() => new Set(resolved.registered), [ resolved.registered ]);
7858
8061
  useEffect(() => {
7859
8062
  setSeeded(prev => {
7860
8063
  const kept = [ ...prev ].filter(([id]) => !discovered.has(id));
@@ -7867,15 +8070,13 @@ function WorkflowProvider(props) {
7867
8070
  docIndex: docIndex
7868
8071
  });
7869
8072
  }, [ entriesStore, seeded, discovered, docIndex ]);
7870
- const isDocResolved = useCallback(docId => resolvedDocs.has(docId), [ resolvedDocs ]), schemaContentTypes = useMemo(() => new Set(contentDocumentTypes(schema)), [ schema ]), {issues: issues, starts: starts, fields: fields, latestVersions: latestVersions, autoStartByType: autoStartByType, autoStartDefinitions: autoStartDefinitions} = useDeployedDefinitions({
8073
+ const isDocResolved = useCallback(docId => resolvedDocs.has(docId), [ resolvedDocs ]), schemaContentTypes = useMemo(() => new Set(contentDocumentTypes(schema)), [ schema ]), {mappings: mappings, issues: issues, starts: starts, fields: fields, latestVersions: latestVersions, autoStartByType: autoStartByType, autoStartDefinitions: autoStartDefinitions} = useDeployedDefinitions({
7871
8074
  engine: engine,
7872
- mappings: config.mappings,
8075
+ overrides: config.mappings,
7873
8076
  contentResource: binding.contentResource,
7874
8077
  schemaContentTypes: schemaContentTypes,
7875
8078
  startRequest: startRequest2,
7876
- autoStart: config.autoStart,
7877
- schema: schema,
7878
- workspaceName: workspaceName
8079
+ schema: schema
7879
8080
  }), value = useMemo(() => ({
7880
8081
  register: register,
7881
8082
  unregister: unregister,
@@ -7883,6 +8084,8 @@ function WorkflowProvider(props) {
7883
8084
  entries: entriesStore,
7884
8085
  requestInstance: requestInstance,
7885
8086
  releaseInstance: releaseInstance,
8087
+ requestEvaluation: requestEvaluation,
8088
+ releaseEvaluation: releaseEvaluation,
7886
8089
  seedInstance: seedInstance,
7887
8090
  openStartDialog: openStartDialog,
7888
8091
  closeStartDialog: closeStartDialog,
@@ -7897,7 +8100,7 @@ function WorkflowProvider(props) {
7897
8100
  completeEffectFor: completeEffectFor,
7898
8101
  engine: engine,
7899
8102
  binding: binding,
7900
- mappings: config.mappings,
8103
+ mappings: mappings,
7901
8104
  mappingIssues: issues,
7902
8105
  mappingStarts: starts,
7903
8106
  mappingFields: fields,
@@ -7905,24 +8108,30 @@ function WorkflowProvider(props) {
7905
8108
  effectHandlers: engine.effectHandlers,
7906
8109
  autoStartByType: autoStartByType,
7907
8110
  autoStartDefinitions: autoStartDefinitions
7908
- }), [ register, unregister, isDocResolved, entriesStore, requestInstance, releaseInstance, seedInstance, openStartDialog, closeStartDialog, startRequest2, loading, discoveryInvalid, fireActionFor, editFieldFor, previewFieldFor, discardFieldPreviewFor, drainEffectsFor, completeEffectFor, engine, binding, config.mappings, issues, starts, latestVersions, autoStartByType, autoStartDefinitions ]);
8111
+ }), [ register, unregister, isDocResolved, entriesStore, requestInstance, releaseInstance, requestEvaluation, releaseEvaluation, seedInstance, openStartDialog, closeStartDialog, startRequest2, loading, discoveryInvalid, fireActionFor, editFieldFor, previewFieldFor, discardFieldPreviewFor, drainEffectsFor, completeEffectFor, engine, binding, mappings, issues, starts, fields, latestVersions, autoStartByType, autoStartDefinitions ]);
7909
8112
  /* @__PURE__ */
7910
8113
  return jsxs(WorkflowContext.Provider, {
7911
8114
  value: value,
7912
- children: [ watching ? /* @__PURE__ */ jsx(DiscoverySubscription, {
8115
+ children: [ documentFilter ? /* @__PURE__ */ jsx(DiscoverySubscription, {
7913
8116
  engine: engine,
7914
- filter: filter,
7915
- onSnapshot: onSnapshot,
8117
+ filter: documentFilter,
8118
+ onSnapshot: onDocumentSnapshot,
7916
8119
  registered: registered
7917
- }) : null, [ ...discovered.values() ].map(inst => /* @__PURE__ */ jsx(InstanceEvaluatorBoundary, {
8120
+ }) : null, requestedFilter ? /* @__PURE__ */ jsx(DiscoverySubscription, {
8121
+ engine: engine,
8122
+ filter: requestedFilter,
8123
+ onSnapshot: onRequestedSnapshot,
8124
+ registered: NO_REGISTERED_DOCUMENTS
8125
+ }) : null, evaluated.map(inst => /* @__PURE__ */ jsx(InstanceEvaluatorBoundary, {
7918
8126
  instanceId: inst._id,
7919
8127
  children: /* @__PURE__ */ jsx(InstanceEvaluator, {
7920
8128
  engine: engine,
7921
8129
  grantsFromPath: grantsFromPath,
8130
+ guardScope: guardScope,
7922
8131
  instanceId: inst._id,
7923
8132
  onReport: onReport
7924
8133
  })
7925
- }, inst._id)), props.renderDefault(props), props.overlay ]
8134
+ }, inst._id)), defaultLayout, props.overlay ]
7926
8135
  });
7927
8136
  }
7928
8137
 
@@ -7932,14 +8141,25 @@ function sameEntries(a, b) {
7932
8141
  return !0;
7933
8142
  }
7934
8143
 
8144
+ function sameMappings(a, b) {
8145
+ return a.length === b.length && a.every((mapping, index) => {
8146
+ const other = b[index];
8147
+ return other !== void 0 && mappingKey(mapping) === mappingKey(other) && mapping.label === other.label && mapping.autoStart === other.autoStart && mapping.initialStateBuilder === other.initialStateBuilder && mapping.contextBuilder === other.contextBuilder && mapping.perspectiveField === other.perspectiveField;
8148
+ });
8149
+ }
8150
+
7935
8151
  function useDeployedDefinitions(args) {
7936
- const {engine: engine, mappings: mappings, contentResource: contentResource, schemaContentTypes: schemaContentTypes, startRequest: startRequest2, autoStart: autoStart, schema: schema, workspaceName: workspaceName} = args, [issues, setIssues] = useState(/* @__PURE__ */ new Map), [starts, setStarts] = useState(/* @__PURE__ */ new Map), [fields, setFields] = useState(/* @__PURE__ */ new Map), [latestVersions, setLatestVersions] = useState(/* @__PURE__ */ new Map), [autoStartByType, setAutoStartByType] = useState(
8152
+ const {engine: engine, overrides: overrides, contentResource: contentResource, schemaContentTypes: schemaContentTypes, startRequest: startRequest2, schema: schema} = args, [mappings, setMappings] = useState([]), [issues, setIssues] = useState(/* @__PURE__ */ new Map), [starts, setStarts] = useState(/* @__PURE__ */ new Map), [fields, setFields] = useState(/* @__PURE__ */ new Map), [latestVersions, setLatestVersions] = useState(/* @__PURE__ */ new Map), [autoStartByType, setAutoStartByType] = useState(
7937
8153
  /* @__PURE__ */ new Map), [autoStartDefinitions, setAutoStartDefinitions] = useState(/* @__PURE__ */ new Map), logged = useRef(/* @__PURE__ */ new Set);
7938
8154
  return useEffect(() => {
7939
8155
  let cancelled = !1;
7940
8156
  return (async () => {
7941
- const definitions = await readDeployedDefinitions(engine), found = mappingIssues({
7942
- mappings: mappings,
8157
+ const definitions = await readDeployedDefinitions(engine), effectiveMappings = discoverWorkflowMappings({
8158
+ definitions: definitions,
8159
+ schemaContentTypes: schemaContentTypes,
8160
+ overrides: overrides
8161
+ }), found = mappingIssues({
8162
+ mappings: effectiveMappings,
7943
8163
  definitions: definitions,
7944
8164
  contentResource: contentResource,
7945
8165
  schemaContentTypes: schemaContentTypes
@@ -7949,32 +8169,30 @@ function useDeployedDefinitions(args) {
7949
8169
  logged.current.has(signature) || (logged.current.add(signature), console.error(`[workflow-studio-plugin] ${message}`));
7950
8170
  }
7951
8171
  const resolvedAutoStart = resolveAutoStart({
7952
- autoStart: autoStart,
7953
- context: {
7954
- workspaceName: workspaceName,
7955
- schema: schema
7956
- },
8172
+ autoStart: mappingAutoStartMap(effectiveMappings),
7957
8173
  knownDocTypes: new Set(schema.getTypeNames()),
7958
8174
  definitions: definitions
7959
8175
  });
7960
8176
  if (logOnce(resolvedAutoStart.warnings, logged.current), cancelled) return;
8177
+ setMappings(prev => sameMappings(prev, effectiveMappings) ? prev : effectiveMappings),
7961
8178
  setIssues(prev => sameEntries(prev, found) ? prev : found), setStarts(mappingStartBlocks({
7962
- mappings: mappings,
8179
+ mappings: effectiveMappings,
7963
8180
  definitions: definitions
7964
8181
  })), setFields(mappingDeclaredFields({
7965
- mappings: mappings,
8182
+ mappings: effectiveMappings,
7966
8183
  definitions: definitions
7967
8184
  }));
7968
8185
  const versions = new Map(latestDeployedDefinitions(definitions).map(d => [ d.name, d.version ]));
7969
8186
  setLatestVersions(prev => sameEntries(prev, versions) ? prev : versions), setAutoStartByType(resolvedAutoStart.byType),
7970
8187
  setAutoStartDefinitions(resolvedAutoStart.definitions);
7971
8188
  })().catch(err => {
7972
- console.error("[workflow-studio-plugin] mapping validation could not read the deployed definitions keeping the previous verdicts:", err);
8189
+ console.error("[workflow-studio-plugin] mapping discovery failed keeping the last resolved workflow state:", err);
7973
8190
  }), () => {
7974
8191
  cancelled = !0;
7975
8192
  };
7976
- }, [ engine, mappings, contentResource, schemaContentTypes, startRequest2, autoStart, schema, workspaceName ]),
8193
+ }, [ engine, overrides, contentResource, schemaContentTypes, startRequest2, schema ]),
7977
8194
  {
8195
+ mappings: mappings,
7978
8196
  issues: issues,
7979
8197
  starts: starts,
7980
8198
  fields: fields,
@@ -8760,7 +8978,7 @@ function isFreshDocument(value) {
8760
8978
  }
8761
8979
 
8762
8980
  function subjectEntry(definition) {
8763
- return (definition.fields ?? []).find(isSubjectEntry);
8981
+ return documentSubjectEntry(definition);
8764
8982
  }
8765
8983
 
8766
8984
  function autoStartInputs(args) {
@@ -8787,11 +9005,11 @@ function pendingWorkflows(workflows, live) {
8787
9005
  }
8788
9006
 
8789
9007
  function extraRequiredInputs(definition) {
8790
- const fields = definition.fields ?? [], subjectNames = new Set(fields.filter(isSubjectEntry).map(entry => entry.name)), requiredNames = new Set(missingRequiredInputs({
9008
+ const fields = definition.fields ?? [], subjectName = subjectEntry(definition)?.name, requiredNames = new Set(missingRequiredInputs({
8791
9009
  entryDefs: fields,
8792
9010
  initialFields: []
8793
9011
  }).map(input => input.name));
8794
- return fields.filter(entry => requiredNames.has(entry.name) && !subjectNames.has(entry.name));
9012
+ return fields.filter(entry => requiredNames.has(entry.name) && entry.name !== subjectName);
8795
9013
  }
8796
9014
 
8797
9015
  function buildAutoStartRequests(args) {
@@ -8839,7 +9057,7 @@ function AutoStartDriver(props) {
8839
9057
  const pending = useMemo(() => pendingWorkflows(workflows, new Set(entries.map(entry => entry.instance.definition))), [ workflows, entries ]), inputs = useMemo(() => autoStartInputs({
8840
9058
  workflows: pending,
8841
9059
  definitions: autoStartDefinitions
8842
- }), [ pending, autoStartDefinitions ]), pendingRef = useRef(pending);
9060
+ }), [ pending, autoStartDefinitions ]), definitionsResolved = workflows.every(name => autoStartDefinitions.has(name)), pendingRef = useRef(pending);
8843
9061
  pendingRef.current = pending;
8844
9062
  const instanceIds = useRef(/* @__PURE__ */ new Map), instanceIdFor = useCallback(workflow => {
8845
9063
  const held = instanceIds.current.get(workflow);
@@ -8883,7 +9101,7 @@ function AutoStartDriver(props) {
8883
9101
  }, [ autoStartDefinitions, docId, docType, binding, engine, observer, seedInstance, instanceIdFor, selectedReleaseId ]), retry = useCallback(() => {
8884
9102
  firedRef.current = !1, setFired(!1), setSettled(!1), setFailure(void 0);
8885
9103
  }, []), action = autoStartAction({
8886
- resolved: isDocResolved(docId),
9104
+ resolved: definitionsResolved && isDocResolved(docId),
8887
9105
  pendingCount: pending.length,
8888
9106
  needsInput: inputs.length > 0,
8889
9107
  fired: fired,
@@ -9305,6 +9523,61 @@ function TaskCountSpinner({entry: entry}) {
9305
9523
  });
9306
9524
  }
9307
9525
 
9526
+ function stagePillState(instance) {
9527
+ const definition = definitionSnapshotOf(instance), stage = definition?.stages.find(candidate => candidate.name === instance.currentStage), state = terminalState(instance);
9528
+ return {
9529
+ title: stageTitle(definition, instance.currentStage),
9530
+ terminal: stage !== void 0 && isTerminalStage(stage) || state !== "in-flight",
9531
+ state: state,
9532
+ stageKnown: stage !== void 0
9533
+ };
9534
+ }
9535
+
9536
+ function WorkflowStagePreview(props) {
9537
+ const {mappings: mappings} = useWorkflowContext(), mapped = props.schemaType !== void 0 && mappingsForDocType(mappings, props.schemaType.name).length > 0, {entries: entries} = useCommittedWorkflowsForDocument(mapped ? props._id ?? null : null), lead = entries.find(isLiveEntry) ?? entries[0], extraLive = entries.filter(e => e !== lead && isLiveEntry(e)).length;
9538
+ return props.renderDefault({
9539
+ ...props,
9540
+ status: lead ? /* @__PURE__ */ jsx(StagePill, {
9541
+ entry: lead,
9542
+ extraLive: extraLive
9543
+ }) : props.status
9544
+ });
9545
+ }
9546
+
9547
+ function StagePill({entry: entry, extraLive: extraLive}) {
9548
+ /* @__PURE__ */
9549
+ return jsxs(Flex, {
9550
+ align: "center",
9551
+ gap: 2,
9552
+ children: [
9553
+ /* @__PURE__ */ jsx(LeadBadge, {
9554
+ entry: entry
9555
+ }), extraLive > 0 ? /* @__PURE__ */ jsxs(Badge, {
9556
+ fontSize: 1,
9557
+ mode: "outline",
9558
+ tone: "default",
9559
+ children: [ "+", extraLive ]
9560
+ }) : null ]
9561
+ });
9562
+ }
9563
+
9564
+ function LeadBadge({entry: entry}) {
9565
+ const {instance: instance} = entry;
9566
+ if (isUnprimed(instance)) /* @__PURE__ */ return jsx(StartIncompleteBadge, {});
9567
+ const pill = stagePillState(instance);
9568
+ /* @__PURE__ */
9569
+ return jsxs(Badge, {
9570
+ fontSize: 1,
9571
+ mode: pill.terminal ? "default" : "outline",
9572
+ tone: pillTone(pill),
9573
+ children: [ pill.title, pill.state === "completed" ? " ✓" : "" ]
9574
+ });
9575
+ }
9576
+
9577
+ function pillTone(args) {
9578
+ return args.state === "aborted" ? "critical" : args.terminal ? "positive" : args.stageKnown ? "primary" : "default";
9579
+ }
9580
+
9308
9581
  function StartWorkflowDialogHost() {
9309
9582
  const {startRequest: startRequest2, closeStartDialog: closeStartDialog} = useWorkflowContext();
9310
9583
  return startRequest2 ? /* @__PURE__ */ jsx(StartWorkflowDialog, {
@@ -9377,7 +9650,8 @@ function withWorkflowLock(Action, guardAction) {
9377
9650
  Locked;
9378
9651
  }
9379
9652
 
9380
- const workflowStudioPlugin = definePlugin(config => ({
9653
+ const workflowStudioPlugin = definePlugin(config => (assertUniqueWorkflowMappings(config.mappings ?? []),
9654
+ {
9381
9655
  name: "workflow-studio-plugin",
9382
9656
  tools: prev => [ ...prev, workflowsTool ],
9383
9657
  studio: {
@@ -9391,82 +9665,35 @@ const workflowStudioPlugin = definePlugin(config => ({
9391
9665
  },
9392
9666
  form: {
9393
9667
  components: {
9394
- input: props => {
9395
- if (props.id === "root" && isObjectInputProps(props)) {
9396
- const docTypeMappings = mappingsForDocType(config.mappings, props.schemaType.name), fallback = docTypeMappings.length > 0 ? /* @__PURE__ */ jsx(WorkflowFormStrip, {
9397
- ...props,
9398
- mappings: docTypeMappings
9399
- }) : props.renderDefault(props);
9400
- /* @__PURE__ */
9401
- return jsx(AutoStartGate, {
9402
- ...props,
9403
- fallback: fallback
9404
- });
9405
- }
9406
- return props.renderDefault(props);
9407
- }
9668
+ preview: WorkflowStagePreview,
9669
+ input: props => props.id === "root" && isObjectInputProps(props) ? /* @__PURE__ */ jsx(WorkflowRootInput, {
9670
+ ...props
9671
+ }) : props.renderDefault(props)
9408
9672
  }
9409
9673
  },
9410
9674
  document: {
9411
9675
  badges: (prev, ctx) => {
9412
- if (!mappingForDocType(config.mappings, ctx.schemaType) || !ctx.documentId) return prev;
9676
+ if (!ctx.documentId) return prev;
9413
9677
  const docId = ctx.documentId;
9414
9678
  return [ makeActiveWorkflowsBadge(docId), makePerspectiveBadge(docId), ...prev ];
9415
9679
  },
9416
- actions: (prev, ctx) => mappingForDocType(config.mappings, ctx.schemaType) ? prev.map(Action => {
9680
+ actions: prev => prev.map(Action => {
9417
9681
  const guardAction = Action.action ? LOCKABLE_ACTIONS.get(Action.action) : void 0;
9418
9682
  return guardAction ? withWorkflowLock(Action, guardAction) : Action;
9419
- }) : prev
9683
+ })
9420
9684
  }
9421
9685
  }));
9422
9686
 
9423
- function WorkflowStagePreview(props) {
9424
- const {entries: entries} = useWorkflowsForDocument(props.documentId ?? props._id ?? null), lead = entries.find(isLiveEntry) ?? entries[0], extraLive = entries.filter(e => e !== lead && isLiveEntry(e)).length;
9425
- return props.renderDefault({
9687
+ function WorkflowRootInput(props) {
9688
+ const {mappings: mappings} = useWorkflowContext(), docTypeMappings = mappingsForDocType(mappings, props.schemaType.name), fallback = docTypeMappings.length > 0 ? /* @__PURE__ */ jsx(WorkflowFormStrip, {
9426
9689
  ...props,
9427
- status: lead ? /* @__PURE__ */ jsx(StagePill, {
9428
- entry: lead,
9429
- extraLive: extraLive
9430
- }) : props.status
9431
- });
9432
- }
9433
-
9434
- function StagePill({entry: entry, extraLive: extraLive}) {
9690
+ mappings: docTypeMappings
9691
+ }) : props.renderDefault(props);
9435
9692
  /* @__PURE__ */
9436
- return jsxs(Flex, {
9437
- align: "center",
9438
- gap: 2,
9439
- children: [
9440
- /* @__PURE__ */ jsx(LeadBadge, {
9441
- entry: entry
9442
- }), extraLive > 0 ? /* @__PURE__ */ jsxs(Badge, {
9443
- fontSize: 1,
9444
- mode: "outline",
9445
- tone: "default",
9446
- children: [ "+", extraLive ]
9447
- }) : null ]
9448
- });
9449
- }
9450
-
9451
- function LeadBadge({entry: entry}) {
9452
- const {instance: instance, evaluation: evaluation} = entry;
9453
- if (isUnprimed(instance)) /* @__PURE__ */ return jsx(StartIncompleteBadge, {});
9454
- const stage = evaluation?.currentStage?.stage, title = stage?.title ?? stageTitle(definitionSnapshotOf(instance), instance.currentStage), state = terminalState(instance), isTerminal = (stage ? isTerminalStage(stage) : !1) || state !== "in-flight";
9455
- /* @__PURE__ */
9456
- return jsxs(Badge, {
9457
- fontSize: 1,
9458
- mode: isTerminal ? "default" : "outline",
9459
- tone: pillTone({
9460
- state: state,
9461
- isTerminal: isTerminal,
9462
- stageKnown: stage !== void 0
9463
- }),
9464
- children: [ title, state === "completed" ? " ✓" : "" ]
9693
+ return jsx(AutoStartGate, {
9694
+ ...props,
9695
+ fallback: fallback
9465
9696
  });
9466
9697
  }
9467
9698
 
9468
- function pillTone(args) {
9469
- return args.state === "aborted" ? "critical" : args.isTerminal ? "positive" : args.stageKnown ? "primary" : "default";
9470
- }
9471
-
9472
- export { AbortedBadge, ActivitiesList, ActivityDetailDialog, ActivityRow, BreadcrumbTail, CollapsibleBand, DismissablePopover, DocPreviewLink, DocRefFace, GroupHeading, HoverHint, InvalidDocNotice, LinkChip, LoadingRow, StageChip, StageFace, StaleLock, TabSwitch, TodoItemsList, TrailingHairline, UnreadableDocsNote, WorkflowStagePreview, activityRowProps, assigneeUserIdsOf, committedRowFace, dateControlKind, dateControlValue, definitionSnapshotOf, describeError, editTargetOf, findActivity, findActivityNode, formatDate, gdrLocality, instanceBreadcrumb, instanceNotice, instanceTitle, isActivityAssignedTo, isEvaluationStale, isOpenActivityStatus, mappingForDocType, openActivityGone, parseDateFieldValue, parseStoredDateValue, readDeployedDefinitions, rowAssignControlState, rowDateControlState, serializeDateFieldValue, stageTitle, stateByActivity, stopRowClick, stopRowMouseDown, useAssignmentIdentity, useDefinition, useProjectMembers, useSaveField, useUserDisplays, useWorkflowContext, useWorkflowInstanceEntry, workflowDefaultDocumentNode, workflowDocTypes, workflowStudioPlugin, workflowsTabCodec, workflowsView };
9699
+ export { AbortedBadge, ActivitiesList, ActivityDetailDialog, ActivityRow, BreadcrumbTail, CollapsibleBand, DismissablePopover, DocPreviewLink, DocRefFace, GroupHeading, HoverHint, InvalidDocNotice, LinkChip, LoadingRow, SpinnerSlot, StageChip, StageFace, StaleLock, TabSwitch, TodoItemsList, TrailingHairline, UnreadableDocsNote, activityRowProps, assigneeUserIdsOf, committedRowFace, dateControlKind, dateControlValue, definitionSnapshotOf, describeError, editTargetOf, findActivity, findActivityNode, formatDate, gdrLocality, instanceBreadcrumb, instanceNotice, instanceTitle, isActivityAssignedTo, isEvaluationStale, isOpenActivityStatus, openActivityGone, parseDateFieldValue, parseStoredDateValue, readDeployedDefinitions, rowAssignControlState, rowDateControlState, serializeDateFieldValue, stageTitle, stateByActivity, stopRowClick, stopRowMouseDown, useAssignmentIdentity, useDefinition, useProjectMembers, useSaveField, useUserDisplays, useWorkflowContext, useWorkflowInstanceEntry, workflowDefaultDocumentNode, workflowStudioPlugin, workflowsTabCodec, workflowsView };