@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.cjs
CHANGED
|
@@ -496,6 +496,8 @@ var AutomationEngine = class {
|
|
|
496
496
|
}
|
|
497
497
|
const runId = this.nextRunId();
|
|
498
498
|
variables.set("$runId", runId);
|
|
499
|
+
variables.set("$flowName", flowName);
|
|
500
|
+
variables.set("$flowLabel", flow.label ?? flowName);
|
|
499
501
|
const startedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
500
502
|
const steps = [];
|
|
501
503
|
try {
|
|
@@ -704,7 +706,11 @@ var AutomationEngine = class {
|
|
|
704
706
|
const steps = run.steps;
|
|
705
707
|
const context = run.context;
|
|
706
708
|
try {
|
|
707
|
-
|
|
709
|
+
if (typeof run.correlation === "string" && run.correlation.startsWith("map:")) {
|
|
710
|
+
await this.executeNode(node, flow, variables, context, steps);
|
|
711
|
+
} else {
|
|
712
|
+
await this.traverseNext(node, flow, variables, context, steps, signal?.branchLabel);
|
|
713
|
+
}
|
|
708
714
|
const output = {};
|
|
709
715
|
if (flow.variables) {
|
|
710
716
|
for (const v of flow.variables) {
|
|
@@ -808,10 +814,12 @@ var AutomationEngine = class {
|
|
|
808
814
|
* caller who resumed the child.
|
|
809
815
|
*/
|
|
810
816
|
async bubbleToParent(run, output) {
|
|
811
|
-
const
|
|
817
|
+
const ctx = run.context;
|
|
818
|
+
const parentRunId = ctx?.$parentRunId;
|
|
812
819
|
if (typeof parentRunId !== "string" || !parentRunId) return;
|
|
813
820
|
try {
|
|
814
|
-
const
|
|
821
|
+
const mapNode = ctx?.$parentMapNode;
|
|
822
|
+
const sig = typeof mapNode === "string" && mapNode ? { variables: { [`${mapNode}.$mapItemOutput`]: output ?? null, [`${mapNode}.$mapItemDone`]: true } } : this.buildSubflowResumeSignal(run.context, output);
|
|
815
823
|
const parentRes = await this.resumeInternal(parentRunId, sig, false);
|
|
816
824
|
if (!parentRes.success) {
|
|
817
825
|
this.logger.warn(
|
|
@@ -2802,6 +2810,96 @@ function registerSubflowNode(engine, ctx) {
|
|
|
2802
2810
|
ctx.logger.info("[Subflow Node] 1 built-in node executor registered");
|
|
2803
2811
|
}
|
|
2804
2812
|
|
|
2813
|
+
// src/builtin/map-node.ts
|
|
2814
|
+
var import_automation13 = require("@objectstack/spec/automation");
|
|
2815
|
+
var MAX_MAP_ITEMS = 1e4;
|
|
2816
|
+
function registerMapNode(engine, ctx) {
|
|
2817
|
+
engine.registerNodeExecutor({
|
|
2818
|
+
type: "map",
|
|
2819
|
+
descriptor: (0, import_automation13.defineActionDescriptor)({
|
|
2820
|
+
type: "map",
|
|
2821
|
+
version: "1.0.0",
|
|
2822
|
+
name: "Map",
|
|
2823
|
+
description: "Run a per-item subflow for each element of a collection, one at a time (each item may pause).",
|
|
2824
|
+
icon: "list-check",
|
|
2825
|
+
category: "logic",
|
|
2826
|
+
source: "builtin",
|
|
2827
|
+
// Each item's subflow may pause, so the map suspends and resumes per item.
|
|
2828
|
+
supportsPause: true,
|
|
2829
|
+
isAsync: true
|
|
2830
|
+
}),
|
|
2831
|
+
async execute(node, variables, context) {
|
|
2832
|
+
const cfg = node.config ?? {};
|
|
2833
|
+
const flowName = typeof cfg.flowName === "string" ? cfg.flowName : typeof cfg.flow === "string" ? cfg.flow : void 0;
|
|
2834
|
+
if (!flowName) {
|
|
2835
|
+
return { success: false, error: `map '${node.id}': config.flowName (the per-item subflow) is required` };
|
|
2836
|
+
}
|
|
2837
|
+
const iteratorVariable = typeof cfg.iteratorVariable === "string" && cfg.iteratorVariable ? cfg.iteratorVariable : "item";
|
|
2838
|
+
const indexVariable = typeof cfg.indexVariable === "string" && cfg.indexVariable ? cfg.indexVariable : void 0;
|
|
2839
|
+
const outVar = typeof cfg.outputVariable === "string" && cfg.outputVariable ? cfg.outputVariable : void 0;
|
|
2840
|
+
const rawCollection = cfg.collection;
|
|
2841
|
+
let collection;
|
|
2842
|
+
if (Array.isArray(rawCollection)) {
|
|
2843
|
+
collection = rawCollection;
|
|
2844
|
+
} else if (typeof rawCollection === "string") {
|
|
2845
|
+
collection = interpolate(rawCollection, variables, context ?? {});
|
|
2846
|
+
if (collection == null && variables.has(rawCollection)) collection = variables.get(rawCollection);
|
|
2847
|
+
}
|
|
2848
|
+
if (!Array.isArray(collection)) {
|
|
2849
|
+
return { success: false, error: `map '${node.id}': collection '${String(rawCollection)}' did not resolve to an array` };
|
|
2850
|
+
}
|
|
2851
|
+
if (collection.length > MAX_MAP_ITEMS) {
|
|
2852
|
+
return { success: false, error: `map '${node.id}': collection length ${collection.length} exceeds the ${MAX_MAP_ITEMS} cap` };
|
|
2853
|
+
}
|
|
2854
|
+
const stateKey = `${node.id}.$mapState`;
|
|
2855
|
+
const state = variables.get(stateKey) ?? {
|
|
2856
|
+
started: 0,
|
|
2857
|
+
results: []
|
|
2858
|
+
};
|
|
2859
|
+
if (variables.get(`${node.id}.$mapItemDone`) === true) {
|
|
2860
|
+
state.results.push(variables.get(`${node.id}.$mapItemOutput`) ?? null);
|
|
2861
|
+
variables.delete(`${node.id}.$mapItemDone`);
|
|
2862
|
+
variables.delete(`${node.id}.$mapItemOutput`);
|
|
2863
|
+
}
|
|
2864
|
+
const parentRunId = variables.get("$runId");
|
|
2865
|
+
while (state.started < collection.length) {
|
|
2866
|
+
const idx = state.started;
|
|
2867
|
+
const item = collection[idx];
|
|
2868
|
+
variables.set(iteratorVariable, item);
|
|
2869
|
+
if (indexVariable) variables.set(indexVariable, idx);
|
|
2870
|
+
const rawInput = cfg.input && typeof cfg.input === "object" ? cfg.input : {};
|
|
2871
|
+
const params = interpolate(rawInput, variables, context ?? {});
|
|
2872
|
+
const itemIsRecord = item != null && typeof item === "object" && typeof item.id === "string";
|
|
2873
|
+
const itemObject = typeof cfg.itemObject === "string" ? cfg.itemObject : context?.object;
|
|
2874
|
+
const childContext = {
|
|
2875
|
+
...context ?? {},
|
|
2876
|
+
params,
|
|
2877
|
+
...itemIsRecord ? { record: item, object: itemObject } : {},
|
|
2878
|
+
...parentRunId != null ? { $parentRunId: String(parentRunId), $parentMapNode: node.id } : {}
|
|
2879
|
+
};
|
|
2880
|
+
const child = await engine.execute(flowName, childContext);
|
|
2881
|
+
if (child.status === "paused") {
|
|
2882
|
+
if (!child.runId) {
|
|
2883
|
+
return { success: false, error: `map '${node.id}': item ${idx} paused without a run id \u2014 cannot link the runs` };
|
|
2884
|
+
}
|
|
2885
|
+
state.started = idx + 1;
|
|
2886
|
+
variables.set(stateKey, state);
|
|
2887
|
+
return { success: true, suspend: true, correlation: `map:${child.runId}` };
|
|
2888
|
+
}
|
|
2889
|
+
if (!child.success) {
|
|
2890
|
+
return { success: false, error: `map '${node.id}': item ${idx} (subflow '${flowName}') failed: ${child.error ?? "unknown error"}` };
|
|
2891
|
+
}
|
|
2892
|
+
state.started = idx + 1;
|
|
2893
|
+
state.results.push(child.output ?? null);
|
|
2894
|
+
}
|
|
2895
|
+
variables.set(stateKey, state);
|
|
2896
|
+
if (outVar) variables.set(outVar, state.results);
|
|
2897
|
+
return { success: true, output: { results: state.results, count: state.results.length } };
|
|
2898
|
+
}
|
|
2899
|
+
});
|
|
2900
|
+
ctx.logger.info("[Map Node] 1 built-in node executor registered");
|
|
2901
|
+
}
|
|
2902
|
+
|
|
2805
2903
|
// src/builtin/index.ts
|
|
2806
2904
|
function installBuiltinNodes(engine, ctx) {
|
|
2807
2905
|
registerLogicNodes(engine, ctx);
|
|
@@ -2815,6 +2913,7 @@ function installBuiltinNodes(engine, ctx) {
|
|
|
2815
2913
|
registerNotifyNode(engine, ctx);
|
|
2816
2914
|
registerWaitNode(engine, ctx);
|
|
2817
2915
|
registerSubflowNode(engine, ctx);
|
|
2916
|
+
registerMapNode(engine, ctx);
|
|
2818
2917
|
const types = engine.getRegisteredNodeTypes();
|
|
2819
2918
|
ctx.logger.info(
|
|
2820
2919
|
`[Automation] ${types.length} built-in node executors installed: ${types.join(", ")}`
|