@ixo/editor 3.8.0 → 3.9.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.
@@ -62,7 +62,13 @@ var CAN_TO_TYPE = {
62
62
  "protocol/select": "qi/protocol.select",
63
63
  "human/checkbox": "qi/human.checkbox.set",
64
64
  "human/form": "qi/human.form.submit",
65
- "oracle/query": "oracle"
65
+ "oracle/query": "oracle",
66
+ "pod/domain-indexer-lookup": "qi/pod.domain-indexer-lookup",
67
+ "pod/domain-single-selection": "qi/pod.domain-single-selection",
68
+ "pod/entity-single-selection": "qi/pod.entity-single-selection",
69
+ "pod/governance-config": "qi/pod.governance-config",
70
+ "pod/member-multi-select": "qi/pod.member-multi-select",
71
+ "pod/list-domain-flows": "qi/pod.list-domain-flows"
66
72
  };
67
73
  var TYPE_TO_CAN = Object.fromEntries(Object.entries(CAN_TO_TYPE).map(([can, type]) => [type, can]));
68
74
  function canToType(can) {
@@ -206,6 +212,184 @@ function hasDiffResolver(actionType) {
206
212
  return diffResolvers.has(actionType);
207
213
  }
208
214
 
215
+ // src/core/lib/actionRegistry/actions/pod/domainIndexerLookup.ts
216
+ registerAction({
217
+ type: "qi/pod.domain-indexer-lookup",
218
+ can: "pod/domain-indexer-lookup",
219
+ sideEffect: false,
220
+ defaultRequiresConfirmation: false,
221
+ outputSchema: [
222
+ { path: "purposeDescription", displayName: "Purpose Description", type: "string", description: "The user-provided purpose text" },
223
+ { path: "blueprintCandidates", displayName: "Blueprint Candidates", type: "array", description: "Ranked array of matching blueprint DIDs and metadata" }
224
+ ],
225
+ run: async (inputs) => {
226
+ const purposeDescription = String(inputs.userMessage || inputs.purposeDescription || "").trim();
227
+ if (!purposeDescription) throw new Error("userMessage is required");
228
+ return {
229
+ output: {
230
+ purposeDescription,
231
+ blueprintCandidates: []
232
+ }
233
+ };
234
+ }
235
+ });
236
+
237
+ // src/core/lib/actionRegistry/actions/pod/domainSingleSelection.ts
238
+ registerAction({
239
+ type: "qi/pod.domain-single-selection",
240
+ can: "pod/domain-single-selection",
241
+ sideEffect: false,
242
+ defaultRequiresConfirmation: false,
243
+ outputSchema: [
244
+ { path: "selectedBlueprintDid", displayName: "Selected Blueprint DID", type: "string", description: "DID of the selected blueprint" },
245
+ { path: "selectedBlueprintName", displayName: "Selected Blueprint Name", type: "string", description: "Display name of the selected blueprint" },
246
+ { path: "selectedBlueprintDescription", displayName: "Selected Blueprint Description", type: "string", description: "Description of the selected blueprint" }
247
+ ],
248
+ run: async (inputs) => {
249
+ const selectedBlueprintDid = String(inputs.selectedBlueprintDid || "").trim();
250
+ if (!selectedBlueprintDid) throw new Error("selectedBlueprintDid is required");
251
+ return {
252
+ output: {
253
+ selectedBlueprintDid,
254
+ selectedBlueprintName: String(inputs.selectedBlueprintName || "").trim(),
255
+ selectedBlueprintDescription: String(inputs.selectedBlueprintDescription || "").trim()
256
+ }
257
+ };
258
+ }
259
+ });
260
+
261
+ // src/core/lib/actionRegistry/actions/pod/entitySingleSelection.ts
262
+ registerAction({
263
+ type: "qi/pod.entity-single-selection",
264
+ can: "pod/entity-single-selection",
265
+ sideEffect: false,
266
+ defaultRequiresConfirmation: false,
267
+ outputSchema: [
268
+ { path: "selectedEntityDid", displayName: "Selected Entity DID", type: "string", description: "DID of the selected parent entity, or null if skipped" },
269
+ { path: "selectedEntityName", displayName: "Selected Entity Name", type: "string", description: "Display name of the selected parent entity" },
270
+ { path: "skipped", displayName: "Skipped", type: "boolean", description: "True if the user skipped this step" }
271
+ ],
272
+ run: async (inputs) => {
273
+ const skipped = !!inputs.skipped;
274
+ if (skipped) {
275
+ return { output: { selectedEntityDid: null, selectedEntityName: "", skipped: true } };
276
+ }
277
+ const selectedEntityDid = String(inputs.selectedEntityDid || "").trim();
278
+ if (!selectedEntityDid) throw new Error("selectedEntityDid is required when not skipping");
279
+ return {
280
+ output: {
281
+ selectedEntityDid,
282
+ selectedEntityName: String(inputs.selectedEntityName || "").trim(),
283
+ skipped: false
284
+ }
285
+ };
286
+ }
287
+ });
288
+
289
+ // src/core/lib/actionRegistry/actions/pod/memberMultiSelect.ts
290
+ registerAction({
291
+ type: "qi/pod.member-multi-select",
292
+ can: "pod/member-multi-select",
293
+ sideEffect: false,
294
+ defaultRequiresConfirmation: false,
295
+ outputSchema: [
296
+ { path: "members", displayName: "Members", type: "array", description: "Array of { did, role, votingPower } \u2014 categorical/multisig only" },
297
+ { path: "multisigThreshold", displayName: "Multisig Threshold", type: "number", description: "Minimum signers required \u2014 multisig only" },
298
+ { path: "nftContractAddress", displayName: "NFT Contract Address", type: "string", description: "NFT staking contract address \u2014 nftStaking only" },
299
+ { path: "tokenConfig", displayName: "Token Config", type: "object", description: "Token configuration \u2014 tokenStaking only" }
300
+ ],
301
+ run: async (inputs) => {
302
+ const groupType = inputs.groupType || "categorical";
303
+ if (groupType === "nftStaking") {
304
+ const addr = String(inputs.nftContractAddress || "").trim();
305
+ if (!addr) throw new Error("NFT contract address is required for nftStaking groups");
306
+ return { output: { nftContractAddress: addr } };
307
+ }
308
+ if (groupType === "tokenStaking") {
309
+ const tokenConfig = inputs.tokenConfig;
310
+ if (!tokenConfig) throw new Error("tokenConfig is required for tokenStaking groups");
311
+ if (tokenConfig.isExistingToken) {
312
+ if (!String(tokenConfig.tokenAddress || "").trim()) throw new Error("Token contract address is required");
313
+ } else {
314
+ if (!String(tokenConfig.tokenName || "").trim()) throw new Error("Token name is required");
315
+ if (!String(tokenConfig.tokenSymbol || "").trim()) throw new Error("Token symbol is required");
316
+ const supply = Number(tokenConfig.tokenSupply);
317
+ if (isNaN(supply) || supply <= 0) throw new Error("Token supply must be a positive number");
318
+ }
319
+ return { output: { tokenConfig } };
320
+ }
321
+ const members = Array.isArray(inputs.members) ? inputs.members : [];
322
+ if (members.length === 0) throw new Error("At least one member is required");
323
+ const dids = members.map((m) => String(m.did || ""));
324
+ const uniqueDids = new Set(dids);
325
+ if (uniqueDids.size !== dids.length) throw new Error("Duplicate member DIDs are not allowed");
326
+ if (groupType === "multisig") {
327
+ const threshold = Number(inputs.multisigThreshold);
328
+ if (!Number.isInteger(threshold) || threshold < 1) throw new Error("Multisig threshold must be a positive integer");
329
+ if (threshold > members.length) throw new Error(`Threshold (${threshold}) cannot exceed member count (${members.length})`);
330
+ return { output: { members, multisigThreshold: threshold } };
331
+ }
332
+ const hasAdmin = members.some((m) => String(m.role || "").toLowerCase() === "admin");
333
+ if (!hasAdmin) throw new Error("At least one member must have the Admin role");
334
+ for (const m of members) {
335
+ const vp = Number(m.votingPower);
336
+ if (!Number.isInteger(vp) || vp <= 0) throw new Error(`Voting power must be a positive integer (got ${m.votingPower} for ${m.did})`);
337
+ }
338
+ return { output: { members } };
339
+ }
340
+ });
341
+
342
+ // src/core/lib/actionRegistry/actions/pod/governanceConfig.ts
343
+ var VALID_GROUP_TYPES = ["categorical", "multisig", "nftStaking", "tokenStaking"];
344
+ registerAction({
345
+ type: "qi/pod.governance-config",
346
+ can: "pod/governance-config",
347
+ sideEffect: false,
348
+ defaultRequiresConfirmation: false,
349
+ outputSchema: [
350
+ { path: "groupType", displayName: "Group Type", type: "string", description: "Governance group type (categorical | multisig | nftStaking | tokenStaking)" },
351
+ { path: "governance", displayName: "Governance Settings", type: "object", description: "Cosmos group decision policy parameters" }
352
+ ],
353
+ run: async (inputs) => {
354
+ const groupType = inputs.groupType;
355
+ if (!groupType || !VALID_GROUP_TYPES.includes(groupType)) {
356
+ throw new Error(`groupType must be one of: ${VALID_GROUP_TYPES.join(", ")}`);
357
+ }
358
+ const governance = inputs.governance;
359
+ if (!governance) throw new Error("governance settings are required");
360
+ if (!governance.votingPeriod) throw new Error("votingPeriod is required");
361
+ if (groupType === "multisig") {
362
+ const threshold = parseInt(governance.threshold, 10);
363
+ if (isNaN(threshold) || threshold < 1) throw new Error("multisig threshold must be a positive integer");
364
+ } else {
365
+ const quorum = parseFloat(governance.quorum);
366
+ if (isNaN(quorum) || quorum <= 0 || quorum > 1) throw new Error("quorum must be between 0 and 1 (e.g. 0.33)");
367
+ const threshold = parseFloat(governance.threshold);
368
+ if (isNaN(threshold) || threshold <= 0 || threshold > 1) throw new Error("threshold must be between 0 and 1 (e.g. 0.51)");
369
+ const vetoThreshold = parseFloat(governance.vetoThreshold);
370
+ if (!isNaN(vetoThreshold) && vetoThreshold + threshold > 1) {
371
+ throw new Error("threshold + vetoThreshold cannot exceed 1.0");
372
+ }
373
+ }
374
+ return { output: { groupType, governance } };
375
+ }
376
+ });
377
+
378
+ // src/core/lib/actionRegistry/actions/pod/listDomainFlows.ts
379
+ registerAction({
380
+ type: "qi/pod.list-domain-flows",
381
+ can: "pod/list-domain-flows",
382
+ sideEffect: false,
383
+ defaultRequiresConfirmation: false,
384
+ outputSchema: [
385
+ { path: "selectedFlowDids", displayName: "Selected Flow DIDs", type: "array", description: "Array of selected flow template DIDs" }
386
+ ],
387
+ run: async (inputs) => {
388
+ const selectedFlowDids = Array.isArray(inputs.selectedFlowDids) ? inputs.selectedFlowDids : [];
389
+ return { output: { selectedFlowDids } };
390
+ }
391
+ });
392
+
209
393
  // src/core/lib/actionRegistry/actions/httpRequest.ts
210
394
  registerAction({
211
395
  type: "qi/http.request",
@@ -2990,6 +3174,29 @@ function removePendingInvocation(yDoc, listenerBlockId, pendingInvocationId) {
2990
3174
  inner.delete(pendingInvocationId);
2991
3175
  return true;
2992
3176
  }
3177
+ function appendRunRecord(yDoc, blockId, details, userId) {
3178
+ const auditMap = yDoc.getMap("auditTrail");
3179
+ yDoc.transact(() => {
3180
+ let arr = auditMap.get(blockId);
3181
+ if (!arr) {
3182
+ arr = new Y.Array();
3183
+ auditMap.set(blockId, arr);
3184
+ }
3185
+ const event = {
3186
+ id: `${details.runId}`,
3187
+ blockId,
3188
+ type: RUN_RECORD_AUDIT_TYPE,
3189
+ details,
3190
+ message: void 0,
3191
+ meta: {
3192
+ timestamp: details.completedAt,
3193
+ userId,
3194
+ editable: false
3195
+ }
3196
+ };
3197
+ arr.push([event]);
3198
+ });
3199
+ }
2993
3200
  function readRunRecords(yDoc, blockId) {
2994
3201
  const auditMap = yDoc.getMap("auditTrail");
2995
3202
  const arr = auditMap.get(blockId);
@@ -3004,6 +3211,39 @@ function readRunRecords(yDoc, blockId) {
3004
3211
  });
3005
3212
  return records;
3006
3213
  }
3214
+ function findFailedListenersForSourceRun(yDoc, sourceBlockId, sourceRunId, listenerBlockIds) {
3215
+ const failures = [];
3216
+ for (const listenerBlockId of listenerBlockIds) {
3217
+ const records = readRunRecords(yDoc, listenerBlockId);
3218
+ for (const record of records) {
3219
+ if (!record.error) continue;
3220
+ if (record.triggeredBy?.sourceBlockId !== sourceBlockId) continue;
3221
+ if (record.fromPendingInvocationId == null) continue;
3222
+ const sourceRunIdField = record.sourceRunId;
3223
+ if (sourceRunIdField && sourceRunIdField !== sourceRunId) continue;
3224
+ failures.push({ listenerBlockId, record });
3225
+ }
3226
+ }
3227
+ return failures;
3228
+ }
3229
+ function replayFailedListenerRun(yDoc, failedRecord, listenerBlockId, originalPayload, originalRefSnapshots, assigneeDid) {
3230
+ if (!failedRecord.triggeredBy) return false;
3231
+ const replayId = `${failedRecord.runId}:replay-${Date.now().toString(36)}`;
3232
+ const now = (/* @__PURE__ */ new Date()).toISOString();
3233
+ const replay = {
3234
+ id: replayId,
3235
+ triggeringBlockId: failedRecord.triggeredBy.sourceBlockId,
3236
+ sourceRunId: failedRecord.sourceRunId || failedRecord.runId,
3237
+ eventName: failedRecord.triggeredBy.eventName,
3238
+ eventIndex: 0,
3239
+ payload: originalPayload,
3240
+ refSnapshots: originalRefSnapshots,
3241
+ assigneeDid,
3242
+ emittedAt: now,
3243
+ expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1e3).toISOString()
3244
+ };
3245
+ return queuePendingInvocation(yDoc, listenerBlockId, replay);
3246
+ }
3007
3247
 
3008
3248
  // src/core/lib/flowEngine/reconcile.ts
3009
3249
  function reconcilePendingInvocations(editor) {
@@ -3142,6 +3382,11 @@ function parseIsoDurationToMs(duration) {
3142
3382
  if (s) ms += parseInt(s, 10) * 1e3;
3143
3383
  return ms;
3144
3384
  }
3385
+ function getActionForBlock(block) {
3386
+ const actionType = block?.props?.actionType;
3387
+ if (typeof actionType !== "string") return void 0;
3388
+ return getAction(actionType);
3389
+ }
3145
3390
 
3146
3391
  // src/core/lib/flowEngine/migration.ts
3147
3392
  var MIGRATION_REGISTRY = {};
@@ -4163,9 +4408,20 @@ export {
4163
4408
  isAuthorized,
4164
4409
  executeNode,
4165
4410
  isRuntimeRef,
4411
+ RUN_RECORD_AUDIT_TYPE,
4412
+ computePendingInvocationId,
4413
+ snapshotInputRefs,
4414
+ getPendingInvocationsMap,
4415
+ getOrCreateBlockPendingMap,
4166
4416
  readPendingInvocations,
4417
+ queuePendingInvocation,
4167
4418
  removePendingInvocation,
4419
+ appendRunRecord,
4420
+ readRunRecords,
4421
+ findFailedListenersForSourceRun,
4422
+ replayFailedListenerRun,
4168
4423
  reconcilePendingInvocations,
4424
+ getActionForBlock,
4169
4425
  compileBaseUcanFlow,
4170
4426
  readCompiledFlowFromYDoc,
4171
4427
  mergeCompiledFlows,
@@ -4177,4 +4433,4 @@ export {
4177
4433
  readFlow,
4178
4434
  setupFlowFromBaseUcan
4179
4435
  };
4180
- //# sourceMappingURL=chunk-QIJA3CMG.mjs.map
4436
+ //# sourceMappingURL=chunk-JHQO6FAQ.mjs.map