@objectstack/service-automation 9.5.1 → 9.7.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 +86 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +46 -16
- package/dist/index.d.ts +46 -16
- package/dist/index.js +86 -17
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.cjs
CHANGED
|
@@ -67,6 +67,8 @@ var _AutomationEngine = class _AutomationEngine {
|
|
|
67
67
|
this.boundFlowTriggers = /* @__PURE__ */ new Map();
|
|
68
68
|
/** Connectors registered by integration plugins, keyed by connector name (ADR-0018 §Addendum). */
|
|
69
69
|
this.connectors = /* @__PURE__ */ new Map();
|
|
70
|
+
/** Bridge to the host function registry for `script`-node calls (#1870), if wired. */
|
|
71
|
+
this.functionResolver = null;
|
|
70
72
|
this.executionLogs = [];
|
|
71
73
|
/**
|
|
72
74
|
* Runs paused at a node, keyed by runId (ADR-0019). In-memory hot cache —
|
|
@@ -354,6 +356,24 @@ var _AutomationEngine = class _AutomationEngine {
|
|
|
354
356
|
resolveConnectorAction(connectorId, actionId) {
|
|
355
357
|
return this.connectors.get(connectorId)?.handlers[actionId];
|
|
356
358
|
}
|
|
359
|
+
/**
|
|
360
|
+
* Wire the engine to the host's named-function registry (#1870). The
|
|
361
|
+
* automation plugin calls this in `start()` with a resolver backed by
|
|
362
|
+
* ObjectQL's `resolveFunction` (populated from `bundle.functions` /
|
|
363
|
+
* `defineStack({ functions })`), so a `script` node can invoke an
|
|
364
|
+
* authored function by name. Passing `null` detaches the bridge.
|
|
365
|
+
*/
|
|
366
|
+
setFunctionResolver(resolver) {
|
|
367
|
+
this.functionResolver = resolver;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Resolve a named function for a `script` node. Returns `undefined` when no
|
|
371
|
+
* resolver is wired or the name is unregistered — the node then fails the
|
|
372
|
+
* step with a clear error rather than silently no-op'ing.
|
|
373
|
+
*/
|
|
374
|
+
resolveFunction(name) {
|
|
375
|
+
return this.functionResolver?.(name) ?? void 0;
|
|
376
|
+
}
|
|
357
377
|
/** Get all registered connector names. */
|
|
358
378
|
getRegisteredConnectors() {
|
|
359
379
|
return [...this.connectors.keys()];
|
|
@@ -1897,7 +1917,7 @@ function resolveToken(token, variables, context) {
|
|
|
1897
1917
|
if (path[0] === "Email") return resolvePath(context.user, ["email", ...path.slice(1)]) ?? void 0;
|
|
1898
1918
|
return resolvePath(context.user, path);
|
|
1899
1919
|
}
|
|
1900
|
-
if (/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]
|
|
1920
|
+
if (/^[A-Za-z_$][\w$]*(?:\.(?:[A-Za-z_$][\w$]*|\d+))*$/.test(trimmed)) {
|
|
1901
1921
|
const segments = trimmed.split(".");
|
|
1902
1922
|
const head = segments[0];
|
|
1903
1923
|
if (variables.has(head)) {
|
|
@@ -2271,14 +2291,21 @@ function registerCrudNodes(engine, ctx) {
|
|
|
2271
2291
|
const data = getData();
|
|
2272
2292
|
if (!data) {
|
|
2273
2293
|
ctx.logger.warn(`[create_record] no data engine; skipping ${objectName}`);
|
|
2274
|
-
|
|
2275
|
-
|
|
2294
|
+
const mockId = `mock-${objectName}-${Date.now()}`;
|
|
2295
|
+
if (outputVariable) variables.set(outputVariable, { id: mockId });
|
|
2296
|
+
return { success: true, output: { id: mockId, object: objectName } };
|
|
2276
2297
|
}
|
|
2277
2298
|
try {
|
|
2278
2299
|
const created = await data.insert(objectName, fields);
|
|
2279
|
-
const
|
|
2280
|
-
|
|
2281
|
-
|
|
2300
|
+
const createdRecord = Array.isArray(created) ? created[0] : created;
|
|
2301
|
+
const insertedId = createdRecord && typeof createdRecord === "object" ? createdRecord.id : createdRecord;
|
|
2302
|
+
if (outputVariable) {
|
|
2303
|
+
variables.set(
|
|
2304
|
+
outputVariable,
|
|
2305
|
+
createdRecord && typeof createdRecord === "object" ? createdRecord : { id: insertedId }
|
|
2306
|
+
);
|
|
2307
|
+
}
|
|
2308
|
+
return { success: true, output: { id: insertedId, record: createdRecord, object: objectName } };
|
|
2282
2309
|
} catch (err) {
|
|
2283
2310
|
return { success: false, error: `create_record(${objectName}) failed: ${err.message}` };
|
|
2284
2311
|
}
|
|
@@ -2345,6 +2372,7 @@ function registerCrudNodes(engine, ctx) {
|
|
|
2345
2372
|
|
|
2346
2373
|
// src/builtin/screen-nodes.ts
|
|
2347
2374
|
var import_automation7 = require("@objectstack/spec/automation");
|
|
2375
|
+
var SCRIPT_BUILTIN_ACTION_TYPES = /* @__PURE__ */ new Set(["email", "slack"]);
|
|
2348
2376
|
function registerScreenNodes(engine, ctx) {
|
|
2349
2377
|
engine.registerNodeExecutor({
|
|
2350
2378
|
type: "screen",
|
|
@@ -2400,24 +2428,53 @@ function registerScreenNodes(engine, ctx) {
|
|
|
2400
2428
|
category: "logic",
|
|
2401
2429
|
source: "builtin"
|
|
2402
2430
|
}),
|
|
2403
|
-
async execute(node,
|
|
2431
|
+
async execute(node, variables, context) {
|
|
2404
2432
|
const cfg = node.config ?? {};
|
|
2405
|
-
const
|
|
2406
|
-
|
|
2433
|
+
const fnRaw = cfg.function ?? cfg.functionName;
|
|
2434
|
+
const fnName = typeof fnRaw === "string" && fnRaw.trim() ? fnRaw.trim() : void 0;
|
|
2435
|
+
const actionType = typeof cfg.actionType === "string" && cfg.actionType.trim() ? cfg.actionType.trim() : void 0;
|
|
2436
|
+
if (!fnName && actionType && SCRIPT_BUILTIN_ACTION_TYPES.has(actionType)) {
|
|
2407
2437
|
ctx.logger.info(
|
|
2408
|
-
`[Script
|
|
2438
|
+
`[Script:${actionType}] template=${String(cfg.template)} recipients=${JSON.stringify(cfg.recipients)} vars=${JSON.stringify(cfg.variables)}`
|
|
2409
2439
|
);
|
|
2410
2440
|
return {
|
|
2411
2441
|
success: true,
|
|
2412
|
-
output: {
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2442
|
+
output: { actionType, template: cfg.template, recipients: cfg.recipients }
|
|
2443
|
+
};
|
|
2444
|
+
}
|
|
2445
|
+
const inlineScript = typeof cfg.script === "string" && cfg.script.trim() ? cfg.script : void 0;
|
|
2446
|
+
if (!fnName && inlineScript) {
|
|
2447
|
+
ctx.logger.warn(
|
|
2448
|
+
`[Script] node '${node.id}': inline \`config.script\` is not executed by the built-in runtime (no server-side JS sandbox) \u2014 this node is a no-op. To run server logic, move it into a registered function and call it via \`config.function\` + \`defineStack({ functions })\`.`
|
|
2449
|
+
);
|
|
2450
|
+
return { success: true, output: { script: "not-executed" } };
|
|
2451
|
+
}
|
|
2452
|
+
const target = fnName ?? (actionType === "invoke_function" ? void 0 : actionType);
|
|
2453
|
+
if (!target) {
|
|
2454
|
+
return {
|
|
2455
|
+
success: false,
|
|
2456
|
+
error: actionType === "invoke_function" ? `script node '${node.id}': actionType 'invoke_function' requires \`config.function\` (or \`functionName\`) naming the function to call.` : `script node '${node.id}': declares neither \`actionType\` nor \`function\` \u2014 nothing to run.`
|
|
2457
|
+
};
|
|
2458
|
+
}
|
|
2459
|
+
const handler = engine.resolveFunction(target);
|
|
2460
|
+
if (!handler) {
|
|
2461
|
+
return {
|
|
2462
|
+
success: false,
|
|
2463
|
+
error: `script node '${node.id}': '${target}' is not a built-in action (${[...SCRIPT_BUILTIN_ACTION_TYPES].join(", ")}) and no function named '${target}' is registered. Register it via \`defineStack({ functions: { '${target}': fn } })\`, or fix the name (#1870).`
|
|
2464
|
+
};
|
|
2465
|
+
}
|
|
2466
|
+
const input = interpolate(cfg.inputs ?? cfg.input ?? {}, variables, context);
|
|
2467
|
+
const outputVariable = typeof cfg.outputVariable === "string" && cfg.outputVariable.trim() ? cfg.outputVariable.trim() : void 0;
|
|
2468
|
+
try {
|
|
2469
|
+
const result = await handler({ input, variables, automation: context, logger: ctx.logger });
|
|
2470
|
+
if (outputVariable) variables.set(outputVariable, result);
|
|
2471
|
+
return { success: true, output: { function: target, result } };
|
|
2472
|
+
} catch (err) {
|
|
2473
|
+
return {
|
|
2474
|
+
success: false,
|
|
2475
|
+
error: `script function '${target}' (node '${node.id}') failed: ${err.message}`
|
|
2417
2476
|
};
|
|
2418
2477
|
}
|
|
2419
|
-
ctx.logger.info(`[Script:${actionType}] node=${node.id} executed (no-op handler)`);
|
|
2420
|
-
return { success: true, output: { actionType } };
|
|
2421
2478
|
}
|
|
2422
2479
|
});
|
|
2423
2480
|
ctx.logger.info("[Screen/Script Nodes] 2 built-in node executors registered");
|
|
@@ -3067,6 +3124,18 @@ var AutomationServicePlugin = class {
|
|
|
3067
3124
|
ctx.logger.info("[Automation] No ObjectQL engine \u2014 suspended runs kept in-memory only");
|
|
3068
3125
|
}
|
|
3069
3126
|
}
|
|
3127
|
+
try {
|
|
3128
|
+
const fnRegistry = ctx.getService("objectql");
|
|
3129
|
+
if (fnRegistry && typeof fnRegistry.resolveFunction === "function") {
|
|
3130
|
+
this.engine.setFunctionResolver((name) => {
|
|
3131
|
+
const fn = fnRegistry.resolveFunction(name);
|
|
3132
|
+
return typeof fn === "function" ? (fnCtx) => fn(fnCtx) : void 0;
|
|
3133
|
+
});
|
|
3134
|
+
ctx.logger.debug("[Automation] script-node function registry bridged to objectql.resolveFunction");
|
|
3135
|
+
}
|
|
3136
|
+
} catch {
|
|
3137
|
+
ctx.logger.debug("[Automation] objectql not present \u2014 script-node function calls will fail loudly when used");
|
|
3138
|
+
}
|
|
3070
3139
|
try {
|
|
3071
3140
|
const ql = ctx.getService("objectql");
|
|
3072
3141
|
if (!ql) {
|