@objectstack/service-automation 7.8.0 → 8.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -181,7 +181,7 @@ var AutomationEngine = class {
181
181
  description: `Deprecated alias of '${canonicalType}' (ADR-0018 M3). Author new flows with '${canonicalType}'.`,
182
182
  category: meta?.category ?? "io",
183
183
  source: "builtin",
184
- paradigms: meta?.paradigms ?? ["flow", "workflow_rule", "approval"],
184
+ paradigms: meta?.paradigms ?? ["flow", "approval"],
185
185
  supportsRetry: true,
186
186
  needsOutbox: meta?.needsOutbox ?? false,
187
187
  deprecated: true,
@@ -1023,6 +1023,9 @@ ${failures.join("\n")}`
1023
1023
  completedAt: (/* @__PURE__ */ new Date()).toISOString(),
1024
1024
  durationMs: Date.now() - stepStart
1025
1025
  });
1026
+ if (result.childSteps?.length) {
1027
+ steps.push(...result.childSteps);
1028
+ }
1026
1029
  if (result.output) {
1027
1030
  for (const [key, value] of Object.entries(result.output)) {
1028
1031
  variables.set(`${node.id}.${key}`, value);
@@ -1087,14 +1090,19 @@ ${failures.join("\n")}`
1087
1090
  * nodes/edges, so the main DAG traversal (`traverseNext`) is never aware of
1088
1091
  * scope markers — keeping the shared traversal untouched.
1089
1092
  *
1090
- * Body step logs are kept in a region-local array (not yet merged into the
1091
- * parent run log); surfacing per-iteration steps is a follow-up.
1093
+ * #1479: the executed body steps are **returned** (tagged with `grouping`)
1094
+ * so the calling container node can fold them into the parent run log via
1095
+ * `NodeExecutionResult.childSteps`. Tagging only fills fields left undefined,
1096
+ * so when regions nest, each step keeps its **innermost** container's
1097
+ * `parentNodeId` / `iteration` / `regionKind`. On failure the region throws
1098
+ * as before (preserving `try_catch` retry semantics); a failed attempt's
1099
+ * partial steps are not surfaced.
1092
1100
  *
1093
1101
  * Durable pause (`suspend`) inside a region is not supported in this
1094
1102
  * iteration — it is converted into a clear error (mirrors the `subflow`
1095
1103
  * nested-pause guard).
1096
1104
  */
1097
- async runRegion(region, variables, context) {
1105
+ async runRegion(region, variables, context, grouping) {
1098
1106
  const entryId = (0, import_automation.findRegionEntry)(region);
1099
1107
  const entry = region.nodes.find((n) => n.id === entryId);
1100
1108
  if (!entry) {
@@ -1112,6 +1120,16 @@ ${failures.join("\n")}`
1112
1120
  }
1113
1121
  throw err;
1114
1122
  }
1123
+ if (grouping) {
1124
+ for (const step of regionSteps) {
1125
+ if (step.parentNodeId === void 0) {
1126
+ step.parentNodeId = grouping.parentNodeId;
1127
+ if (grouping.iteration !== void 0) step.iteration = grouping.iteration;
1128
+ if (grouping.regionKind !== void 0) step.regionKind = grouping.regionKind;
1129
+ }
1130
+ }
1131
+ }
1132
+ return regionSteps;
1115
1133
  }
1116
1134
  /**
1117
1135
  * Execute a promise with timeout using Promise.race.
@@ -1796,13 +1814,19 @@ function registerLoopNode(engine, ctx) {
1796
1814
  };
1797
1815
  }
1798
1816
  let iterations = 0;
1817
+ const childSteps = [];
1799
1818
  for (let i = 0; i < collection.length; i++) {
1800
1819
  variables.set(iteratorVariable, collection[i]);
1801
1820
  if (indexVariable) variables.set(indexVariable, i);
1802
- await engine.runRegion(body, variables, context ?? {});
1821
+ const iterSteps = await engine.runRegion(body, variables, context ?? {}, {
1822
+ parentNodeId: node.id,
1823
+ iteration: i,
1824
+ regionKind: "loop-body"
1825
+ });
1826
+ childSteps.push(...iterSteps);
1803
1827
  iterations++;
1804
1828
  }
1805
- return { success: true, output: { iterations } };
1829
+ return { success: true, output: { iterations }, childSteps };
1806
1830
  }
1807
1831
  });
1808
1832
  ctx.logger.info("[Loop Node] 1 built-in node executor registered");
@@ -1850,15 +1874,22 @@ function registerParallelNode(engine, ctx) {
1850
1874
  error: `parallel '${node.id}': config.branches must declare at least 2 branch regions`
1851
1875
  };
1852
1876
  }
1877
+ let branchSteps;
1853
1878
  try {
1854
- await Promise.all(
1855
- branches.map((branch) => engine.runRegion(branch, variables, context ?? {}))
1879
+ branchSteps = await Promise.all(
1880
+ branches.map(
1881
+ (branch, i) => engine.runRegion(branch, variables, context ?? {}, {
1882
+ parentNodeId: node.id,
1883
+ iteration: i,
1884
+ regionKind: "parallel-branch"
1885
+ })
1886
+ )
1856
1887
  );
1857
1888
  } catch (err) {
1858
1889
  const message = err instanceof Error ? err.message : String(err);
1859
1890
  return { success: false, error: `parallel '${node.id}': branch failed \u2014 ${message}` };
1860
1891
  }
1861
- return { success: true, output: { branches: branches.length } };
1892
+ return { success: true, output: { branches: branches.length }, childSteps: branchSteps.flat() };
1862
1893
  }
1863
1894
  });
1864
1895
  ctx.logger.info("[Parallel Node] 1 built-in node executor registered");
@@ -1929,8 +1960,11 @@ function registerTryCatchNode(engine, ctx) {
1929
1960
  if (delay > 0) await new Promise((r) => setTimeout(r, delay));
1930
1961
  }
1931
1962
  try {
1932
- await engine.runRegion(tryRegion, variables, ctxOrEmpty);
1933
- return { success: true, output: { attempts: attempt + 1, caught: false } };
1963
+ const trySteps = await engine.runRegion(tryRegion, variables, ctxOrEmpty, {
1964
+ parentNodeId: node.id,
1965
+ regionKind: "try"
1966
+ });
1967
+ return { success: true, output: { attempts: attempt + 1, caught: false }, childSteps: trySteps };
1934
1968
  } catch (err) {
1935
1969
  lastError = err instanceof Error ? err.message : String(err);
1936
1970
  }
@@ -1938,8 +1972,15 @@ function registerTryCatchNode(engine, ctx) {
1938
1972
  if (catchRegion != null) {
1939
1973
  variables.set(errorVariable, { nodeId: node.id, message: lastError });
1940
1974
  try {
1941
- await engine.runRegion(catchRegion, variables, ctxOrEmpty);
1942
- return { success: true, output: { attempts: maxRetries + 1, caught: true, error: lastError } };
1975
+ const catchSteps = await engine.runRegion(catchRegion, variables, ctxOrEmpty, {
1976
+ parentNodeId: node.id,
1977
+ regionKind: "catch"
1978
+ });
1979
+ return {
1980
+ success: true,
1981
+ output: { attempts: maxRetries + 1, caught: true, error: lastError },
1982
+ childSteps: catchSteps
1983
+ };
1943
1984
  } catch (catchErr) {
1944
1985
  const catchMsg = catchErr instanceof Error ? catchErr.message : String(catchErr);
1945
1986
  return { success: false, error: `try_catch '${node.id}': catch region failed \u2014 ${catchMsg}` };
@@ -2197,7 +2238,7 @@ function registerHttpNodes(engine, ctx) {
2197
2238
  // and the messaging HTTP outbox is wired).
2198
2239
  needsOutbox: true,
2199
2240
  supportsRetry: true,
2200
- paradigms: ["flow", "workflow_rule", "approval"],
2241
+ paradigms: ["flow", "approval"],
2201
2242
  configSchema: {
2202
2243
  type: "object",
2203
2244
  required: ["url"],
@@ -2307,8 +2348,9 @@ function registerConnectorNodes(engine, ctx) {
2307
2348
  category: "io",
2308
2349
  source: "builtin",
2309
2350
  supportsRetry: true,
2310
- // Present in all three authoring paradigms (ADR-0018 §registry table).
2311
- paradigms: ["flow", "workflow_rule", "approval"],
2351
+ // Present in both authoring paradigms (ADR-0018 §registry table;
2352
+ // workflow_rule retired per ADR-0019).
2353
+ paradigms: ["flow", "approval"],
2312
2354
  // Config contract — drives the Studio property form and flow validation.
2313
2355
  configSchema: {
2314
2356
  type: "object",
@@ -2383,7 +2425,7 @@ function registerNotifyNode(engine, ctx) {
2383
2425
  // Delivery is outbox-backed inside the messaging service (ADR-0030
2384
2426
  // emit → sys_notification_delivery), so it inherits retry/dead-letter.
2385
2427
  needsOutbox: true,
2386
- paradigms: ["flow", "workflow_rule", "approval"]
2428
+ paradigms: ["flow", "approval"]
2387
2429
  }),
2388
2430
  async execute(node, variables, context) {
2389
2431
  const cfg = node.config ?? {};