@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,6 +1,6 @@
1
1
  "use strict";
2
2
 
3
- var workflowEngine = require("@sanity/workflow-engine"), jsxRuntime = require("react/jsx-runtime"), Timeline = require("@sanity/icons/Timeline"), ui = require("@sanity/ui"), react = require("react"), structure = require("sanity/structure"), formatDistanceToNow = require("date-fns/formatDistanceToNow"), csm = require("@sanity/client/csm"), workflowStudio = require("@sanity/workflow-studio"), sanity = require("sanity"), Calendar = require("@sanity/icons/Calendar"), workflowComponents = require("@sanity/workflow-components"), User = require("@sanity/icons/User"), Close = require("@sanity/icons/Close"), Search = require("@sanity/icons/Search"), ChevronRight = require("@sanity/icons/ChevronRight"), Checkmark = require("@sanity/icons/Checkmark"), Edit = require("@sanity/icons/Edit"), content = require("@sanity/util/content"), InfoOutline = require("@sanity/icons/InfoOutline"), Document = require("@sanity/icons/Document"), WarningOutline = require("@sanity/icons/WarningOutline"), router = require("sanity/router"), types = require("@sanity/types"), Launch = require("@sanity/icons/Launch"), Empty$1 = require("@sanity/icons/Empty"), ErrorFilled = require("@sanity/icons/ErrorFilled"), RemoveCircle = require("@sanity/icons/RemoveCircle"), Add = require("@sanity/icons/Add"), Bolt = require("@sanity/icons/Bolt"), ChevronDown = require("@sanity/icons/ChevronDown"), CheckmarkCircle = require("@sanity/icons/CheckmarkCircle"), Ulist = require("@sanity/icons/Ulist"), pluralize = require("pluralize-esm"), EllipsisHorizontal = require("@sanity/icons/EllipsisHorizontal"), ArrowUp = require("@sanity/icons/ArrowUp"), workflowReact = require("@sanity/workflow-react"), Transfer = require("@sanity/icons/Transfer");
3
+ var jsxRuntime = require("react/jsx-runtime"), Timeline = require("@sanity/icons/Timeline"), ui = require("@sanity/ui"), react = require("react"), structure = require("sanity/structure"), workflowEngine = require("@sanity/workflow-engine"), Checkmark = require("@sanity/icons/Checkmark"), Close = require("@sanity/icons/Close"), formatDistanceToNow = require("date-fns/formatDistanceToNow"), csm = require("@sanity/client/csm"), workflowStudio = require("@sanity/workflow-studio"), sanity = require("sanity"), Calendar = require("@sanity/icons/Calendar"), workflowComponents = require("@sanity/workflow-components"), User = require("@sanity/icons/User"), Search = require("@sanity/icons/Search"), ChevronRight = require("@sanity/icons/ChevronRight"), Edit = require("@sanity/icons/Edit"), content = require("@sanity/util/content"), InfoOutline = require("@sanity/icons/InfoOutline"), Document = require("@sanity/icons/Document"), WarningOutline = require("@sanity/icons/WarningOutline"), router = require("sanity/router"), types = require("@sanity/types"), Launch = require("@sanity/icons/Launch"), Empty$1 = require("@sanity/icons/Empty"), ErrorFilled = require("@sanity/icons/ErrorFilled"), RemoveCircle = require("@sanity/icons/RemoveCircle"), Add = require("@sanity/icons/Add"), Bolt = require("@sanity/icons/Bolt"), ChevronDown = require("@sanity/icons/ChevronDown"), CheckmarkCircle = require("@sanity/icons/CheckmarkCircle"), Ulist = require("@sanity/icons/Ulist"), pluralize = require("pluralize-esm"), EllipsisHorizontal = require("@sanity/icons/EllipsisHorizontal"), ArrowUp = require("@sanity/icons/ArrowUp"), workflowReact = require("@sanity/workflow-react"), Transfer = require("@sanity/icons/Transfer");
4
4
 
5
5
  function _interopDefaultCompat(e) {
6
6
  return e && typeof e == "object" && "default" in e ? e : {
@@ -23,20 +23,58 @@ function gdrLocality(id, contentResource) {
23
23
  };
24
24
  }
25
25
 
26
- function mappingsForDocType(mappings, docType) {
27
- return mappings.filter(m => m.docType === docType);
26
+ function assertUniqueWorkflowMappings(mappings) {
27
+ const keys = /* @__PURE__ */ new Set;
28
+ for (const mapping of mappings) {
29
+ const key = mappingKey(mapping);
30
+ if (keys.has(key)) throw new Error(`Duplicate workflow mapping for ${key}`);
31
+ keys.add(key);
32
+ }
28
33
  }
29
34
 
30
- function mappingForDocType(mappings, docType) {
31
- return mappingsForDocType(mappings, docType)[0];
35
+ function discoverWorkflowMappings(args) {
36
+ assertUniqueWorkflowMappings(args.overrides ?? []);
37
+ const discovered = workflowEngine.latestDeployedDefinitions(args.definitions).flatMap(definition => {
38
+ if (!workflowEngine.isStartableDefinition(definition)) return [];
39
+ const subject = (definition.fields ?? []).find(workflowEngine.isSubjectEntry);
40
+ if (subject === void 0 || !workflowEngine.isInputSourced(subject)) return [];
41
+ const source = {
42
+ fields: [ ...definition.fields ?? [] ]
43
+ };
44
+ return [ ...args.schemaContentTypes ].filter(docType => workflowEngine.acceptsDocumentType(source, docType)).map(docType => ({
45
+ docType: docType,
46
+ definition: definition.name,
47
+ label: definition.title ?? definition.name
48
+ }));
49
+ }), byKey = new Map(discovered.map(mapping => [ mappingKey(mapping), mapping ]));
50
+ for (const override of args.overrides ?? []) {
51
+ const key = mappingKey(override);
52
+ byKey.set(key, override);
53
+ }
54
+ return [ ...byKey.values() ];
55
+ }
56
+
57
+ function mappingAutoStartMap(mappings) {
58
+ const byType = /* @__PURE__ */ Object.create(null);
59
+ for (const mapping of mappings) {
60
+ if (!mapping.autoStart) continue;
61
+ const definitions = byType[mapping.docType] ?? [];
62
+ byType[mapping.docType] = [ ...definitions, mapping.definition ];
63
+ }
64
+ return byType;
65
+ }
66
+
67
+ function mappingsForDocType(mappings, docType) {
68
+ return mappings.filter(m => m.docType === docType);
32
69
  }
33
70
 
34
71
  function mappingKey(mapping) {
35
72
  return `${mapping.docType}::${mapping.definition}`;
36
73
  }
37
74
 
38
- function workflowDocTypes(mappings) {
39
- return new Set(mappings.map(m => m.docType));
75
+ function documentSubjectEntry(definition) {
76
+ const fields = definition.fields ?? [];
77
+ return fields.find(workflowEngine.isSubjectEntry) ?? fields.find(entry => entry.type === "doc.ref" && entry.name === "subject");
40
78
  }
41
79
 
42
80
  function mappingStartBlocks(args) {
@@ -357,7 +395,7 @@ function stateByActivity(instance) {
357
395
  }
358
396
 
359
397
  function evaluationFreshness(args) {
360
- return args.evaluation ? args.committed.lastChangedAt > args.evaluation.instance.lastChangedAt ? "stale" : "current" : "pending";
398
+ return args.evaluation ? Date.parse(args.committed.lastChangedAt) > Date.parse(args.evaluation.instance.lastChangedAt) ? "stale" : "current" : "pending";
361
399
  }
362
400
 
363
401
  function isEvaluationCurrent(args) {
@@ -404,12 +442,27 @@ function useWorkflowContext() {
404
442
  }
405
443
 
406
444
  function useWorkflowsForDocument(rawId) {
407
- const {register: register, unregister: unregister, entries: entries} = useWorkflowContext(), docId = rawId ? csm.getPublishedId(rawId) : void 0;
445
+ return useDocumentWorkflowEntries(rawId, !0);
446
+ }
447
+
448
+ function useCommittedWorkflowsForDocument(rawId) {
449
+ return useDocumentWorkflowEntries(rawId, !1);
450
+ }
451
+
452
+ function useDocumentWorkflowEntries(rawId, evaluate) {
453
+ const {register: register, unregister: unregister, requestEvaluation: requestEvaluation, releaseEvaluation: releaseEvaluation, entries: entries} = useWorkflowContext(), docId = rawId ? csm.getPublishedId(rawId) : void 0;
408
454
  react.useEffect(() => {
409
455
  if (docId) return register(docId), () => unregister(docId);
410
456
  }, [ docId, register, unregister ]);
411
- const list = react.useSyncExternalStore(entries.subscribe, react.useCallback(() => docId ? entries.getDocument(docId) : EMPTY_ENTRIES, [ entries, docId ]));
412
- return {
457
+ const list = react.useSyncExternalStore(entries.subscribe, react.useCallback(() => docId ? entries.getDocument(docId) : EMPTY_ENTRIES, [ entries, docId ])), evaluationKey = evaluate ? list.map(entry => entry.instance._id).join("\0") : "";
458
+ return react.useEffect(() => {
459
+ if (!evaluate) return;
460
+ const ids = evaluationKey === "" ? [] : evaluationKey.split("\0");
461
+ for (const id of ids) requestEvaluation(id);
462
+ return () => {
463
+ for (const id of ids) releaseEvaluation(id);
464
+ };
465
+ }, [ evaluate, evaluationKey, requestEvaluation, releaseEvaluation ]), {
413
466
  docId: docId,
414
467
  entries: list
415
468
  };
@@ -420,11 +473,13 @@ function useHeldWorkflowEntry(instanceId) {
420
473
  return react.useSyncExternalStore(entries.subscribe, react.useCallback(() => instanceId ? entries.getInstance(instanceId) : void 0, [ entries, instanceId ]));
421
474
  }
422
475
 
423
- function useWorkflowInstanceEntry(instanceId) {
424
- const {requestInstance: requestInstance, releaseInstance: releaseInstance} = useWorkflowContext();
476
+ function useWorkflowInstanceEntry(instanceId, options = {}) {
477
+ const {requestInstance: requestInstance, releaseInstance: releaseInstance, requestEvaluation: requestEvaluation, releaseEvaluation: releaseEvaluation} = useWorkflowContext(), evaluate = options.evaluate ?? !0;
425
478
  return react.useEffect(() => {
426
479
  if (instanceId) return requestInstance(instanceId), () => releaseInstance(instanceId);
427
- }, [ instanceId, requestInstance, releaseInstance ]), useHeldWorkflowEntry(instanceId);
480
+ }, [ instanceId, requestInstance, releaseInstance ]), react.useEffect(() => {
481
+ if (!(!instanceId || !evaluate)) return requestEvaluation(instanceId), () => releaseEvaluation(instanceId);
482
+ }, [ instanceId, evaluate, requestEvaluation, releaseEvaluation ]), useHeldWorkflowEntry(instanceId);
428
483
  }
429
484
 
430
485
  const isTodoDone = item => item.status === "done", toggledTodoStatus = item => isTodoDone(item) ? "open" : "done";
@@ -697,13 +752,29 @@ function concludesHere(actionEval, activityName) {
697
752
  return changesOwnStatus(actionEval.action, activityName) || actionEval.firing?.exitsStage === !0;
698
753
  }
699
754
 
700
- function actionButtonTone(args) {
701
- return concludesHere(args.actionEval, args.activityName) ? {
702
- mode: "default",
703
- tone: actionAdvanceStatus(args.actionEval.action, args.activityName) === "failed" ? "critical" : "default"
704
- } : {
705
- mode: "ghost",
706
- tone: "default"
755
+ const SEMANTIC_FACE = {
756
+ "decision.accept": {
757
+ tone: "positive",
758
+ icon: Checkmark.CheckmarkIcon
759
+ },
760
+ "decision.decline": {
761
+ tone: "caution",
762
+ icon: Close.CloseIcon
763
+ }
764
+ };
765
+
766
+ function actionButtonFace(args) {
767
+ const mode = concludesHere(args.actionEval, args.activityName) ? "default" : "ghost";
768
+ if (actionAdvanceStatus(args.actionEval.action, args.activityName) === "failed") return {
769
+ mode: mode,
770
+ tone: "critical",
771
+ icon: void 0
772
+ };
773
+ const [semantic] = args.actionEval.semantics ?? [], face = semantic === void 0 ? void 0 : SEMANTIC_FACE[semantic];
774
+ return {
775
+ mode: mode,
776
+ tone: face?.tone ?? "default",
777
+ icon: face?.icon
707
778
  };
708
779
  }
709
780
 
@@ -958,10 +1029,19 @@ function personActor(user) {
958
1029
  };
959
1030
  }
960
1031
 
1032
+ const memberIndexes = /* @__PURE__ */ new WeakMap;
1033
+
1034
+ function memberIndex(members) {
1035
+ const cached = memberIndexes.get(members);
1036
+ if (cached) return cached;
1037
+ const created = new Map(members.map(member => [ member.id, member ]));
1038
+ return memberIndexes.set(members, created), created;
1039
+ }
1040
+
961
1041
  const selfUserFor = (id, me) => me != null && me.id === id ? me : void 0;
962
1042
 
963
1043
  function userDisplayFor(args) {
964
- const member = args.members.find(m => m.id === args.id), self = selfUserFor(args.id, args.me);
1044
+ const member = memberIndex(args.members).get(args.id), self = selfUserFor(args.id, args.me);
965
1045
  return {
966
1046
  id: args.id,
967
1047
  name: member?.displayName ?? self?.name ?? args.id,
@@ -971,19 +1051,20 @@ function userDisplayFor(args) {
971
1051
 
972
1052
  function useUserDisplays(ids) {
973
1053
  const {members: members} = useProjectMembers(), me = sanity.useCurrentUser();
974
- return ids.map(id => userDisplayFor({
1054
+ return react.useMemo(() => ids.map(id => userDisplayFor({
975
1055
  id: id,
976
1056
  members: members,
977
1057
  me: me
978
- }));
1058
+ })), [ ids, me, members ]);
979
1059
  }
980
1060
 
981
1061
  function useUserDisplay(id) {
982
- const [display] = useUserDisplays([ id ]);
983
- return display ?? {
984
- name: id,
985
- imageUrl: void 0
986
- };
1062
+ const {members: members} = useProjectMembers(), me = sanity.useCurrentUser();
1063
+ return react.useMemo(() => userDisplayFor({
1064
+ id: id,
1065
+ members: members,
1066
+ me: me
1067
+ }), [ id, me, members ]);
987
1068
  }
988
1069
 
989
1070
  function useAssignmentIdentity() {
@@ -994,8 +1075,12 @@ function useAssignmentIdentity() {
994
1075
  };
995
1076
  }
996
1077
 
1078
+ const projectMembersByUsers = /* @__PURE__ */ new WeakMap;
1079
+
997
1080
  function projectMembersFrom(users) {
998
- return users.map(({membership: membership, profile: profile}) => ({
1081
+ const cached = projectMembersByUsers.get(users);
1082
+ if (cached) return cached;
1083
+ const members = users.map(({membership: membership, profile: profile}) => ({
999
1084
  id: membership.id,
1000
1085
  displayName: profile?.displayName ?? membership.id,
1001
1086
  ...profile?.email ? {
@@ -1006,6 +1091,7 @@ function projectMembersFrom(users) {
1006
1091
  } : {},
1007
1092
  roles: (membership.roles ?? []).map(role => role.name)
1008
1093
  }));
1094
+ return projectMembersByUsers.set(users, members), members;
1009
1095
  }
1010
1096
 
1011
1097
  function useProjectMembers() {
@@ -1447,7 +1533,7 @@ function DocPicker({value: value, onChange: onChange, types: types2, readOnly: r
1447
1533
  children: /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
1448
1534
  flex: 1,
1449
1535
  children: /* @__PURE__ */ jsxRuntime.jsx(PreviewOrId, {
1450
- id: workflowEngine.extractDocumentId(value.id),
1536
+ id: workflowEngine.toBareId(value.id),
1451
1537
  type: value.type
1452
1538
  })
1453
1539
  })
@@ -1543,7 +1629,7 @@ function DocRefsInput({value: value, onChange: onChange, types: types2}) {
1543
1629
  /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
1544
1630
  flex: 1,
1545
1631
  children: /* @__PURE__ */ jsxRuntime.jsx(PreviewOrId, {
1546
- id: workflowEngine.extractDocumentId(ref.id),
1632
+ id: workflowEngine.toBareId(ref.id),
1547
1633
  type: ref.type
1548
1634
  })
1549
1635
  }),
@@ -1697,6 +1783,15 @@ function useFireAction(args) {
1697
1783
  };
1698
1784
  }
1699
1785
 
1786
+ const ACTION_ICON_GAP = 2;
1787
+
1788
+ function actionIconProps(icon) {
1789
+ return icon ? {
1790
+ gap: ACTION_ICON_GAP,
1791
+ icon: icon
1792
+ } : {};
1793
+ }
1794
+
1700
1795
  function stopRowMouseDown(e) {
1701
1796
  e.preventDefault(), e.stopPropagation();
1702
1797
  }
@@ -1724,7 +1819,7 @@ function RowClickShield({active: active, children: children}) {
1724
1819
  });
1725
1820
  }
1726
1821
 
1727
- function FireButton({instanceId: instanceId, activity: activity, action: action, label: label, mode: mode = "default", chrome: chrome, tone: tone = "default"}) {
1822
+ function FireButton({instanceId: instanceId, activity: activity, action: action, label: label, mode: mode = "default", chrome: chrome, tone: tone = "default", icon: icon}) {
1728
1823
  const {fire: fire, pending: pending} = useFireAction({
1729
1824
  instanceId: instanceId,
1730
1825
  activity: activity,
@@ -1736,6 +1831,7 @@ function FireButton({instanceId: instanceId, activity: activity, action: action,
1736
1831
  /* @__PURE__ */
1737
1832
  return jsxRuntime.jsx(ui.Button, {
1738
1833
  fontSize: 1,
1834
+ ...actionIconProps(icon),
1739
1835
  loading: pending,
1740
1836
  mode: chrome?.mode ?? mode,
1741
1837
  onClick: handleClick,
@@ -1753,6 +1849,7 @@ function ActivityActionRow({actionEval: actionEval, instanceId: instanceId, acti
1753
1849
  const rowKind = actionRowKind(actionEval);
1754
1850
  return rowKind.kind === "disabled" ? /* @__PURE__ */ jsxRuntime.jsx(DisabledActionButton, {
1755
1851
  actionEval: actionEval,
1852
+ activity: activity,
1756
1853
  chrome: chrome
1757
1854
  }) : rowKind.kind === "plain" ? /* @__PURE__ */ jsxRuntime.jsx(PlainActionButton, {
1758
1855
  actionEval: actionEval,
@@ -1767,13 +1864,18 @@ function ActivityActionRow({actionEval: actionEval, instanceId: instanceId, acti
1767
1864
  });
1768
1865
  }
1769
1866
 
1770
- function DisabledActionButton({actionEval: actionEval, chrome: chrome}) {
1771
- const label = actionTriggerLabel(actionEval), button = /* @__PURE__ */ jsxRuntime.jsx(ui.Button, {
1867
+ function DisabledActionButton({actionEval: actionEval, activity: activity, chrome: chrome}) {
1868
+ const label = actionTriggerLabel(actionEval), {tone: tone, icon: icon} = actionButtonFace({
1869
+ actionEval: actionEval,
1870
+ activityName: activity
1871
+ }), button = /* @__PURE__ */ jsxRuntime.jsx(ui.Button, {
1772
1872
  fontSize: 1,
1873
+ ...actionIconProps(icon),
1773
1874
  mode: chrome?.mode ?? "ghost",
1774
1875
  onClick: e => e.stopPropagation(),
1775
1876
  padding: 2,
1776
1877
  text: label,
1878
+ tone: tone,
1777
1879
  ...spanTriggerProps(chrome),
1778
1880
  disabled: !0
1779
1881
  });
@@ -1784,11 +1886,12 @@ function DisabledActionButton({actionEval: actionEval, chrome: chrome}) {
1784
1886
  }
1785
1887
 
1786
1888
  function PlainActionButton({actionEval: actionEval, instanceId: instanceId, activity: activity, chrome: chrome}) {
1787
- const {action: action} = actionEval, label = actionLabel(action), btn = actionButtonTone({
1889
+ const {action: action} = actionEval, label = actionLabel(action), btn = actionButtonFace({
1788
1890
  actionEval: actionEval,
1789
1891
  activityName: activity
1790
1892
  }), button = /* @__PURE__ */ jsxRuntime.jsx(FireButton, {
1791
1893
  action: action.name,
1894
+ icon: btn.icon,
1792
1895
  instanceId: instanceId,
1793
1896
  label: label,
1794
1897
  mode: btn.mode,
@@ -1863,7 +1966,7 @@ function ParamsActionDialog({actionEval: actionEval, instanceId: instanceId, act
1863
1966
  }
1864
1967
 
1865
1968
  function ParamsActionButton({actionEval: actionEval, instanceId: instanceId, activity: activity, chrome: chrome}) {
1866
- const [open, setOpen] = react.useState(!1), btn = actionButtonTone({
1969
+ const [open, setOpen] = react.useState(!1), btn = actionButtonFace({
1867
1970
  actionEval: actionEval,
1868
1971
  activityName: activity
1869
1972
  });
@@ -1872,6 +1975,7 @@ function ParamsActionButton({actionEval: actionEval, instanceId: instanceId, act
1872
1975
  children: [
1873
1976
  /* @__PURE__ */ jsxRuntime.jsx(ui.Button, {
1874
1977
  fontSize: 1,
1978
+ ...actionIconProps(btn.icon),
1875
1979
  mode: chrome?.mode ?? btn.mode,
1876
1980
  onClick: e => {
1877
1981
  chrome?.inButtonCard === !0 && e.stopPropagation(), setOpen(!0);
@@ -2697,7 +2801,7 @@ function ReadOnlyDocRefs({entry: entry}) {
2697
2801
  padding: 2,
2698
2802
  radius: 2,
2699
2803
  children: /* @__PURE__ */ jsxRuntime.jsx(PreviewOrId, {
2700
- id: workflowEngine.extractDocumentId(ref.id),
2804
+ id: workflowEngine.toBareId(ref.id),
2701
2805
  type: ref.type
2702
2806
  })
2703
2807
  }, ref.id))
@@ -4061,7 +4165,12 @@ const ACTIONS_MENU_TRIGGER = {
4061
4165
  };
4062
4166
 
4063
4167
  function ActionMenuItem({actionEval: actionEval, instanceId: instanceId, activity: activity, onCollectParams: onCollectParams}) {
4064
- const rowKind = actionRowKind(actionEval), label = actionTriggerLabel(actionEval), {fire: fire, pending: pending} = useFireAction({
4168
+ const rowKind = actionRowKind(actionEval), label = actionTriggerLabel(actionEval), {tone: tone, icon: icon} = actionButtonFace({
4169
+ actionEval: actionEval,
4170
+ activityName: activity
4171
+ }), iconProp = icon ? {
4172
+ icon: icon
4173
+ } : {}, {fire: fire, pending: pending} = useFireAction({
4065
4174
  instanceId: instanceId,
4066
4175
  activity: activity,
4067
4176
  action: actionEval.action.name,
@@ -4069,17 +4178,23 @@ function ActionMenuItem({actionEval: actionEval, instanceId: instanceId, activit
4069
4178
  });
4070
4179
  return rowKind.kind === "disabled" ? /* @__PURE__ */ jsxRuntime.jsx(ui.MenuItem, {
4071
4180
  disabled: !0,
4072
- text: label
4181
+ ...iconProp,
4182
+ text: label,
4183
+ tone: tone
4073
4184
  }) : rowKind.kind === "plain" ? /* @__PURE__ */ jsxRuntime.jsx(ui.MenuItem, {
4074
4185
  disabled: pending,
4186
+ ...iconProp,
4075
4187
  onClick: () => {
4076
4188
  fire();
4077
4189
  },
4078
- text: label
4190
+ text: label,
4191
+ tone: tone
4079
4192
  }) : /* @__PURE__ */ jsxRuntime.jsx(ui.MenuItem, {
4080
4193
  disabled: pending,
4194
+ ...iconProp,
4081
4195
  onClick: onCollectParams,
4082
- text: label
4196
+ text: label,
4197
+ tone: tone
4083
4198
  });
4084
4199
  }
4085
4200
 
@@ -4192,6 +4307,7 @@ function FittedActions({actions: actions, activity: activity, instanceId: instan
4192
4307
  children: [
4193
4308
  /* @__PURE__ */ jsxRuntime.jsx(MeasureRow, {
4194
4309
  actions: actions,
4310
+ activity: activity,
4195
4311
  onElement: setMeasureEl
4196
4312
  }),
4197
4313
  /* @__PURE__ */ jsxRuntime.jsxs(FooterRow, {
@@ -4211,7 +4327,7 @@ function FittedActions({actions: actions, activity: activity, instanceId: instan
4211
4327
  });
4212
4328
  }
4213
4329
 
4214
- function MeasureRow({actions: actions, onElement: onElement}) {
4330
+ function MeasureRow({actions: actions, activity: activity, onElement: onElement}) {
4215
4331
  /* @__PURE__ */
4216
4332
  return jsxRuntime.jsx("div", {
4217
4333
  "aria-hidden": !0,
@@ -4225,12 +4341,20 @@ function MeasureRow({actions: actions, onElement: onElement}) {
4225
4341
  display: "inline-flex",
4226
4342
  whiteSpace: "nowrap"
4227
4343
  },
4228
- children: [ actions.map(a => /* @__PURE__ */ jsxRuntime.jsx(ui.Button, {
4229
- as: "span",
4230
- fontSize: 1,
4231
- padding: 2,
4232
- text: actionTriggerLabel(a)
4233
- }, a.action.name)),
4344
+ children: [ actions.map(a => {
4345
+ const {icon: icon} = actionButtonFace({
4346
+ actionEval: a,
4347
+ activityName: activity
4348
+ });
4349
+ /* @__PURE__ */
4350
+ return jsxRuntime.jsx(ui.Button, {
4351
+ as: "span",
4352
+ fontSize: 1,
4353
+ ...actionIconProps(icon),
4354
+ padding: 2,
4355
+ text: actionTriggerLabel(a)
4356
+ }, a.action.name);
4357
+ }),
4234
4358
  /* @__PURE__ */ jsxRuntime.jsx(ui.Button, {
4235
4359
  ...ACTIONS_MENU_TRIGGER,
4236
4360
  as: "span",
@@ -4512,7 +4636,26 @@ function CountedLabel({count: count, label: label}) {
4512
4636
  });
4513
4637
  }
4514
4638
 
4515
- function GroupHeading({count: count, title: title, end: end}) {
4639
+ function SpinnerSlot({busy: busy, size: size}) {
4640
+ /* @__PURE__ */
4641
+ return jsxRuntime.jsx(ui.Flex, {
4642
+ align: "center",
4643
+ flex: "none",
4644
+ justify: "center",
4645
+ style: {
4646
+ width: 19,
4647
+ height: 19
4648
+ },
4649
+ children: busy ? /* @__PURE__ */ jsxRuntime.jsx(ui.Spinner, {
4650
+ muted: !0,
4651
+ ...size !== void 0 ? {
4652
+ size: size
4653
+ } : {}
4654
+ }) : null
4655
+ });
4656
+ }
4657
+
4658
+ function GroupHeading({busy: busy, count: count, title: title, end: end}) {
4516
4659
  /* @__PURE__ */
4517
4660
  return jsxRuntime.jsxs(ui.Flex, {
4518
4661
  align: "center",
@@ -4527,6 +4670,9 @@ function GroupHeading({count: count, title: title, end: end}) {
4527
4670
  weight: "semibold",
4528
4671
  children: title
4529
4672
  })
4673
+ }), busy === void 0 ? null : /* @__PURE__ */ jsxRuntime.jsx(SpinnerSlot, {
4674
+ busy: busy,
4675
+ size: 1
4530
4676
  }), end === void 0 ? null : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, {
4531
4677
  children: [
4532
4678
  /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, {
@@ -4649,6 +4795,18 @@ function TabSwitch({ariaControls: ariaControls, idPrefix: idPrefix, options: opt
4649
4795
  });
4650
4796
  }
4651
4797
 
4798
+ function useDelayedFlag(active, delayMs) {
4799
+ const [held, setHeld] = react.useState(!1);
4800
+ return react.useEffect(() => {
4801
+ if (!active) {
4802
+ setHeld(!1);
4803
+ return;
4804
+ }
4805
+ const timer = setTimeout(() => setHeld(!0), delayMs);
4806
+ return () => clearTimeout(timer);
4807
+ }, [ active, delayMs ]), held && active;
4808
+ }
4809
+
4652
4810
  function instanceTitle(instance, definition) {
4653
4811
  return definition?.title ?? instance.definition;
4654
4812
  }
@@ -5487,9 +5645,16 @@ function instanceNotice(entry) {
5487
5645
  size: 1,
5488
5646
  children: "This workflow didn’t finish starting — use Start workflow to continue it"
5489
5647
  })
5490
- }) : pending ? /* @__PURE__ */ jsxRuntime.jsx(LoadingRow, {
5491
- label: "Loading…",
5492
- padding: 4
5648
+ }) : pending ? /* @__PURE__ */ jsxRuntime.jsx(WorkRow, {
5649
+ lead: /* @__PURE__ */ jsxRuntime.jsx(ui.Spinner, {
5650
+ muted: !0,
5651
+ size: 1
5652
+ }),
5653
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
5654
+ muted: !0,
5655
+ size: 1,
5656
+ children: "Loading…"
5657
+ })
5493
5658
  }) : null;
5494
5659
  }
5495
5660
 
@@ -6553,20 +6718,7 @@ function InstanceDebugMenu({entry: entry}) {
6553
6718
  }
6554
6719
 
6555
6720
  function InstanceAccordion({entry: entry, children: children, defaultOpen: defaultOpen}) {
6556
- const definition = useDefinition(entry), {instance: instance, ready: ready} = entry, settled = !isLiveEntry(entry), title = instanceTitle(instance, definition), badgeTrim = useBadgeCapTrim(), status = /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
6557
- align: "center",
6558
- gap: 2,
6559
- style: badgeTrim,
6560
- children: [
6561
- /* @__PURE__ */ jsxRuntime.jsx(AbortedBadge, {
6562
- instance: instance
6563
- }), settled ? null : /* @__PURE__ */ jsxRuntime.jsx(OlderDefinitionBadge, {
6564
- instance: instance
6565
- }), !ready && entry.invalid === void 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Spinner, {
6566
- muted: !0,
6567
- size: 1
6568
- }) : null ]
6569
- });
6721
+ const definition = useDefinition(entry), {instance: instance} = entry, settled = !isLiveEntry(entry), title = instanceTitle(instance, definition), badgeTrim = useBadgeCapTrim();
6570
6722
  /* @__PURE__ */
6571
6723
  return jsxRuntime.jsx(CollapsibleBand, {
6572
6724
  defaultOpen: defaultOpen ?? !settled,
@@ -6589,7 +6741,18 @@ function InstanceAccordion({entry: entry, children: children, defaultOpen: defau
6589
6741
  textOverflow: "ellipsis",
6590
6742
  weight: "medium",
6591
6743
  children: title
6592
- }), status ]
6744
+ }),
6745
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
6746
+ align: "center",
6747
+ gap: 2,
6748
+ style: badgeTrim,
6749
+ children: [
6750
+ /* @__PURE__ */ jsxRuntime.jsx(AbortedBadge, {
6751
+ instance: instance
6752
+ }), settled ? null : /* @__PURE__ */ jsxRuntime.jsx(OlderDefinitionBadge, {
6753
+ instance: instance
6754
+ }) ]
6755
+ }) ]
6593
6756
  }),
6594
6757
  /* @__PURE__ */ jsxRuntime.jsx(TrailingHairline, {}),
6595
6758
  /* @__PURE__ */ jsxRuntime.jsx(InstanceDebugMenu, {
@@ -6607,7 +6770,7 @@ function InstanceAccordion({entry: entry, children: children, defaultOpen: defau
6607
6770
  }
6608
6771
 
6609
6772
  function PartOfBand({instance: instance}) {
6610
- const parentRef = instance.ancestors.at(-1), parentId = parentRef ? workflowEngine.extractDocumentId(parentRef.id) : null, parentEntry = useWorkflowInstanceEntry(parentId), parentDefinition = useDefinition(parentEntry);
6773
+ const parentRef = instance.ancestors.at(-1), parentId = parentRef ? workflowEngine.toBareId(parentRef.id) : null, parentEntry = useWorkflowInstanceEntry(parentId), parentDefinition = useDefinition(parentEntry);
6611
6774
  if (!parentId) return null;
6612
6775
  const title = parentEntry ? instanceTitle(parentEntry.instance, parentDefinition) : parentId;
6613
6776
  /* @__PURE__ */
@@ -7164,11 +7327,9 @@ const VIEW_TAB_CODEC = workflowsTabCodec([ "overview", "for-me" ]), WorkflowsVie
7164
7327
  };
7165
7328
 
7166
7329
  function DiscoverySpinner() {
7167
- const [visible, setVisible] = react.useState(!1);
7168
- return react.useEffect(() => {
7169
- const timer = setTimeout(() => setVisible(!0), 1e3);
7170
- return () => clearTimeout(timer);
7171
- }, []), /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, {
7330
+ const visible = useDelayedFlag(!0, 1e3);
7331
+ /* @__PURE__ */
7332
+ return jsxRuntime.jsx(ui.Flex, {
7172
7333
  align: "center",
7173
7334
  height: "fill",
7174
7335
  justify: "center",
@@ -7178,7 +7339,7 @@ function DiscoverySpinner() {
7178
7339
  });
7179
7340
  }
7180
7341
 
7181
- const GROUP_GAP = 1;
7342
+ const BOOT_CUE_DELAY_MS = 400, GROUP_GAP = 1;
7182
7343
 
7183
7344
  function useWorkflowsTab() {
7184
7345
  const {params: params, setParams: setParams} = structure.usePaneRouter();
@@ -7203,7 +7364,7 @@ function useOpenActivity(entries) {
7203
7364
  }
7204
7365
 
7205
7366
  function WorkflowsPanel({entries: entries, documentId: documentId, docTypeMappings: docTypeMappings, initialValue: initialValue}) {
7206
- const identity = useAssignmentIdentity(), panelId = react.useId(), {params: params} = structure.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] = react.useState(() => focusedLanding(params?.[WORKFLOWS_FOCUS_PARAM], entries.map(e => e.instance._id))), assignedWorkUnknown = active.some(e => e.invalid !== void 0), sectionFor = e => /* @__PURE__ */ jsxRuntime.jsx(WorkflowInstanceSection, {
7367
+ const identity = useAssignmentIdentity(), panelId = react.useId(), {params: params} = structure.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] = react.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__ */ jsxRuntime.jsx(WorkflowInstanceSection, {
7207
7368
  defaultOpen: landingDefaultOpen(landing, e.instance._id),
7208
7369
  entry: e,
7209
7370
  onOpenActivity: activityName => setOpenActivity({
@@ -7224,6 +7385,7 @@ function WorkflowsPanel({entries: entries, documentId: documentId, docTypeMappin
7224
7385
  padding: 3,
7225
7386
  children: [
7226
7387
  /* @__PURE__ */ jsxRuntime.jsx(GroupHeading, {
7388
+ busy: booting,
7227
7389
  count: active.length,
7228
7390
  end:
7229
7391
  /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
@@ -7359,10 +7521,8 @@ function workflowsView(S) {
7359
7521
  return S.view.component(WorkflowsView).id(WORKFLOWS_VIEW_ID).title("Workflows");
7360
7522
  }
7361
7523
 
7362
- function workflowDefaultDocumentNode(options) {
7363
- return (S, ctx) => {
7364
- if (mappingForDocType(options.mappings, ctx.schemaType)) return S.document().views([ S.view.form(), workflowsView(S) ]);
7365
- };
7524
+ function workflowDefaultDocumentNode() {
7525
+ return S => S.document().views([ S.view.form(), workflowsView(S) ]);
7366
7526
  }
7367
7527
 
7368
7528
  function lockTitleFor(args) {
@@ -7375,26 +7535,12 @@ function lockTitleFor(args) {
7375
7535
  return workflows.length === 1 ? `The ${workflowList} workflow is holding this document.` : `The ${workflowList} workflows are holding this document.`;
7376
7536
  }
7377
7537
 
7378
- const EMPTY = {
7379
- byType: /* @__PURE__ */ new Map,
7380
- definitions: /* @__PURE__ */ new Map,
7381
- warnings: []
7382
- };
7383
-
7384
7538
  function resolveAutoStart(args) {
7385
- const {autoStart: autoStart, context: context, knownDocTypes: knownDocTypes, definitions: definitions} = args;
7386
- if (autoStart === void 0) return EMPTY;
7387
- const evaluated = evaluateMap(autoStart, context);
7388
- if ("warning" in evaluated) return {
7389
- byType: /* @__PURE__ */ new Map,
7390
- definitions: /* @__PURE__ */ new Map,
7391
- warnings: [ evaluated.warning ]
7392
- };
7393
- const map = evaluated.map, latestByName = new Map(workflowEngine.latestDeployedDefinitions(definitions).map(d => [ d.name, d ])), byType = /* @__PURE__ */ new Map, resolvedDefinitions = /* @__PURE__ */ new Map, warnings = [];
7394
- for (const [docType, spec] of Object.entries(map)) {
7539
+ const {autoStart: autoStart, knownDocTypes: knownDocTypes, definitions: definitions} = args, latestByName = new Map(workflowEngine.latestDeployedDefinitions(definitions).map(d => [ d.name, d ])), byType = /* @__PURE__ */ new Map, resolvedDefinitions = /* @__PURE__ */ new Map, warnings = [];
7540
+ for (const [docType, names] of Object.entries(autoStart)) {
7395
7541
  const entry = resolveEntry({
7396
7542
  docType: docType,
7397
- spec: spec,
7543
+ names: names,
7398
7544
  knownDocTypes: knownDocTypes,
7399
7545
  latestByName: latestByName
7400
7546
  });
@@ -7410,35 +7556,15 @@ function resolveAutoStart(args) {
7410
7556
  };
7411
7557
  }
7412
7558
 
7413
- function evaluateMap(autoStart, context) {
7414
- let map;
7415
- try {
7416
- map = typeof autoStart == "function" ? autoStart(context) : autoStart;
7417
- } catch (err) {
7418
- return {
7419
- warning: `autoStart: config function threw — ignoring it (${describeError(err)}).`
7420
- };
7421
- }
7422
- return typeof map != "object" || map === null ? {
7423
- warning: "autoStart: config did not resolve to an object — ignoring it."
7424
- } : {
7425
- map: map
7426
- };
7427
- }
7428
-
7429
- function specNames(spec) {
7430
- return typeof spec == "string" ? [ spec ] : Array.isArray(spec) ? spec : [];
7431
- }
7432
-
7433
7559
  function resolveEntry(args) {
7434
- const {docType: docType, spec: spec, knownDocTypes: knownDocTypes, latestByName: latestByName} = args;
7560
+ const {docType: docType, names: names, knownDocTypes: knownDocTypes, latestByName: latestByName} = args;
7435
7561
  if (!knownDocTypes.has(docType)) return {
7436
7562
  valid: [],
7437
7563
  definitions: [],
7438
7564
  warnings: [ `autoStart: no schema type "${docType}" — entry skipped.` ]
7439
7565
  };
7440
7566
  const valid = [], definitions = [], warnings = [];
7441
- for (const name of specNames(spec)) {
7567
+ for (const name of names) {
7442
7568
  const definition = latestByName.get(name), issue = workflowIssue({
7443
7569
  name: name,
7444
7570
  docType: docType,
@@ -7464,11 +7590,9 @@ function workflowIssue(args) {
7464
7590
  const {name: name, docType: docType, definition: definition} = args;
7465
7591
  if (definition === void 0) return `autoStart: "${docType}" → workflow "${name}" is not deployed — skipped.`;
7466
7592
  if (!workflowEngine.isStartableDefinition(definition)) return `autoStart: "${docType}" → workflow "${name}" is spawn-only, not startable — skipped.`;
7467
- if (!workflowEngine.acceptsDocumentType({
7468
- fields: [ ...definition.fields ?? [] ]
7469
- }, docType)) return `autoStart: "${docType}" → workflow "${name}" has no subject accepting "${docType}" — skipped.`;
7470
- const subject = (definition.fields ?? []).find(workflowEngine.isSubjectEntry);
7471
- if (subject !== void 0 && !workflowEngine.isInputSourced(subject)) return `autoStart: "${docType}" → workflow "${name}" has a self-filling subject (not caller-provided), so the document can't be its subject — skipped.`;
7593
+ const subject = documentSubjectEntry(definition), acceptsType = subject?.types === void 0 || subject.types.includes(docType);
7594
+ if (subject === void 0 || !acceptsType) return `autoStart: "${docType}" workflow "${name}" has no document subject accepting "${docType}" — skipped.`;
7595
+ if (!workflowEngine.isInputSourced(subject)) return `autoStart: "${docType}" → workflow "${name}" has a self-filling subject (not caller-provided), so the document can't be its subject — skipped.`;
7472
7596
  }
7473
7597
 
7474
7598
  function contentDocumentTypes(schema) {
@@ -7488,20 +7612,31 @@ const EMPTY_SNAPSHOT = {
7488
7612
  };
7489
7613
 
7490
7614
  function useDiscoveryState(args) {
7491
- const {registered: registered, requested: requested, contentResource: contentResource} = args, watching = registered.length + requested.length > 0, [latest, setLatest] = react.useState(EMPTY_SNAPSHOT), [resolved, setResolved] = react.useState(EMPTY_SNAPSHOT), onSnapshot = react.useCallback(snapshot => {
7492
- setLatest(snapshot), snapshot.instances !== void 0 && setResolved(snapshot);
7493
- }, []);
7494
- react.useEffect(() => {
7495
- watching || (setLatest(EMPTY_SNAPSHOT), setResolved(EMPTY_SNAPSHOT));
7496
- }, [ watching ]);
7497
- const filter = react.useMemo(() => ({
7498
- documents: registered.map(id => workflowEngine.gdrFromResource(contentResource, id)),
7615
+ const {observed: observed, requested: requested, contentResource: contentResource} = args, documentWatching = observed.length > 0, requestedWatching = requested.length > 0, documents = useDiscoveryStream(documentWatching), explicit = useDiscoveryStream(requestedWatching), documentFilter = react.useMemo(() => documentWatching ? {
7616
+ documents: observed.map(id => workflowEngine.gdrFromResource(contentResource, id)),
7617
+ includeCompleted: !0
7618
+ } : void 0, [ documentWatching, observed, contentResource ]), requestedFilter = react.useMemo(() => requestedWatching ? {
7499
7619
  ids: requested,
7500
7620
  includeCompleted: !0
7501
- }), [ registered, requested, contentResource ]);
7621
+ } : void 0, [ requestedWatching, requested ]), resolved = react.useMemo(() => mergeResolved(documents.resolved, explicit.resolved), [ documents.resolved, explicit.resolved ]);
7502
7622
  return {
7503
- watching: watching,
7504
- filter: filter,
7623
+ documentFilter: documentFilter,
7624
+ requestedFilter: requestedFilter,
7625
+ loading: documents.loading || explicit.loading,
7626
+ invalid: documents.invalid ?? explicit.invalid,
7627
+ resolved: resolved,
7628
+ onDocumentSnapshot: documents.onSnapshot,
7629
+ onRequestedSnapshot: explicit.onSnapshot
7630
+ };
7631
+ }
7632
+
7633
+ function useDiscoveryStream(watching) {
7634
+ const [latest, setLatest] = react.useState(EMPTY_SNAPSHOT), [resolved, setResolved] = react.useState(EMPTY_SNAPSHOT), onSnapshot = react.useCallback(snapshot => {
7635
+ setLatest(snapshot), snapshot.instances !== void 0 && setResolved(snapshot);
7636
+ }, []);
7637
+ return react.useEffect(() => {
7638
+ watching || (setLatest(EMPTY_SNAPSHOT), setResolved(EMPTY_SNAPSHOT));
7639
+ }, [ watching ]), {
7505
7640
  loading: watching && latest.instances === void 0 && latest.invalid === void 0,
7506
7641
  invalid: watching ? latest.invalid : void 0,
7507
7642
  resolved: resolved,
@@ -7509,18 +7644,62 @@ function useDiscoveryState(args) {
7509
7644
  };
7510
7645
  }
7511
7646
 
7512
- function indexDiscoveredInstances(args) {
7513
- const {instances: instances, registered: registered, contentResource: contentResource} = args, ordered = [ ...instances ].sort((a, b) => b.startedAt.localeCompare(a.startedAt)), index = /* @__PURE__ */ new Map;
7514
- for (const bareId of registered) {
7515
- const uri = workflowEngine.gdrFromResource(contentResource, bareId), watching = ordered.filter(inst => workflowEngine.instanceWatchesDocument(inst, uri));
7516
- watching.length > 0 && index.set(bareId, watching.map(inst => inst._id));
7647
+ function mergeResolved(documents, explicit) {
7648
+ const rows = [ ...documents.instances ?? [], ...explicit.instances ?? [] ], freshest = /* @__PURE__ */ new Map;
7649
+ for (const instance of rows) {
7650
+ const current = freshest.get(instance._id);
7651
+ (current === void 0 || Date.parse(instance.lastChangedAt) > Date.parse(current.lastChangedAt)) && freshest.set(instance._id, instance);
7517
7652
  }
7653
+ return {
7654
+ instances: documents.instances === void 0 && explicit.instances === void 0 ? void 0 : [ ...freshest.values() ],
7655
+ invalid: documents.invalid ?? explicit.invalid,
7656
+ registered: documents.registered
7657
+ };
7658
+ }
7659
+
7660
+ function createDiscoveryIndexer(deriveWatchSet = workflowEngine.subscriptionDocumentsForInstance) {
7661
+ const cached = /* @__PURE__ */ new Map;
7662
+ return {
7663
+ index(args) {
7664
+ const liveIds = new Set(args.instances.map(instance => instance._id));
7665
+ for (const id of cached.keys()) liveIds.has(id) || cached.delete(id);
7666
+ const watchedById = /* @__PURE__ */ new Map;
7667
+ for (const instance of args.instances) {
7668
+ let row = cached.get(instance._id);
7669
+ row?.source !== instance && (row = {
7670
+ source: instance,
7671
+ globalDocumentIds: [ ...new Set(deriveWatchSet(instance).documents.map(document => document.globalDocumentId)) ]
7672
+ }, cached.set(instance._id, row)), watchedById.set(instance._id, row.globalDocumentIds);
7673
+ }
7674
+ return buildDiscoveryIndex(args, watchedById);
7675
+ }
7676
+ };
7677
+ }
7678
+
7679
+ function buildDiscoveryIndex(args, watchedById) {
7680
+ 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 => [ workflowEngine.gdrFromResource(contentResource, bareId), bareId ]));
7681
+ for (const instance of ordered) appendInstanceWatches({
7682
+ index: index,
7683
+ registeredByUri: registeredByUri,
7684
+ instanceId: instance._id,
7685
+ watched: watchedById.get(instance._id) ?? []
7686
+ });
7518
7687
  return {
7519
7688
  instances: new Map(ordered.map(inst => [ inst._id, inst ])),
7520
7689
  index: index
7521
7690
  };
7522
7691
  }
7523
7692
 
7693
+ function appendInstanceWatches(args) {
7694
+ const {index: index, registeredByUri: registeredByUri, instanceId: instanceId, watched: watched} = args;
7695
+ for (const uri of watched) {
7696
+ const bareId = registeredByUri.get(uri);
7697
+ if (bareId === void 0) continue;
7698
+ const ids = index.get(bareId);
7699
+ ids === void 0 ? index.set(bareId, [ instanceId ]) : ids.push(instanceId);
7700
+ }
7701
+ }
7702
+
7524
7703
  function sameInstance(a, b) {
7525
7704
  return a === b || a._id === b._id && a._rev === b._rev;
7526
7705
  }
@@ -7544,7 +7723,7 @@ function entryOf(inst, report) {
7544
7723
  instance: report?.evaluation?.instance ?? inst,
7545
7724
  committed: inst,
7546
7725
  evaluation: report?.evaluation,
7547
- ready: report?.ready ?? !1,
7726
+ ready: report?.ready ?? workflowEngine.terminalState(inst) !== "in-flight",
7548
7727
  invalid: report?.invalid,
7549
7728
  guards: report?.guards
7550
7729
  };
@@ -7637,7 +7816,19 @@ function createEntriesStore() {
7637
7816
  };
7638
7817
  }
7639
7818
 
7640
- const notReady = (..._args) => Promise.reject(new Error("workflow session not ready yet"));
7819
+ function sessionPlan(args) {
7820
+ const demandedIds = new Set(args.demanded), evaluated = [ ...args.discovered.values() ].filter(inst => demandedIds.has(inst._id));
7821
+ return {
7822
+ evaluated: evaluated,
7823
+ guardScope: evaluated.map(inst => inst._id).sort()
7824
+ };
7825
+ }
7826
+
7827
+ function useDefaultLayout(props) {
7828
+ return react.useMemo(() => props.renderDefault(props), [ props ]);
7829
+ }
7830
+
7831
+ const notReady = (..._args) => Promise.reject(new Error("workflow session not ready yet")), NO_REGISTERED_DOCUMENTS = [], DEMAND_RELEASE_GRACE_MS = 500;
7641
7832
 
7642
7833
  function workspaceParts(config) {
7643
7834
  if (!config.projectId || !config.dataset) throw new Error("workflow plugin requires a workspace with projectId and dataset");
@@ -7665,10 +7856,11 @@ class InstanceEvaluatorBoundary extends react.Component {
7665
7856
  }
7666
7857
 
7667
7858
  function InstanceEvaluator(props) {
7668
- 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} = workflowStudio.useWorkflowSession({
7859
+ 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} = workflowStudio.useWorkflowSession({
7669
7860
  engine: engine,
7670
7861
  instanceId: instanceId,
7671
- grantsFromPath: grantsFromPath
7862
+ grantsFromPath: grantsFromPath,
7863
+ guardScope: guardScope
7672
7864
  });
7673
7865
  return react.useEffect(() => {
7674
7866
  onReport(instanceId, {
@@ -7712,9 +7904,9 @@ function engineOptionsFrom(config) {
7712
7904
  }
7713
7905
 
7714
7906
  function WorkflowProvider(props) {
7715
- const {config: config} = props, studioClient = sanity.useClient({
7907
+ const {config: config} = props, defaultLayout = useDefaultLayout(props), studioClient = sanity.useClient({
7716
7908
  apiVersion: WORKFLOW_API_VERSION
7717
- }), schema = sanity.useSchema(), {name: workspaceName} = sanity.useWorkspace(), {projectId: projectId, dataset: contentDataset} = workspaceParts(studioClient.config()), workflowDataset = config.workflowDataset ?? contentDataset, binding = react.useMemo(() => ({
7909
+ }), schema = sanity.useSchema(), {projectId: projectId, dataset: contentDataset} = workspaceParts(studioClient.config()), workflowDataset = config.workflowDataset ?? contentDataset, binding = react.useMemo(() => ({
7718
7910
  engineResource: {
7719
7911
  type: "dataset",
7720
7912
  id: `${projectId}.${workflowDataset}`
@@ -7732,7 +7924,15 @@ function WorkflowProvider(props) {
7732
7924
  }), grantsFromPath = workflowEngine.aclPathForResource({
7733
7925
  type: "dataset",
7734
7926
  id: `${projectId}.${workflowDataset}`
7735
- }), {ids: registered, acquire: register, release: unregister} = workflowReact.useRefcountedIds(), {ids: requested, acquire: requestInstance, release: releaseInstance} = workflowReact.useRefcountedIds(), [seeded, setSeeded] = react.useState(/* @__PURE__ */ new Map), seedInstance = react.useCallback(instance => {
7927
+ }), {ids: registered, acquire: registerActive, release: unregisterActive} = workflowReact.useRefcountedIds(), {ids: observed, acquire: retainRegistration, release: releaseRegistration} = workflowReact.useRefcountedIds({
7928
+ releaseDelayMs: DEMAND_RELEASE_GRACE_MS
7929
+ }), register = react.useCallback(id => {
7930
+ registerActive(id), retainRegistration(id);
7931
+ }, [ registerActive, retainRegistration ]), unregister = react.useCallback(id => {
7932
+ unregisterActive(id), releaseRegistration(id);
7933
+ }, [ unregisterActive, releaseRegistration ]), {ids: requested, acquire: requestInstance, release: releaseInstance} = workflowReact.useRefcountedIds(), {ids: evaluationDemand, acquire: requestEvaluation, release: releaseEvaluation} = workflowReact.useRefcountedIds({
7934
+ releaseDelayMs: DEMAND_RELEASE_GRACE_MS
7935
+ }), [seeded, setSeeded] = react.useState(/* @__PURE__ */ new Map), seedInstance = react.useCallback(instance => {
7736
7936
  setSeeded(prev => new Map(prev).set(instance._id, instance));
7737
7937
  }, []), [startRequest2, setStartRequest] = react.useState(void 0), openStartDialog = react.useCallback(r => setStartRequest(r), []), closeStartDialog = react.useCallback(() => setStartRequest(void 0), []), [entriesStore] = react.useState(createEntriesStore);
7738
7938
  react.useEffect(() => () => entriesStore.dispose(), [ entriesStore ]);
@@ -7783,15 +7983,18 @@ function WorkflowProvider(props) {
7783
7983
  });
7784
7984
  }, [ engine ]);
7785
7985
  drainRef.current = drainEffectsFor;
7786
- const {watching: watching, filter: filter, loading: loading, invalid: discoveryInvalid, resolved: resolved, onSnapshot: onSnapshot} = useDiscoveryState({
7787
- registered: registered,
7986
+ const {documentFilter: documentFilter, requestedFilter: requestedFilter, loading: loading, invalid: discoveryInvalid, resolved: resolved, onDocumentSnapshot: onDocumentSnapshot, onRequestedSnapshot: onRequestedSnapshot} = useDiscoveryState({
7987
+ observed: observed,
7788
7988
  requested: requested,
7789
7989
  contentResource: binding.contentResource
7790
- }), {instances: discovered, index: docIndex} = react.useMemo(() => indexDiscoveredInstances({
7990
+ }), discoveryIndexer = react.useMemo(createDiscoveryIndexer, []), {instances: discovered, index: docIndex} = react.useMemo(() => discoveryIndexer.index({
7791
7991
  instances: resolved.instances ?? [],
7792
7992
  registered: resolved.registered,
7793
7993
  contentResource: binding.contentResource
7794
- }), [ resolved, binding.contentResource ]), resolvedDocs = react.useMemo(() => new Set(resolved.registered), [ resolved.registered ]);
7994
+ }), [ discoveryIndexer, resolved, binding.contentResource ]), {evaluated: evaluated, guardScope: guardScope} = react.useMemo(() => sessionPlan({
7995
+ discovered: discovered,
7996
+ demanded: evaluationDemand
7997
+ }), [ discovered, evaluationDemand ]), resolvedDocs = react.useMemo(() => new Set(resolved.registered), [ resolved.registered ]);
7795
7998
  react.useEffect(() => {
7796
7999
  setSeeded(prev => {
7797
8000
  const kept = [ ...prev ].filter(([id]) => !discovered.has(id));
@@ -7804,15 +8007,13 @@ function WorkflowProvider(props) {
7804
8007
  docIndex: docIndex
7805
8008
  });
7806
8009
  }, [ entriesStore, seeded, discovered, docIndex ]);
7807
- const isDocResolved = react.useCallback(docId => resolvedDocs.has(docId), [ resolvedDocs ]), schemaContentTypes = react.useMemo(() => new Set(contentDocumentTypes(schema)), [ schema ]), {issues: issues, starts: starts, fields: fields, latestVersions: latestVersions, autoStartByType: autoStartByType, autoStartDefinitions: autoStartDefinitions} = useDeployedDefinitions({
8010
+ const isDocResolved = react.useCallback(docId => resolvedDocs.has(docId), [ resolvedDocs ]), schemaContentTypes = react.useMemo(() => new Set(contentDocumentTypes(schema)), [ schema ]), {mappings: mappings, issues: issues, starts: starts, fields: fields, latestVersions: latestVersions, autoStartByType: autoStartByType, autoStartDefinitions: autoStartDefinitions} = useDeployedDefinitions({
7808
8011
  engine: engine,
7809
- mappings: config.mappings,
8012
+ overrides: config.mappings,
7810
8013
  contentResource: binding.contentResource,
7811
8014
  schemaContentTypes: schemaContentTypes,
7812
8015
  startRequest: startRequest2,
7813
- autoStart: config.autoStart,
7814
- schema: schema,
7815
- workspaceName: workspaceName
8016
+ schema: schema
7816
8017
  }), value = react.useMemo(() => ({
7817
8018
  register: register,
7818
8019
  unregister: unregister,
@@ -7820,6 +8021,8 @@ function WorkflowProvider(props) {
7820
8021
  entries: entriesStore,
7821
8022
  requestInstance: requestInstance,
7822
8023
  releaseInstance: releaseInstance,
8024
+ requestEvaluation: requestEvaluation,
8025
+ releaseEvaluation: releaseEvaluation,
7823
8026
  seedInstance: seedInstance,
7824
8027
  openStartDialog: openStartDialog,
7825
8028
  closeStartDialog: closeStartDialog,
@@ -7834,7 +8037,7 @@ function WorkflowProvider(props) {
7834
8037
  completeEffectFor: completeEffectFor,
7835
8038
  engine: engine,
7836
8039
  binding: binding,
7837
- mappings: config.mappings,
8040
+ mappings: mappings,
7838
8041
  mappingIssues: issues,
7839
8042
  mappingStarts: starts,
7840
8043
  mappingFields: fields,
@@ -7842,24 +8045,30 @@ function WorkflowProvider(props) {
7842
8045
  effectHandlers: engine.effectHandlers,
7843
8046
  autoStartByType: autoStartByType,
7844
8047
  autoStartDefinitions: autoStartDefinitions
7845
- }), [ 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 ]);
8048
+ }), [ 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 ]);
7846
8049
  /* @__PURE__ */
7847
8050
  return jsxRuntime.jsxs(WorkflowContext.Provider, {
7848
8051
  value: value,
7849
- children: [ watching ? /* @__PURE__ */ jsxRuntime.jsx(DiscoverySubscription, {
8052
+ children: [ documentFilter ? /* @__PURE__ */ jsxRuntime.jsx(DiscoverySubscription, {
7850
8053
  engine: engine,
7851
- filter: filter,
7852
- onSnapshot: onSnapshot,
8054
+ filter: documentFilter,
8055
+ onSnapshot: onDocumentSnapshot,
7853
8056
  registered: registered
7854
- }) : null, [ ...discovered.values() ].map(inst => /* @__PURE__ */ jsxRuntime.jsx(InstanceEvaluatorBoundary, {
8057
+ }) : null, requestedFilter ? /* @__PURE__ */ jsxRuntime.jsx(DiscoverySubscription, {
8058
+ engine: engine,
8059
+ filter: requestedFilter,
8060
+ onSnapshot: onRequestedSnapshot,
8061
+ registered: NO_REGISTERED_DOCUMENTS
8062
+ }) : null, evaluated.map(inst => /* @__PURE__ */ jsxRuntime.jsx(InstanceEvaluatorBoundary, {
7855
8063
  instanceId: inst._id,
7856
8064
  children: /* @__PURE__ */ jsxRuntime.jsx(InstanceEvaluator, {
7857
8065
  engine: engine,
7858
8066
  grantsFromPath: grantsFromPath,
8067
+ guardScope: guardScope,
7859
8068
  instanceId: inst._id,
7860
8069
  onReport: onReport
7861
8070
  })
7862
- }, inst._id)), props.renderDefault(props), props.overlay ]
8071
+ }, inst._id)), defaultLayout, props.overlay ]
7863
8072
  });
7864
8073
  }
7865
8074
 
@@ -7869,14 +8078,25 @@ function sameEntries(a, b) {
7869
8078
  return !0;
7870
8079
  }
7871
8080
 
8081
+ function sameMappings(a, b) {
8082
+ return a.length === b.length && a.every((mapping, index) => {
8083
+ const other = b[index];
8084
+ 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;
8085
+ });
8086
+ }
8087
+
7872
8088
  function useDeployedDefinitions(args) {
7873
- const {engine: engine, mappings: mappings, contentResource: contentResource, schemaContentTypes: schemaContentTypes, startRequest: startRequest2, autoStart: autoStart, schema: schema, workspaceName: workspaceName} = args, [issues, setIssues] = react.useState(/* @__PURE__ */ new Map), [starts, setStarts] = react.useState(/* @__PURE__ */ new Map), [fields, setFields] = react.useState(/* @__PURE__ */ new Map), [latestVersions, setLatestVersions] = react.useState(/* @__PURE__ */ new Map), [autoStartByType, setAutoStartByType] = react.useState(
8089
+ const {engine: engine, overrides: overrides, contentResource: contentResource, schemaContentTypes: schemaContentTypes, startRequest: startRequest2, schema: schema} = args, [mappings, setMappings] = react.useState([]), [issues, setIssues] = react.useState(/* @__PURE__ */ new Map), [starts, setStarts] = react.useState(/* @__PURE__ */ new Map), [fields, setFields] = react.useState(/* @__PURE__ */ new Map), [latestVersions, setLatestVersions] = react.useState(/* @__PURE__ */ new Map), [autoStartByType, setAutoStartByType] = react.useState(
7874
8090
  /* @__PURE__ */ new Map), [autoStartDefinitions, setAutoStartDefinitions] = react.useState(/* @__PURE__ */ new Map), logged = react.useRef(/* @__PURE__ */ new Set);
7875
8091
  return react.useEffect(() => {
7876
8092
  let cancelled = !1;
7877
8093
  return (async () => {
7878
- const definitions = await readDeployedDefinitions(engine), found = mappingIssues({
7879
- mappings: mappings,
8094
+ const definitions = await readDeployedDefinitions(engine), effectiveMappings = discoverWorkflowMappings({
8095
+ definitions: definitions,
8096
+ schemaContentTypes: schemaContentTypes,
8097
+ overrides: overrides
8098
+ }), found = mappingIssues({
8099
+ mappings: effectiveMappings,
7880
8100
  definitions: definitions,
7881
8101
  contentResource: contentResource,
7882
8102
  schemaContentTypes: schemaContentTypes
@@ -7886,32 +8106,30 @@ function useDeployedDefinitions(args) {
7886
8106
  logged.current.has(signature) || (logged.current.add(signature), console.error(`[workflow-studio-plugin] ${message}`));
7887
8107
  }
7888
8108
  const resolvedAutoStart = resolveAutoStart({
7889
- autoStart: autoStart,
7890
- context: {
7891
- workspaceName: workspaceName,
7892
- schema: schema
7893
- },
8109
+ autoStart: mappingAutoStartMap(effectiveMappings),
7894
8110
  knownDocTypes: new Set(schema.getTypeNames()),
7895
8111
  definitions: definitions
7896
8112
  });
7897
8113
  if (logOnce(resolvedAutoStart.warnings, logged.current), cancelled) return;
8114
+ setMappings(prev => sameMappings(prev, effectiveMappings) ? prev : effectiveMappings),
7898
8115
  setIssues(prev => sameEntries(prev, found) ? prev : found), setStarts(mappingStartBlocks({
7899
- mappings: mappings,
8116
+ mappings: effectiveMappings,
7900
8117
  definitions: definitions
7901
8118
  })), setFields(mappingDeclaredFields({
7902
- mappings: mappings,
8119
+ mappings: effectiveMappings,
7903
8120
  definitions: definitions
7904
8121
  }));
7905
8122
  const versions = new Map(workflowEngine.latestDeployedDefinitions(definitions).map(d => [ d.name, d.version ]));
7906
8123
  setLatestVersions(prev => sameEntries(prev, versions) ? prev : versions), setAutoStartByType(resolvedAutoStart.byType),
7907
8124
  setAutoStartDefinitions(resolvedAutoStart.definitions);
7908
8125
  })().catch(err => {
7909
- console.error("[workflow-studio-plugin] mapping validation could not read the deployed definitions keeping the previous verdicts:", err);
8126
+ console.error("[workflow-studio-plugin] mapping discovery failed keeping the last resolved workflow state:", err);
7910
8127
  }), () => {
7911
8128
  cancelled = !0;
7912
8129
  };
7913
- }, [ engine, mappings, contentResource, schemaContentTypes, startRequest2, autoStart, schema, workspaceName ]),
8130
+ }, [ engine, overrides, contentResource, schemaContentTypes, startRequest2, schema ]),
7914
8131
  {
8132
+ mappings: mappings,
7915
8133
  issues: issues,
7916
8134
  starts: starts,
7917
8135
  fields: fields,
@@ -8697,7 +8915,7 @@ function isFreshDocument(value) {
8697
8915
  }
8698
8916
 
8699
8917
  function subjectEntry(definition) {
8700
- return (definition.fields ?? []).find(workflowEngine.isSubjectEntry);
8918
+ return documentSubjectEntry(definition);
8701
8919
  }
8702
8920
 
8703
8921
  function autoStartInputs(args) {
@@ -8724,11 +8942,11 @@ function pendingWorkflows(workflows, live) {
8724
8942
  }
8725
8943
 
8726
8944
  function extraRequiredInputs(definition) {
8727
- const fields = definition.fields ?? [], subjectNames = new Set(fields.filter(workflowEngine.isSubjectEntry).map(entry => entry.name)), requiredNames = new Set(workflowEngine.missingRequiredInputs({
8945
+ const fields = definition.fields ?? [], subjectName = subjectEntry(definition)?.name, requiredNames = new Set(workflowEngine.missingRequiredInputs({
8728
8946
  entryDefs: fields,
8729
8947
  initialFields: []
8730
8948
  }).map(input => input.name));
8731
- return fields.filter(entry => requiredNames.has(entry.name) && !subjectNames.has(entry.name));
8949
+ return fields.filter(entry => requiredNames.has(entry.name) && entry.name !== subjectName);
8732
8950
  }
8733
8951
 
8734
8952
  function buildAutoStartRequests(args) {
@@ -8776,7 +8994,7 @@ function AutoStartDriver(props) {
8776
8994
  const pending = react.useMemo(() => pendingWorkflows(workflows, new Set(entries.map(entry => entry.instance.definition))), [ workflows, entries ]), inputs = react.useMemo(() => autoStartInputs({
8777
8995
  workflows: pending,
8778
8996
  definitions: autoStartDefinitions
8779
- }), [ pending, autoStartDefinitions ]), pendingRef = react.useRef(pending);
8997
+ }), [ pending, autoStartDefinitions ]), definitionsResolved = workflows.every(name => autoStartDefinitions.has(name)), pendingRef = react.useRef(pending);
8780
8998
  pendingRef.current = pending;
8781
8999
  const instanceIds = react.useRef(/* @__PURE__ */ new Map), instanceIdFor = react.useCallback(workflow => {
8782
9000
  const held = instanceIds.current.get(workflow);
@@ -8820,7 +9038,7 @@ function AutoStartDriver(props) {
8820
9038
  }, [ autoStartDefinitions, docId, docType, binding, engine, observer, seedInstance, instanceIdFor, selectedReleaseId ]), retry = react.useCallback(() => {
8821
9039
  firedRef.current = !1, setFired(!1), setSettled(!1), setFailure(void 0);
8822
9040
  }, []), action = autoStartAction({
8823
- resolved: isDocResolved(docId),
9041
+ resolved: definitionsResolved && isDocResolved(docId),
8824
9042
  pendingCount: pending.length,
8825
9043
  needsInput: inputs.length > 0,
8826
9044
  fired: fired,
@@ -9242,6 +9460,61 @@ function TaskCountSpinner({entry: entry}) {
9242
9460
  });
9243
9461
  }
9244
9462
 
9463
+ function stagePillState(instance) {
9464
+ const definition = definitionSnapshotOf(instance), stage = definition?.stages.find(candidate => candidate.name === instance.currentStage), state = workflowEngine.terminalState(instance);
9465
+ return {
9466
+ title: stageTitle(definition, instance.currentStage),
9467
+ terminal: stage !== void 0 && workflowEngine.isTerminalStage(stage) || state !== "in-flight",
9468
+ state: state,
9469
+ stageKnown: stage !== void 0
9470
+ };
9471
+ }
9472
+
9473
+ function WorkflowStagePreview(props) {
9474
+ 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;
9475
+ return props.renderDefault({
9476
+ ...props,
9477
+ status: lead ? /* @__PURE__ */ jsxRuntime.jsx(StagePill, {
9478
+ entry: lead,
9479
+ extraLive: extraLive
9480
+ }) : props.status
9481
+ });
9482
+ }
9483
+
9484
+ function StagePill({entry: entry, extraLive: extraLive}) {
9485
+ /* @__PURE__ */
9486
+ return jsxRuntime.jsxs(ui.Flex, {
9487
+ align: "center",
9488
+ gap: 2,
9489
+ children: [
9490
+ /* @__PURE__ */ jsxRuntime.jsx(LeadBadge, {
9491
+ entry: entry
9492
+ }), extraLive > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(ui.Badge, {
9493
+ fontSize: 1,
9494
+ mode: "outline",
9495
+ tone: "default",
9496
+ children: [ "+", extraLive ]
9497
+ }) : null ]
9498
+ });
9499
+ }
9500
+
9501
+ function LeadBadge({entry: entry}) {
9502
+ const {instance: instance} = entry;
9503
+ if (workflowEngine.isUnprimed(instance)) /* @__PURE__ */ return jsxRuntime.jsx(StartIncompleteBadge, {});
9504
+ const pill = stagePillState(instance);
9505
+ /* @__PURE__ */
9506
+ return jsxRuntime.jsxs(ui.Badge, {
9507
+ fontSize: 1,
9508
+ mode: pill.terminal ? "default" : "outline",
9509
+ tone: pillTone(pill),
9510
+ children: [ pill.title, pill.state === "completed" ? " ✓" : "" ]
9511
+ });
9512
+ }
9513
+
9514
+ function pillTone(args) {
9515
+ return args.state === "aborted" ? "critical" : args.terminal ? "positive" : args.stageKnown ? "primary" : "default";
9516
+ }
9517
+
9245
9518
  function StartWorkflowDialogHost() {
9246
9519
  const {startRequest: startRequest2, closeStartDialog: closeStartDialog} = useWorkflowContext();
9247
9520
  return startRequest2 ? /* @__PURE__ */ jsxRuntime.jsx(StartWorkflowDialog, {
@@ -9316,7 +9589,8 @@ function withWorkflowLock(Action, guardAction) {
9316
9589
  Locked;
9317
9590
  }
9318
9591
 
9319
- const workflowStudioPlugin = sanity.definePlugin(config => ({
9592
+ const workflowStudioPlugin = sanity.definePlugin(config => (assertUniqueWorkflowMappings(config.mappings ?? []),
9593
+ {
9320
9594
  name: "workflow-studio-plugin",
9321
9595
  tools: prev => [ ...prev, workflowsTool ],
9322
9596
  studio: {
@@ -9330,84 +9604,37 @@ const workflowStudioPlugin = sanity.definePlugin(config => ({
9330
9604
  },
9331
9605
  form: {
9332
9606
  components: {
9333
- input: props => {
9334
- if (props.id === "root" && sanity.isObjectInputProps(props)) {
9335
- const docTypeMappings = mappingsForDocType(config.mappings, props.schemaType.name), fallback = docTypeMappings.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(WorkflowFormStrip, {
9336
- ...props,
9337
- mappings: docTypeMappings
9338
- }) : props.renderDefault(props);
9339
- /* @__PURE__ */
9340
- return jsxRuntime.jsx(AutoStartGate, {
9341
- ...props,
9342
- fallback: fallback
9343
- });
9344
- }
9345
- return props.renderDefault(props);
9346
- }
9607
+ preview: WorkflowStagePreview,
9608
+ input: props => props.id === "root" && sanity.isObjectInputProps(props) ? /* @__PURE__ */ jsxRuntime.jsx(WorkflowRootInput, {
9609
+ ...props
9610
+ }) : props.renderDefault(props)
9347
9611
  }
9348
9612
  },
9349
9613
  document: {
9350
9614
  badges: (prev, ctx) => {
9351
- if (!mappingForDocType(config.mappings, ctx.schemaType) || !ctx.documentId) return prev;
9615
+ if (!ctx.documentId) return prev;
9352
9616
  const docId = ctx.documentId;
9353
9617
  return [ makeActiveWorkflowsBadge(docId), makePerspectiveBadge(docId), ...prev ];
9354
9618
  },
9355
- actions: (prev, ctx) => mappingForDocType(config.mappings, ctx.schemaType) ? prev.map(Action => {
9619
+ actions: prev => prev.map(Action => {
9356
9620
  const guardAction = Action.action ? LOCKABLE_ACTIONS.get(Action.action) : void 0;
9357
9621
  return guardAction ? withWorkflowLock(Action, guardAction) : Action;
9358
- }) : prev
9622
+ })
9359
9623
  }
9360
9624
  }));
9361
9625
 
9362
- function WorkflowStagePreview(props) {
9363
- const {entries: entries} = useWorkflowsForDocument(props.documentId ?? props._id ?? null), lead = entries.find(isLiveEntry) ?? entries[0], extraLive = entries.filter(e => e !== lead && isLiveEntry(e)).length;
9364
- return props.renderDefault({
9626
+ function WorkflowRootInput(props) {
9627
+ const {mappings: mappings} = useWorkflowContext(), docTypeMappings = mappingsForDocType(mappings, props.schemaType.name), fallback = docTypeMappings.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(WorkflowFormStrip, {
9365
9628
  ...props,
9366
- status: lead ? /* @__PURE__ */ jsxRuntime.jsx(StagePill, {
9367
- entry: lead,
9368
- extraLive: extraLive
9369
- }) : props.status
9370
- });
9371
- }
9372
-
9373
- function StagePill({entry: entry, extraLive: extraLive}) {
9374
- /* @__PURE__ */
9375
- return jsxRuntime.jsxs(ui.Flex, {
9376
- align: "center",
9377
- gap: 2,
9378
- children: [
9379
- /* @__PURE__ */ jsxRuntime.jsx(LeadBadge, {
9380
- entry: entry
9381
- }), extraLive > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(ui.Badge, {
9382
- fontSize: 1,
9383
- mode: "outline",
9384
- tone: "default",
9385
- children: [ "+", extraLive ]
9386
- }) : null ]
9387
- });
9388
- }
9389
-
9390
- function LeadBadge({entry: entry}) {
9391
- const {instance: instance, evaluation: evaluation} = entry;
9392
- if (workflowEngine.isUnprimed(instance)) /* @__PURE__ */ return jsxRuntime.jsx(StartIncompleteBadge, {});
9393
- const stage = evaluation?.currentStage?.stage, title = stage?.title ?? stageTitle(definitionSnapshotOf(instance), instance.currentStage), state = workflowEngine.terminalState(instance), isTerminal = (stage ? workflowEngine.isTerminalStage(stage) : !1) || state !== "in-flight";
9629
+ mappings: docTypeMappings
9630
+ }) : props.renderDefault(props);
9394
9631
  /* @__PURE__ */
9395
- return jsxRuntime.jsxs(ui.Badge, {
9396
- fontSize: 1,
9397
- mode: isTerminal ? "default" : "outline",
9398
- tone: pillTone({
9399
- state: state,
9400
- isTerminal: isTerminal,
9401
- stageKnown: stage !== void 0
9402
- }),
9403
- children: [ title, state === "completed" ? " ✓" : "" ]
9632
+ return jsxRuntime.jsx(AutoStartGate, {
9633
+ ...props,
9634
+ fallback: fallback
9404
9635
  });
9405
9636
  }
9406
9637
 
9407
- function pillTone(args) {
9408
- return args.state === "aborted" ? "critical" : args.isTerminal ? "positive" : args.stageKnown ? "primary" : "default";
9409
- }
9410
-
9411
9638
  exports.AbortedBadge = AbortedBadge;
9412
9639
 
9413
9640
  exports.ActivitiesList = ActivitiesList;
@@ -9436,6 +9663,8 @@ exports.LinkChip = LinkChip;
9436
9663
 
9437
9664
  exports.LoadingRow = LoadingRow;
9438
9665
 
9666
+ exports.SpinnerSlot = SpinnerSlot;
9667
+
9439
9668
  exports.StageChip = StageChip;
9440
9669
 
9441
9670
  exports.StageFace = StageFace;
@@ -9450,8 +9679,6 @@ exports.TrailingHairline = TrailingHairline;
9450
9679
 
9451
9680
  exports.UnreadableDocsNote = UnreadableDocsNote;
9452
9681
 
9453
- exports.WorkflowStagePreview = WorkflowStagePreview;
9454
-
9455
9682
  exports.activityRowProps = activityRowProps;
9456
9683
 
9457
9684
  exports.assigneeUserIdsOf = assigneeUserIdsOf;
@@ -9488,8 +9715,6 @@ exports.isEvaluationStale = isEvaluationStale;
9488
9715
 
9489
9716
  exports.isOpenActivityStatus = isOpenActivityStatus;
9490
9717
 
9491
- exports.mappingForDocType = mappingForDocType;
9492
-
9493
9718
  exports.openActivityGone = openActivityGone;
9494
9719
 
9495
9720
  exports.parseDateFieldValue = parseDateFieldValue;
@@ -9528,8 +9753,6 @@ exports.useWorkflowInstanceEntry = useWorkflowInstanceEntry;
9528
9753
 
9529
9754
  exports.workflowDefaultDocumentNode = workflowDefaultDocumentNode;
9530
9755
 
9531
- exports.workflowDocTypes = workflowDocTypes;
9532
-
9533
9756
  exports.workflowStudioPlugin = workflowStudioPlugin;
9534
9757
 
9535
9758
  exports.workflowsTabCodec = workflowsTabCodec;