@ixo/editor 6.14.0 → 6.16.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.
@@ -649,7 +649,8 @@ function generateActionManifest() {
649
649
  proof: serializeProof(action),
650
650
  hasDynamicEvents: !!action.getDynamicEvents,
651
651
  hasDynamicOutputSchema: !!action.getDynamicOutputSchema,
652
- eligibleForEventTrigger: !!action.eligibleForEventTrigger
652
+ eligibleForEventTrigger: !!action.eligibleForEventTrigger,
653
+ hasCustomInputValidation: !!action.getMissingInputs
653
654
  };
654
655
  if (action.can) entry.can = action.can;
655
656
  if (action.requiredCapability) entry.requiredCapability = action.requiredCapability;
@@ -668,6 +669,49 @@ function generateActionManifest() {
668
669
  return { manifestVersion: "1", actions: actions2 };
669
670
  }
670
671
 
672
+ // src/core/lib/actionRegistry/inputRequirements.ts
673
+ function isBlankInputValue(value) {
674
+ if (value == null) return true;
675
+ if (typeof value === "string") return value.trim().length === 0;
676
+ if (Array.isArray(value)) return value.length === 0;
677
+ return false;
678
+ }
679
+ function parseMaybeJsonObject(value) {
680
+ if (value && typeof value === "object" && !Array.isArray(value)) return value;
681
+ if (typeof value === "string" && value.trim()) {
682
+ try {
683
+ const parsed = JSON.parse(value);
684
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
685
+ } catch {
686
+ return void 0;
687
+ }
688
+ }
689
+ return void 0;
690
+ }
691
+ function dedupe(values) {
692
+ return Array.from(new Set(values.filter((value) => typeof value === "string" && value.length > 0)));
693
+ }
694
+ function getMissingActionInputs(actionType, inputs) {
695
+ if (!actionType) return [];
696
+ const action = getAction(actionType);
697
+ if (!action) return [];
698
+ const merged = inputs ?? {};
699
+ if (action.getMissingInputs) {
700
+ try {
701
+ return dedupe(action.getMissingInputs(merged) || []);
702
+ } catch (error) {
703
+ warnOnce(
704
+ `missing-inputs:${action.type}`,
705
+ `[flow-config] action ${action.type}: getMissingInputs threw (${error instanceof Error ? error.message : String(error)}); reporting no missing inputs`
706
+ );
707
+ return [];
708
+ }
709
+ }
710
+ const schema = action.inputSchema;
711
+ const required = Array.isArray(schema?.required) ? schema.required.filter((name) => typeof name === "string") : [];
712
+ return dedupe(required.filter((name) => isBlankInputValue(merged[name])));
713
+ }
714
+
671
715
  // src/core/lib/actionRegistry/canMapping.ts
672
716
  var CAN_TO_TYPE = {
673
717
  "bid/submit": "qi/bid.submit",
@@ -940,18 +984,51 @@ function buildServicesFromHandlers(handlers) {
940
984
 
941
985
  // src/core/lib/xeroWorkItems.ts
942
986
  var XERO_WORK_ITEMS_MAP_NAME = "xeroWorkItems";
987
+ var XERO_WORK_TERMINAL_MAP_NAME = "xeroWorkItemsTerminal";
943
988
  function getXeroWorkItemsMap(doc) {
944
989
  return doc.getMap(XERO_WORK_ITEMS_MAP_NAME);
945
990
  }
991
+ function getXeroWorkTerminalMap(doc) {
992
+ return doc.getMap(XERO_WORK_TERMINAL_MAP_NAME);
993
+ }
994
+ function terminalMapFor(map) {
995
+ return map?.doc ? getXeroWorkTerminalMap(map.doc) : null;
996
+ }
997
+ function resolveItem(terminal, item) {
998
+ const record = terminal?.get(item.id);
999
+ if (!record || item.status === "completed") return item;
1000
+ return {
1001
+ ...item,
1002
+ status: "completed",
1003
+ error: void 0,
1004
+ ...record.reviewPayload !== void 0 ? { reviewPayload: record.reviewPayload } : {},
1005
+ ...record.submittedPayload !== void 0 ? { submittedPayload: record.submittedPayload } : {},
1006
+ ...record.result !== void 0 ? { result: record.result } : {},
1007
+ completedAt: record.completedAt,
1008
+ ...record.completedByDid !== void 0 ? { completedByDid: record.completedByDid } : {}
1009
+ };
1010
+ }
1011
+ function ensureTerminalRecord(terminal, item) {
1012
+ if (!terminal || terminal.get(item.id)) return;
1013
+ terminal.set(item.id, {
1014
+ itemId: item.id,
1015
+ ...item.reviewPayload !== void 0 ? { reviewPayload: item.reviewPayload } : {},
1016
+ ...item.submittedPayload !== void 0 ? { submittedPayload: item.submittedPayload } : {},
1017
+ ...item.result !== void 0 ? { result: item.result } : {},
1018
+ completedAt: item.completedAt ?? Date.now(),
1019
+ ...item.completedByDid !== void 0 ? { completedByDid: item.completedByDid } : {}
1020
+ });
1021
+ }
946
1022
  function getEditorXeroWorkItemsMap(editor) {
947
1023
  return editor?._yXeroWorkItems || null;
948
1024
  }
949
1025
  function readMapItems(map) {
950
1026
  if (!map) return [];
1027
+ const terminal = terminalMapFor(map);
951
1028
  const items = [];
952
1029
  map.forEach((value) => {
953
1030
  if (value && typeof value === "object" && "id" in value) {
954
- items.push({ ...value });
1031
+ items.push(resolveItem(terminal, { ...value }));
955
1032
  }
956
1033
  });
957
1034
  return items;
@@ -995,18 +1072,25 @@ function upsertXeroWorkItem(map, next) {
995
1072
  if (!map) {
996
1073
  return next;
997
1074
  }
1075
+ const terminal = terminalMapFor(map);
998
1076
  let existing;
999
1077
  map.forEach((item) => {
1000
1078
  if (item?.idempotencyKey === next.idempotencyKey) existing = item;
1001
1079
  });
1002
- const stored = existing ? mergeItem(existing, next) : next;
1003
- map.set(stored.id, stored);
1080
+ const stored = existing ? mergeItem(resolveItem(terminal, existing), next) : next;
1081
+ const write = () => {
1082
+ map.set(stored.id, stored);
1083
+ if (stored.status === "completed") ensureTerminalRecord(terminal, stored);
1084
+ };
1085
+ if (map.doc) map.doc.transact(write);
1086
+ else write();
1004
1087
  return stored;
1005
1088
  }
1006
1089
  function updateXeroWorkReviewPayload(map, itemId, reviewPayload) {
1007
1090
  if (!map) return null;
1008
- const item = map.get(itemId);
1009
- if (!item) return null;
1091
+ const raw = map.get(itemId);
1092
+ if (!raw) return null;
1093
+ const item = resolveItem(terminalMapFor(map), raw);
1010
1094
  if (item.status === "completed") return item;
1011
1095
  const updatedItem = { ...item, reviewPayload };
1012
1096
  map.set(itemId, updatedItem);
@@ -1014,8 +1098,9 @@ function updateXeroWorkReviewPayload(map, itemId, reviewPayload) {
1014
1098
  }
1015
1099
  function markXeroWorkFailed(map, itemId, params) {
1016
1100
  if (!map) return null;
1017
- const item = map.get(itemId);
1018
- if (!item) return null;
1101
+ const raw = map.get(itemId);
1102
+ if (!raw) return null;
1103
+ const item = resolveItem(terminalMapFor(map), raw);
1019
1104
  if (item.status === "completed") return item;
1020
1105
  const at = params.at ?? Date.now();
1021
1106
  const updatedItem = {
@@ -1030,8 +1115,9 @@ function markXeroWorkFailed(map, itemId, params) {
1030
1115
  }
1031
1116
  function markXeroWorkApproved(map, itemId, params) {
1032
1117
  if (!map) return null;
1033
- const item = map.get(itemId);
1034
- if (!item) return null;
1118
+ const raw = map.get(itemId);
1119
+ if (!raw) return null;
1120
+ const item = resolveItem(terminalMapFor(map), raw);
1035
1121
  if (item.status === "approved" || item.status === "completed" || item.status === "cancelled") return item;
1036
1122
  const at = params.at ?? Date.now();
1037
1123
  const updatedItem = {
@@ -1046,8 +1132,10 @@ function markXeroWorkApproved(map, itemId, params) {
1046
1132
  }
1047
1133
  function markXeroWorkCompleted(map, itemId, params) {
1048
1134
  if (!map) return null;
1049
- const item = map.get(itemId);
1050
- if (!item) return null;
1135
+ const raw = map.get(itemId);
1136
+ if (!raw) return null;
1137
+ const terminal = terminalMapFor(map);
1138
+ const item = resolveItem(terminal, raw);
1051
1139
  if (item.status === "completed") return item;
1052
1140
  const at = params.at ?? Date.now();
1053
1141
  const updatedItem = {
@@ -1061,7 +1149,12 @@ function markXeroWorkCompleted(map, itemId, params) {
1061
1149
  completedByDid: params.byDid,
1062
1150
  attempts: [...item.attempts ?? [], { at, byDid: params.byDid, payload: params.payload, result: params.result }]
1063
1151
  };
1064
- map.set(itemId, updatedItem);
1152
+ const write = () => {
1153
+ map.set(itemId, updatedItem);
1154
+ ensureTerminalRecord(terminal, updatedItem);
1155
+ };
1156
+ if (map.doc) map.doc.transact(write);
1157
+ else write();
1065
1158
  return updatedItem;
1066
1159
  }
1067
1160
  function findXeroWorkItemsForEditor(editor, filter) {
@@ -1114,6 +1207,9 @@ registerAction({
1114
1207
  { path: "purposeDescription", displayName: "Purpose Description", type: "string", description: "The user-provided purpose text" },
1115
1208
  { path: "blueprintCandidates", displayName: "Blueprint Candidates", type: "array", description: "Ranked array of matching blueprint DIDs and metadata" }
1116
1209
  ],
1210
+ // Mirrors run(): either userMessage or purposeDescription satisfies the
1211
+ // purpose-text requirement, reported under the primary name.
1212
+ getMissingInputs: (inputs) => String(inputs.userMessage || inputs.purposeDescription || "").trim() ? [] : ["userMessage"],
1117
1213
  run: async (inputs) => {
1118
1214
  const purposeDescription = String(inputs.userMessage || inputs.purposeDescription || "").trim();
1119
1215
  if (!purposeDescription) throw new Error("userMessage is required");
@@ -1227,6 +1323,8 @@ registerAction({
1227
1323
  pendingDisplayFields: ["selectedEntityName", "selectedEntityDid"]
1228
1324
  }
1229
1325
  ],
1326
+ // Mirrors run(): a truthy `skipped` waives the selection entirely.
1327
+ getMissingInputs: (inputs) => inputs.skipped ? [] : isBlankInputValue(inputs.selectedEntityDid) ? ["selectedEntityDid"] : [],
1230
1328
  run: async (inputs) => {
1231
1329
  const skipped = !!inputs.skipped;
1232
1330
  if (skipped) {
@@ -1289,6 +1387,30 @@ registerAction({
1289
1387
  pendingDisplayFields: ["memberConfig.memberCount"]
1290
1388
  }
1291
1389
  ],
1390
+ // Mirrors run(), including its default: an absent groupType is treated as
1391
+ // 'categorical', not as a missing input.
1392
+ getMissingInputs: (inputs) => {
1393
+ const groupType = String(inputs.groupType || "").trim() || "categorical";
1394
+ if (groupType === "nftStaking") {
1395
+ return String(inputs.nftContractAddress || "").trim() ? [] : ["nftContractAddress"];
1396
+ }
1397
+ if (groupType === "tokenStaking") {
1398
+ const tokenConfig = inputs.tokenConfig;
1399
+ if (!tokenConfig || typeof tokenConfig !== "object") return ["tokenConfig"];
1400
+ if (tokenConfig.isExistingToken) {
1401
+ return String(tokenConfig.tokenAddress || "").trim() ? [] : ["tokenConfig.tokenAddress"];
1402
+ }
1403
+ const missing2 = [];
1404
+ if (!String(tokenConfig.tokenName || "").trim()) missing2.push("tokenConfig.tokenName");
1405
+ if (!String(tokenConfig.tokenSymbol || "").trim()) missing2.push("tokenConfig.tokenSymbol");
1406
+ if (isBlankInputValue(tokenConfig.tokenSupply)) missing2.push("tokenConfig.tokenSupply");
1407
+ return missing2;
1408
+ }
1409
+ const missing = [];
1410
+ if (!Array.isArray(inputs.members) || inputs.members.length === 0) missing.push("members");
1411
+ if (groupType === "multisig" && isBlankInputValue(inputs.multisigThreshold)) missing.push("multisigThreshold");
1412
+ return missing;
1413
+ },
1292
1414
  run: async (inputs) => {
1293
1415
  const groupType = inputs.groupType || "categorical";
1294
1416
  if (groupType === "nftStaking") {
@@ -1377,6 +1499,29 @@ registerAction({
1377
1499
  pendingDisplayFields: ["governanceConfig.groupName", "governanceConfig.groupType"]
1378
1500
  }
1379
1501
  ],
1502
+ // Mirrors run()'s presence preamble: base fields always, then the decision
1503
+ // policy fields the selected groupType demands. Value validity (quorum and
1504
+ // threshold ranges, veto sum) stays in run().
1505
+ getMissingInputs: (inputs) => {
1506
+ const missing = [];
1507
+ if (!String(inputs.groupName || "").trim()) missing.push("groupName");
1508
+ const groupType = String(inputs.groupType || "").trim();
1509
+ if (!groupType) missing.push("groupType");
1510
+ const governance = inputs.governance;
1511
+ if (!governance || typeof governance !== "object") {
1512
+ missing.push("governance", "governance.votingPeriod");
1513
+ return missing;
1514
+ }
1515
+ if (!governance.votingPeriod) missing.push("governance.votingPeriod");
1516
+ if (!groupType) return missing;
1517
+ if (groupType === "multisig") {
1518
+ if (!String(governance.threshold ?? "").trim()) missing.push("governance.threshold");
1519
+ } else {
1520
+ if (governance.quorum == null || String(governance.quorum).trim() === "") missing.push("governance.quorum");
1521
+ if (governance.threshold == null || String(governance.threshold).trim() === "") missing.push("governance.threshold");
1522
+ }
1523
+ return missing;
1524
+ },
1380
1525
  run: async (inputs) => {
1381
1526
  const groupName = String(inputs.groupName || "").trim();
1382
1527
  if (!groupName) throw new Error("groupName is required");
@@ -5561,6 +5706,14 @@ registerAction({
5561
5706
  pendingDisplayFields: ["entityDid", "governanceGroupCoreAddress"]
5562
5707
  }
5563
5708
  ],
5709
+ // Mirrors run(): the card envelope (object or JSON string) must carry
5710
+ // credentialSubject.name before signing can start.
5711
+ getMissingInputs: (inputs) => {
5712
+ const card = parseMaybeJsonObject(inputs.domainCardData);
5713
+ if (!card) return ["domainCardData"];
5714
+ const subject = card.credentialSubject;
5715
+ return subject && String(subject.name || "").trim() ? [] : ["domainCardData.credentialSubject.name"];
5716
+ },
5564
5717
  run: async (inputs, ctx) => {
5565
5718
  const handlers = ctx.handlers || {};
5566
5719
  let domainCardData;
@@ -5914,6 +6067,14 @@ registerAction({
5914
6067
  pendingDisplayFields: ["approvedAt"]
5915
6068
  }
5916
6069
  ],
6070
+ // Mirrors run(): the card envelope (object or JSON string) must carry
6071
+ // credentialSubject.name before a preview can render.
6072
+ getMissingInputs: (inputs) => {
6073
+ const card = parseMaybeJsonObject(inputs.domainCardData);
6074
+ if (!card) return ["domainCardData"];
6075
+ const subject = card.credentialSubject;
6076
+ return subject && String(subject.name || "").trim() ? [] : ["domainCardData.credentialSubject.name"];
6077
+ },
5917
6078
  run: async (inputs) => {
5918
6079
  let domainCardData = inputs.domainCardData;
5919
6080
  if (typeof domainCardData === "string") {
@@ -11014,6 +11175,7 @@ var buildFlowNodeFromBlock = (block) => {
11014
11175
 
11015
11176
  // src/core/lib/flowEngine/runtime.ts
11016
11177
  var XERO_WORK_ITEMS_MAP_NAME2 = "xeroWorkItems";
11178
+ var XERO_WORK_TERMINAL_MAP_NAME2 = "xeroWorkItemsTerminal";
11017
11179
  var XERO_CONNECTION_MAP_NAME = "xeroConnection";
11018
11180
  var ensureStateObject = (value) => {
11019
11181
  if (!value || typeof value !== "object") {
@@ -11060,6 +11222,7 @@ function clearRuntimeForTemplateClone(yDoc) {
11060
11222
  const agentLeases = yDoc.getMap("agentLeases");
11061
11223
  const auditTrail = yDoc.getMap("auditTrail");
11062
11224
  const xeroWorkItems = yDoc.getMap(XERO_WORK_ITEMS_MAP_NAME2);
11225
+ const xeroWorkTerminal = yDoc.getMap(XERO_WORK_TERMINAL_MAP_NAME2);
11063
11226
  const xeroConnection = yDoc.getMap(XERO_CONNECTION_MAP_NAME);
11064
11227
  yDoc.transact(() => {
11065
11228
  runtime.forEach((_, key) => runtime.delete(key));
@@ -11069,6 +11232,7 @@ function clearRuntimeForTemplateClone(yDoc) {
11069
11232
  agentLeases.forEach((_, key) => agentLeases.delete(key));
11070
11233
  auditTrail.forEach((_, key) => auditTrail.delete(key));
11071
11234
  xeroWorkItems.forEach((_, key) => xeroWorkItems.delete(key));
11235
+ xeroWorkTerminal.forEach((_, key) => xeroWorkTerminal.delete(key));
11072
11236
  xeroConnection.forEach((_, key) => xeroConnection.delete(key));
11073
11237
  });
11074
11238
  }
@@ -14882,6 +15046,8 @@ export {
14882
15046
  getEventsForBlock,
14883
15047
  getOutputSchemaForBlock,
14884
15048
  generateActionManifest,
15049
+ isBlankInputValue,
15050
+ getMissingActionInputs,
14885
15051
  canToType,
14886
15052
  typeToCan,
14887
15053
  getAllCanMappings,
@@ -14894,7 +15060,9 @@ export {
14894
15060
  extractDid,
14895
15061
  extractSurveyAnswerSchema,
14896
15062
  XERO_WORK_ITEMS_MAP_NAME,
15063
+ XERO_WORK_TERMINAL_MAP_NAME,
14897
15064
  getXeroWorkItemsMap,
15065
+ getXeroWorkTerminalMap,
14898
15066
  buildXeroInvoiceWorkKey,
14899
15067
  buildXeroPaymentWorkKey,
14900
15068
  findXeroWorkItems,
@@ -15036,4 +15204,4 @@ export {
15036
15204
  executeQueuedFlowAgentCoreCommands,
15037
15205
  FlowAgentService
15038
15206
  };
15039
- //# sourceMappingURL=chunk-LMHIE6UG.js.map
15207
+ //# sourceMappingURL=chunk-TFY73KIE.js.map