@objectstack/service-automation 9.0.0 → 9.0.1
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 +102 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +102 -3
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -460,6 +460,8 @@ var AutomationEngine = class {
|
|
|
460
460
|
}
|
|
461
461
|
const runId = this.nextRunId();
|
|
462
462
|
variables.set("$runId", runId);
|
|
463
|
+
variables.set("$flowName", flowName);
|
|
464
|
+
variables.set("$flowLabel", flow.label ?? flowName);
|
|
463
465
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
464
466
|
const steps = [];
|
|
465
467
|
try {
|
|
@@ -668,7 +670,11 @@ var AutomationEngine = class {
|
|
|
668
670
|
const steps = run.steps;
|
|
669
671
|
const context = run.context;
|
|
670
672
|
try {
|
|
671
|
-
|
|
673
|
+
if (typeof run.correlation === "string" && run.correlation.startsWith("map:")) {
|
|
674
|
+
await this.executeNode(node, flow, variables, context, steps);
|
|
675
|
+
} else {
|
|
676
|
+
await this.traverseNext(node, flow, variables, context, steps, signal?.branchLabel);
|
|
677
|
+
}
|
|
672
678
|
const output = {};
|
|
673
679
|
if (flow.variables) {
|
|
674
680
|
for (const v of flow.variables) {
|
|
@@ -772,10 +778,12 @@ var AutomationEngine = class {
|
|
|
772
778
|
* caller who resumed the child.
|
|
773
779
|
*/
|
|
774
780
|
async bubbleToParent(run, output) {
|
|
775
|
-
const
|
|
781
|
+
const ctx = run.context;
|
|
782
|
+
const parentRunId = ctx?.$parentRunId;
|
|
776
783
|
if (typeof parentRunId !== "string" || !parentRunId) return;
|
|
777
784
|
try {
|
|
778
|
-
const
|
|
785
|
+
const mapNode = ctx?.$parentMapNode;
|
|
786
|
+
const sig = typeof mapNode === "string" && mapNode ? { variables: { [`${mapNode}.$mapItemOutput`]: output ?? null, [`${mapNode}.$mapItemDone`]: true } } : this.buildSubflowResumeSignal(run.context, output);
|
|
779
787
|
const parentRes = await this.resumeInternal(parentRunId, sig, false);
|
|
780
788
|
if (!parentRes.success) {
|
|
781
789
|
this.logger.warn(
|
|
@@ -2766,6 +2774,96 @@ function registerSubflowNode(engine, ctx) {
|
|
|
2766
2774
|
ctx.logger.info("[Subflow Node] 1 built-in node executor registered");
|
|
2767
2775
|
}
|
|
2768
2776
|
|
|
2777
|
+
// src/builtin/map-node.ts
|
|
2778
|
+
import { defineActionDescriptor as defineActionDescriptor13 } from "@objectstack/spec/automation";
|
|
2779
|
+
var MAX_MAP_ITEMS = 1e4;
|
|
2780
|
+
function registerMapNode(engine, ctx) {
|
|
2781
|
+
engine.registerNodeExecutor({
|
|
2782
|
+
type: "map",
|
|
2783
|
+
descriptor: defineActionDescriptor13({
|
|
2784
|
+
type: "map",
|
|
2785
|
+
version: "1.0.0",
|
|
2786
|
+
name: "Map",
|
|
2787
|
+
description: "Run a per-item subflow for each element of a collection, one at a time (each item may pause).",
|
|
2788
|
+
icon: "list-check",
|
|
2789
|
+
category: "logic",
|
|
2790
|
+
source: "builtin",
|
|
2791
|
+
// Each item's subflow may pause, so the map suspends and resumes per item.
|
|
2792
|
+
supportsPause: true,
|
|
2793
|
+
isAsync: true
|
|
2794
|
+
}),
|
|
2795
|
+
async execute(node, variables, context) {
|
|
2796
|
+
const cfg = node.config ?? {};
|
|
2797
|
+
const flowName = typeof cfg.flowName === "string" ? cfg.flowName : typeof cfg.flow === "string" ? cfg.flow : void 0;
|
|
2798
|
+
if (!flowName) {
|
|
2799
|
+
return { success: false, error: `map '${node.id}': config.flowName (the per-item subflow) is required` };
|
|
2800
|
+
}
|
|
2801
|
+
const iteratorVariable = typeof cfg.iteratorVariable === "string" && cfg.iteratorVariable ? cfg.iteratorVariable : "item";
|
|
2802
|
+
const indexVariable = typeof cfg.indexVariable === "string" && cfg.indexVariable ? cfg.indexVariable : void 0;
|
|
2803
|
+
const outVar = typeof cfg.outputVariable === "string" && cfg.outputVariable ? cfg.outputVariable : void 0;
|
|
2804
|
+
const rawCollection = cfg.collection;
|
|
2805
|
+
let collection;
|
|
2806
|
+
if (Array.isArray(rawCollection)) {
|
|
2807
|
+
collection = rawCollection;
|
|
2808
|
+
} else if (typeof rawCollection === "string") {
|
|
2809
|
+
collection = interpolate(rawCollection, variables, context ?? {});
|
|
2810
|
+
if (collection == null && variables.has(rawCollection)) collection = variables.get(rawCollection);
|
|
2811
|
+
}
|
|
2812
|
+
if (!Array.isArray(collection)) {
|
|
2813
|
+
return { success: false, error: `map '${node.id}': collection '${String(rawCollection)}' did not resolve to an array` };
|
|
2814
|
+
}
|
|
2815
|
+
if (collection.length > MAX_MAP_ITEMS) {
|
|
2816
|
+
return { success: false, error: `map '${node.id}': collection length ${collection.length} exceeds the ${MAX_MAP_ITEMS} cap` };
|
|
2817
|
+
}
|
|
2818
|
+
const stateKey = `${node.id}.$mapState`;
|
|
2819
|
+
const state = variables.get(stateKey) ?? {
|
|
2820
|
+
started: 0,
|
|
2821
|
+
results: []
|
|
2822
|
+
};
|
|
2823
|
+
if (variables.get(`${node.id}.$mapItemDone`) === true) {
|
|
2824
|
+
state.results.push(variables.get(`${node.id}.$mapItemOutput`) ?? null);
|
|
2825
|
+
variables.delete(`${node.id}.$mapItemDone`);
|
|
2826
|
+
variables.delete(`${node.id}.$mapItemOutput`);
|
|
2827
|
+
}
|
|
2828
|
+
const parentRunId = variables.get("$runId");
|
|
2829
|
+
while (state.started < collection.length) {
|
|
2830
|
+
const idx = state.started;
|
|
2831
|
+
const item = collection[idx];
|
|
2832
|
+
variables.set(iteratorVariable, item);
|
|
2833
|
+
if (indexVariable) variables.set(indexVariable, idx);
|
|
2834
|
+
const rawInput = cfg.input && typeof cfg.input === "object" ? cfg.input : {};
|
|
2835
|
+
const params = interpolate(rawInput, variables, context ?? {});
|
|
2836
|
+
const itemIsRecord = item != null && typeof item === "object" && typeof item.id === "string";
|
|
2837
|
+
const itemObject = typeof cfg.itemObject === "string" ? cfg.itemObject : context?.object;
|
|
2838
|
+
const childContext = {
|
|
2839
|
+
...context ?? {},
|
|
2840
|
+
params,
|
|
2841
|
+
...itemIsRecord ? { record: item, object: itemObject } : {},
|
|
2842
|
+
...parentRunId != null ? { $parentRunId: String(parentRunId), $parentMapNode: node.id } : {}
|
|
2843
|
+
};
|
|
2844
|
+
const child = await engine.execute(flowName, childContext);
|
|
2845
|
+
if (child.status === "paused") {
|
|
2846
|
+
if (!child.runId) {
|
|
2847
|
+
return { success: false, error: `map '${node.id}': item ${idx} paused without a run id \u2014 cannot link the runs` };
|
|
2848
|
+
}
|
|
2849
|
+
state.started = idx + 1;
|
|
2850
|
+
variables.set(stateKey, state);
|
|
2851
|
+
return { success: true, suspend: true, correlation: `map:${child.runId}` };
|
|
2852
|
+
}
|
|
2853
|
+
if (!child.success) {
|
|
2854
|
+
return { success: false, error: `map '${node.id}': item ${idx} (subflow '${flowName}') failed: ${child.error ?? "unknown error"}` };
|
|
2855
|
+
}
|
|
2856
|
+
state.started = idx + 1;
|
|
2857
|
+
state.results.push(child.output ?? null);
|
|
2858
|
+
}
|
|
2859
|
+
variables.set(stateKey, state);
|
|
2860
|
+
if (outVar) variables.set(outVar, state.results);
|
|
2861
|
+
return { success: true, output: { results: state.results, count: state.results.length } };
|
|
2862
|
+
}
|
|
2863
|
+
});
|
|
2864
|
+
ctx.logger.info("[Map Node] 1 built-in node executor registered");
|
|
2865
|
+
}
|
|
2866
|
+
|
|
2769
2867
|
// src/builtin/index.ts
|
|
2770
2868
|
function installBuiltinNodes(engine, ctx) {
|
|
2771
2869
|
registerLogicNodes(engine, ctx);
|
|
@@ -2779,6 +2877,7 @@ function installBuiltinNodes(engine, ctx) {
|
|
|
2779
2877
|
registerNotifyNode(engine, ctx);
|
|
2780
2878
|
registerWaitNode(engine, ctx);
|
|
2781
2879
|
registerSubflowNode(engine, ctx);
|
|
2880
|
+
registerMapNode(engine, ctx);
|
|
2782
2881
|
const types = engine.getRegisteredNodeTypes();
|
|
2783
2882
|
ctx.logger.info(
|
|
2784
2883
|
`[Automation] ${types.length} built-in node executors installed: ${types.join(", ")}`
|