@objectstack/service-automation 4.0.3 → 4.0.5

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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/engine.ts","../src/plugin.ts","../src/plugins/crud-nodes-plugin.ts","../src/plugins/logic-nodes-plugin.ts","../src/plugins/http-connector-plugin.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n// Core engine\nexport { AutomationEngine } from './engine.js';\nexport type { NodeExecutor, NodeExecutionResult, FlowTrigger } from './engine.js';\n\n// Kernel plugin\nexport { AutomationServicePlugin } from './plugin.js';\nexport type { AutomationServicePluginOptions } from './plugin.js';\n\n// Node plugins\nexport { CrudNodesPlugin } from './plugins/crud-nodes-plugin.js';\nexport { LogicNodesPlugin } from './plugins/logic-nodes-plugin.js';\nexport { HttpConnectorPlugin } from './plugins/http-connector-plugin.js';\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { FlowParsed, FlowNodeParsed, FlowEdgeParsed } from '@objectstack/spec/automation';\nimport type { ExecutionLog } from '@objectstack/spec/automation';\nimport type { AutomationContext, AutomationResult, IAutomationService } from '@objectstack/spec/contracts';\nimport type { Logger } from '@objectstack/spec/contracts';\nimport { FlowSchema } from '@objectstack/spec/automation';\n\n// ─── Node Executor Interface (Plugin Extension Point) ───────────────\n\n/**\n * Each node type corresponds to a NodeExecutor.\n * Third-party plugins only need to implement this interface and register\n * it with the engine to extend automation capabilities.\n */\nexport interface NodeExecutor {\n /** Corresponds to FlowNodeAction enum value */\n readonly type: string;\n\n /**\n * Execute a node\n * @param node - Current node definition\n * @param variables - Flow variable context (read/write)\n * @param context - Trigger context\n * @returns Execution result (may include output data, branch conditions, etc.)\n */\n execute(\n node: FlowNodeParsed,\n variables: Map<string, unknown>,\n context: AutomationContext,\n ): Promise<NodeExecutionResult>;\n}\n\nexport interface NodeExecutionResult {\n success: boolean;\n output?: Record<string, unknown>;\n error?: string;\n /** Used by decision nodes — returns the selected branch label */\n branchLabel?: string;\n}\n\n// ─── Trigger Interface (Plugin Extension Point) ─────────────────────\n\n/**\n * Trigger interface. Schedule/Event/API triggers are registered via plugins.\n */\nexport interface FlowTrigger {\n readonly type: string;\n start(flowName: string, callback: (ctx: AutomationContext) => Promise<void>): void;\n stop(flowName: string): void;\n}\n\n// ─── Core Automation Engine ─────────────────────────────────────────\n\n/**\n * Internal execution step log entry.\n */\ninterface StepLogEntry {\n nodeId: string;\n nodeType: string;\n nodeLabel?: string;\n status: 'success' | 'failure' | 'skipped';\n startedAt: string;\n completedAt?: string;\n durationMs?: number;\n error?: { code: string; message: string; stack?: string };\n}\n\n/**\n * Internal execution log entry — compatible with ExecutionLog from spec.\n */\ninterface ExecutionLogEntry {\n id: string;\n flowName: string;\n flowVersion?: number;\n status: ExecutionLog['status'];\n startedAt: string;\n completedAt?: string;\n durationMs?: number;\n trigger: { type: string; userId?: string; object?: string; recordId?: string };\n steps: StepLogEntry[];\n variables?: Record<string, unknown>;\n output?: unknown;\n error?: string;\n}\n\nexport class AutomationEngine implements IAutomationService {\n private flows = new Map<string, FlowParsed>();\n private flowEnabled = new Map<string, boolean>();\n private flowVersionHistory = new Map<string, Array<{ version: number; definition: FlowParsed; createdAt: string }>>();\n private nodeExecutors = new Map<string, NodeExecutor>();\n private triggers = new Map<string, FlowTrigger>();\n private executionLogs: ExecutionLogEntry[] = [];\n private maxLogSize = 1000;\n private logger: Logger;\n private runCounter = 0;\n\n constructor(logger: Logger) {\n this.logger = logger;\n }\n\n // ── Plugin Extension API ──────────────────────────────\n\n /** Register a node executor (called by plugins) */\n registerNodeExecutor(executor: NodeExecutor): void {\n if (this.nodeExecutors.has(executor.type)) {\n this.logger.warn(`Node executor '${executor.type}' replaced`);\n }\n this.nodeExecutors.set(executor.type, executor);\n this.logger.info(`Node executor registered: ${executor.type}`);\n }\n\n /** Unregister a node executor (hot-unplug) */\n unregisterNodeExecutor(type: string): void {\n this.nodeExecutors.delete(type);\n this.logger.info(`Node executor unregistered: ${type}`);\n }\n\n /** Register a trigger (called by plugins) */\n registerTrigger(trigger: FlowTrigger): void {\n this.triggers.set(trigger.type, trigger);\n this.logger.info(`Trigger registered: ${trigger.type}`);\n }\n\n /** Unregister a trigger (hot-unplug) */\n unregisterTrigger(type: string): void {\n this.triggers.delete(type);\n this.logger.info(`Trigger unregistered: ${type}`);\n }\n\n /** Get all registered node types */\n getRegisteredNodeTypes(): string[] {\n return [...this.nodeExecutors.keys()];\n }\n\n /** Get all registered trigger types */\n getRegisteredTriggerTypes(): string[] {\n return [...this.triggers.keys()];\n }\n\n // ── IAutomationService Contract Implementation ────────\n\n registerFlow(name: string, definition: unknown): void {\n const parsed = FlowSchema.parse(definition);\n\n // DAG cycle detection\n this.detectCycles(parsed);\n\n // Version history management\n const history = this.flowVersionHistory.get(name) ?? [];\n history.push({\n version: parsed.version,\n definition: parsed,\n createdAt: new Date().toISOString(),\n });\n this.flowVersionHistory.set(name, history);\n\n this.flows.set(name, parsed);\n if (!this.flowEnabled.has(name)) {\n this.flowEnabled.set(name, true);\n }\n this.logger.info(`Flow registered: ${name} (version ${parsed.version})`);\n }\n\n unregisterFlow(name: string): void {\n this.flows.delete(name);\n this.flowEnabled.delete(name);\n this.flowVersionHistory.delete(name);\n this.logger.info(`Flow unregistered: ${name}`);\n }\n\n async listFlows(): Promise<string[]> {\n return [...this.flows.keys()];\n }\n\n async getFlow(name: string): Promise<FlowParsed | null> {\n return this.flows.get(name) ?? null;\n }\n\n async toggleFlow(name: string, enabled: boolean): Promise<void> {\n if (!this.flows.has(name)) {\n throw new Error(`Flow '${name}' not found`);\n }\n this.flowEnabled.set(name, enabled);\n this.logger.info(`Flow '${name}' ${enabled ? 'enabled' : 'disabled'}`);\n }\n\n /** Get flow version history */\n getFlowVersionHistory(name: string): Array<{ version: number; definition: FlowParsed; createdAt: string }> {\n return this.flowVersionHistory.get(name) ?? [];\n }\n\n /** Rollback flow to a specific version */\n rollbackFlow(name: string, version: number): void {\n const history = this.flowVersionHistory.get(name);\n if (!history) {\n throw new Error(`Flow '${name}' has no version history`);\n }\n const entry = history.find(h => h.version === version);\n if (!entry) {\n throw new Error(`Version ${version} not found for flow '${name}'`);\n }\n this.flows.set(name, entry.definition);\n this.logger.info(`Flow '${name}' rolled back to version ${version}`);\n }\n\n async listRuns(flowName: string, options?: { limit?: number; cursor?: string }): Promise<ExecutionLogEntry[]> {\n const limit = options?.limit ?? 20;\n const logs = this.executionLogs.filter(l => l.flowName === flowName);\n return logs.slice(-limit).reverse();\n }\n\n async getRun(runId: string): Promise<ExecutionLogEntry | null> {\n return this.executionLogs.find(l => l.id === runId) ?? null;\n }\n\n async execute(flowName: string, context?: AutomationContext): Promise<AutomationResult> {\n const startTime = Date.now();\n const flow = this.flows.get(flowName);\n\n if (!flow) {\n return { success: false, error: `Flow '${flowName}' not found` };\n }\n\n // Check if flow is disabled\n if (this.flowEnabled.get(flowName) === false) {\n return { success: false, error: `Flow '${flowName}' is disabled` };\n }\n\n // Initialize variable context\n const variables = new Map<string, unknown>();\n if (flow.variables) {\n for (const v of flow.variables) {\n if (v.isInput && context?.params?.[v.name] !== undefined) {\n variables.set(v.name, context.params[v.name]);\n }\n }\n }\n // Inject trigger record\n if (context?.record) {\n variables.set('$record', context.record);\n }\n\n const runId = `run_${++this.runCounter}`;\n const startedAt = new Date().toISOString();\n const steps: StepLogEntry[] = [];\n\n try {\n // Find the start node\n const startNode = flow.nodes.find(n => n.type === 'start');\n if (!startNode) {\n return { success: false, error: 'Flow has no start node' };\n }\n\n // Validate node input schemas before execution\n this.validateNodeInputSchemas(flow, variables);\n\n // DAG traversal execution\n await this.executeNode(startNode, flow, variables, context ?? {}, steps);\n\n // Collect output variables\n const output: Record<string, unknown> = {};\n if (flow.variables) {\n for (const v of flow.variables) {\n if (v.isOutput) {\n output[v.name] = variables.get(v.name);\n }\n }\n }\n\n const durationMs = Date.now() - startTime;\n\n // Record execution log\n this.recordLog({\n id: runId,\n flowName,\n flowVersion: flow.version,\n status: 'completed',\n startedAt,\n completedAt: new Date().toISOString(),\n durationMs,\n trigger: {\n type: context?.event ?? 'manual',\n userId: context?.userId,\n object: context?.object,\n },\n steps,\n output,\n });\n\n return {\n success: true,\n output,\n durationMs,\n };\n } catch (err: unknown) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n\n // Record failed execution log\n const durationMs = Date.now() - startTime;\n this.recordLog({\n id: runId,\n flowName,\n flowVersion: flow.version,\n status: 'failed',\n startedAt,\n completedAt: new Date().toISOString(),\n durationMs,\n trigger: {\n type: context?.event ?? 'manual',\n userId: context?.userId,\n object: context?.object,\n },\n steps,\n error: errorMessage,\n });\n\n // Error handling strategy\n if (flow.errorHandling?.strategy === 'retry') {\n return this.retryExecution(flowName, context, startTime, flow.errorHandling);\n }\n return {\n success: false,\n error: errorMessage,\n durationMs,\n };\n }\n }\n\n // ── DAG Traversal Core ──────────────────────────────────\n\n private recordLog(entry: ExecutionLogEntry): void {\n this.executionLogs.push(entry);\n // Evict oldest logs when exceeding max size\n if (this.executionLogs.length > this.maxLogSize) {\n this.executionLogs.splice(0, this.executionLogs.length - this.maxLogSize);\n }\n }\n\n /**\n * Detect cycles in the flow graph (DAG validation).\n * Uses DFS with coloring (white/gray/black) to detect back edges.\n * Throws an error with cycle details if a cycle is found.\n */\n private detectCycles(flow: FlowParsed): void {\n const WHITE = 0, GRAY = 1, BLACK = 2;\n const color = new Map<string, number>();\n const parent = new Map<string, string>();\n\n // Build adjacency list from edges\n const adj = new Map<string, string[]>();\n for (const node of flow.nodes) {\n color.set(node.id, WHITE);\n adj.set(node.id, []);\n }\n for (const edge of flow.edges) {\n const targets = adj.get(edge.source);\n if (targets) targets.push(edge.target);\n }\n\n const dfs = (nodeId: string): string[] | null => {\n color.set(nodeId, GRAY);\n for (const neighbor of adj.get(nodeId) ?? []) {\n if (color.get(neighbor) === GRAY) {\n // Back edge found — reconstruct cycle\n const cycle = [neighbor, nodeId];\n let cur = nodeId;\n while (cur !== neighbor) {\n cur = parent.get(cur)!;\n if (cur) cycle.push(cur);\n else break;\n }\n return cycle.reverse();\n }\n if (color.get(neighbor) === WHITE) {\n parent.set(neighbor, nodeId);\n const result = dfs(neighbor);\n if (result) return result;\n }\n }\n color.set(nodeId, BLACK);\n return null;\n };\n\n for (const node of flow.nodes) {\n if (color.get(node.id) === WHITE) {\n const cycle = dfs(node.id);\n if (cycle) {\n throw new Error(`Flow contains a cycle: ${cycle.join(' → ')}. Only DAG flows are allowed.`);\n }\n }\n }\n }\n\n /**\n * Get the runtime type name of a value for schema validation.\n */\n private getValueType(value: unknown): string {\n if (Array.isArray(value)) return 'array';\n if (typeof value === 'object' && value !== null) return 'object';\n return typeof value;\n }\n\n /**\n * Validate node input schemas before execution.\n * Checks that node config matches declared inputSchema if present.\n */\n private validateNodeInputSchemas(flow: FlowParsed, _variables: Map<string, unknown>): void {\n for (const node of flow.nodes) {\n if (node.inputSchema && node.config) {\n for (const [paramName, paramDef] of Object.entries(node.inputSchema)) {\n if (paramDef.required && !(paramName in (node.config as Record<string, unknown>))) {\n throw new Error(\n `Node '${node.id}' missing required input parameter '${paramName}'`,\n );\n }\n const value = (node.config as Record<string, unknown>)[paramName];\n if (value !== undefined) {\n const actualType = this.getValueType(value);\n if (actualType !== paramDef.type) {\n throw new Error(\n `Node '${node.id}' parameter '${paramName}' expected type '${paramDef.type}' but got '${actualType}'`,\n );\n }\n }\n }\n }\n }\n }\n\n /**\n * Execute a node with timeout support, fault edge handling, and step logging.\n */\n private async executeNode(\n node: FlowNodeParsed,\n flow: FlowParsed,\n variables: Map<string, unknown>,\n context: AutomationContext,\n steps: StepLogEntry[],\n ): Promise<void> {\n if (node.type === 'end') return;\n\n const stepStart = Date.now();\n const stepStartedAt = new Date().toISOString();\n\n // Find executor\n const executor = this.nodeExecutors.get(node.type);\n if (!executor) {\n // start node without executor is fine — just skip\n if (node.type !== 'start') {\n steps.push({\n nodeId: node.id,\n nodeType: node.type,\n status: 'failure',\n startedAt: stepStartedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - stepStart,\n error: { code: 'NO_EXECUTOR', message: `No executor registered for node type '${node.type}'` },\n });\n throw new Error(`No executor registered for node type '${node.type}'`);\n }\n // Log start node step\n steps.push({\n nodeId: node.id,\n nodeType: node.type,\n status: 'success',\n startedAt: stepStartedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - stepStart,\n });\n } else {\n // Execute node with optional timeout\n let result: NodeExecutionResult;\n try {\n if (node.timeoutMs && node.timeoutMs > 0) {\n result = await this.executeWithTimeout(\n executor.execute(node, variables, context),\n node.timeoutMs,\n node.id,\n );\n } else {\n result = await executor.execute(node, variables, context);\n }\n } catch (execErr: unknown) {\n const errMsg = execErr instanceof Error ? execErr.message : String(execErr);\n steps.push({\n nodeId: node.id,\n nodeType: node.type,\n status: 'failure',\n startedAt: stepStartedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - stepStart,\n error: { code: 'EXECUTION_ERROR', message: errMsg },\n });\n\n // Check for fault edges\n const faultEdge = flow.edges.find(e => e.source === node.id && e.type === 'fault');\n if (faultEdge) {\n variables.set('$error', { nodeId: node.id, message: errMsg });\n const faultTarget = flow.nodes.find(n => n.id === faultEdge.target);\n if (faultTarget) {\n await this.executeNode(faultTarget, flow, variables, context, steps);\n return;\n }\n }\n throw execErr;\n }\n\n if (!result.success) {\n const errMsg = result.error ?? 'Unknown error';\n steps.push({\n nodeId: node.id,\n nodeType: node.type,\n status: 'failure',\n startedAt: stepStartedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - stepStart,\n error: { code: 'NODE_FAILURE', message: errMsg },\n });\n\n // Write error output to variable context for downstream nodes\n variables.set('$error', { nodeId: node.id, message: errMsg, output: result.output });\n\n // Check for fault edges\n const faultEdge = flow.edges.find(e => e.source === node.id && e.type === 'fault');\n if (faultEdge) {\n const faultTarget = flow.nodes.find(n => n.id === faultEdge.target);\n if (faultTarget) {\n await this.executeNode(faultTarget, flow, variables, context, steps);\n return;\n }\n }\n throw new Error(`Node '${node.id}' failed: ${errMsg}`);\n }\n\n // Log successful step\n steps.push({\n nodeId: node.id,\n nodeType: node.type,\n status: 'success',\n startedAt: stepStartedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - stepStart,\n });\n\n // Write back output variables\n if (result.output) {\n for (const [key, value] of Object.entries(result.output)) {\n variables.set(`${node.id}.${key}`, value);\n }\n }\n }\n\n // Find next nodes — separate conditional and unconditional edges\n const outEdges = flow.edges.filter(\n e => e.source === node.id && e.type !== 'fault',\n );\n\n const conditionalEdges: FlowEdgeParsed[] = [];\n const unconditionalEdges: FlowEdgeParsed[] = [];\n for (const edge of outEdges) {\n if (edge.condition) {\n conditionalEdges.push(edge);\n } else {\n unconditionalEdges.push(edge);\n }\n }\n\n // Conditional edges: evaluate sequentially (mutually exclusive)\n for (const edge of conditionalEdges) {\n if (this.evaluateCondition(edge.condition!, variables)) {\n const nextNode = flow.nodes.find(n => n.id === edge.target);\n if (nextNode) {\n await this.executeNode(nextNode, flow, variables, context, steps);\n }\n }\n }\n\n // Unconditional edges: execute in parallel (Promise.all)\n if (unconditionalEdges.length > 0) {\n const parallelTasks = unconditionalEdges\n .map(edge => flow.nodes.find(n => n.id === edge.target))\n .filter((n): n is FlowNodeParsed => n != null)\n .map(nextNode => this.executeNode(nextNode, flow, variables, context, steps));\n\n await Promise.all(parallelTasks);\n }\n }\n\n /**\n * Execute a promise with timeout using Promise.race.\n */\n private executeWithTimeout(\n promise: Promise<NodeExecutionResult>,\n timeoutMs: number,\n nodeId: string,\n ): Promise<NodeExecutionResult> {\n return Promise.race([\n promise,\n new Promise<NodeExecutionResult>((_, reject) =>\n setTimeout(() => reject(new Error(`Node '${nodeId}' timed out after ${timeoutMs}ms`)), timeoutMs),\n ),\n ]);\n }\n\n /**\n * Safe expression evaluator.\n * Uses simple operator-based parsing without `new Function`.\n * Supports: comparisons (>, <, >=, <=, ==, !=, ===, !==),\n * boolean literals (true, false), and basic arithmetic.\n */\n evaluateCondition(expression: string, variables: Map<string, unknown>): boolean {\n // Template replacement: {varName} → value\n let resolved = expression;\n for (const [key, value] of variables) {\n resolved = resolved.split(`{${key}}`).join(String(value));\n }\n resolved = resolved.trim();\n\n try {\n // Boolean literals\n if (resolved === 'true') return true;\n if (resolved === 'false') return false;\n\n // Comparison operators (ordered by length to match longer operators first)\n const operators = ['===', '!==', '>=', '<=', '!=', '==', '>', '<'] as const;\n for (const op of operators) {\n const idx = resolved.indexOf(op);\n if (idx !== -1) {\n const left = resolved.slice(0, idx).trim();\n const right = resolved.slice(idx + op.length).trim();\n return this.compareValues(left, op, right);\n }\n }\n\n // Numeric truthy check\n const numVal = Number(resolved);\n if (!isNaN(numVal)) return numVal !== 0;\n\n return false;\n } catch {\n return false;\n }\n }\n\n /**\n * Compare two string-represented values with an operator.\n */\n private compareValues(left: string, op: string, right: string): boolean {\n const lNum = Number(left);\n const rNum = Number(right);\n const bothNumeric = !isNaN(lNum) && !isNaN(rNum) && left !== '' && right !== '';\n\n if (bothNumeric) {\n switch (op) {\n case '>': return lNum > rNum;\n case '<': return lNum < rNum;\n case '>=': return lNum >= rNum;\n case '<=': return lNum <= rNum;\n case '==': case '===': return lNum === rNum;\n case '!=': case '!==': return lNum !== rNum;\n default: return false;\n }\n }\n // String comparison\n switch (op) {\n case '==': case '===': return left === right;\n case '!=': case '!==': return left !== right;\n case '>': return left > right;\n case '<': return left < right;\n case '>=': return left >= right;\n case '<=': return left <= right;\n default: return false;\n }\n }\n\n /**\n * Retry execution with exponential backoff, jitter, and recursive protection.\n * Uses an iterative loop with an internal retry flag to prevent recursive call stacking.\n */\n private async retryExecution(\n flowName: string,\n context: AutomationContext | undefined,\n startTime: number,\n errorHandling: {\n maxRetries?: number;\n retryDelayMs?: number;\n backoffMultiplier?: number;\n maxRetryDelayMs?: number;\n jitter?: boolean;\n },\n ): Promise<AutomationResult> {\n const maxRetries = errorHandling.maxRetries ?? 3;\n const baseDelay = errorHandling.retryDelayMs ?? 1000;\n const multiplier = errorHandling.backoffMultiplier ?? 1;\n const maxDelay = errorHandling.maxRetryDelayMs ?? 30000;\n const useJitter = errorHandling.jitter ?? false;\n\n let lastError = 'Max retries exceeded';\n for (let i = 0; i < maxRetries; i++) {\n // Calculate delay with exponential backoff\n let delay = Math.min(baseDelay * Math.pow(multiplier, i), maxDelay);\n if (useJitter) {\n delay = delay * (0.5 + Math.random() * 0.5);\n }\n await new Promise(r => setTimeout(r, delay));\n\n // Execute directly without recursion into retryExecution again\n const result = await this.executeWithoutRetry(flowName, context);\n if (result.success) return result;\n lastError = result.error ?? 'Unknown error';\n }\n return { success: false, error: lastError, durationMs: Date.now() - startTime };\n }\n\n /**\n * Execute a flow without triggering retry logic (used by retryExecution to prevent recursion).\n */\n private async executeWithoutRetry(\n flowName: string,\n context?: AutomationContext,\n ): Promise<AutomationResult> {\n const startTime = Date.now();\n const flow = this.flows.get(flowName);\n\n if (!flow) {\n return { success: false, error: `Flow '${flowName}' not found` };\n }\n if (this.flowEnabled.get(flowName) === false) {\n return { success: false, error: `Flow '${flowName}' is disabled` };\n }\n\n const variables = new Map<string, unknown>();\n if (flow.variables) {\n for (const v of flow.variables) {\n if (v.isInput && context?.params?.[v.name] !== undefined) {\n variables.set(v.name, context.params[v.name]);\n }\n }\n }\n if (context?.record) {\n variables.set('$record', context.record);\n }\n\n const runId = `run_${++this.runCounter}`;\n const startedAt = new Date().toISOString();\n const steps: StepLogEntry[] = [];\n\n try {\n const startNode = flow.nodes.find(n => n.type === 'start');\n if (!startNode) {\n return { success: false, error: 'Flow has no start node' };\n }\n\n await this.executeNode(startNode, flow, variables, context ?? {}, steps);\n\n const output: Record<string, unknown> = {};\n if (flow.variables) {\n for (const v of flow.variables) {\n if (v.isOutput) {\n output[v.name] = variables.get(v.name);\n }\n }\n }\n\n const durationMs = Date.now() - startTime;\n this.recordLog({\n id: runId,\n flowName,\n flowVersion: flow.version,\n status: 'completed',\n startedAt,\n completedAt: new Date().toISOString(),\n durationMs,\n trigger: {\n type: context?.event ?? 'manual',\n userId: context?.userId,\n object: context?.object,\n },\n steps,\n output,\n });\n\n return { success: true, output, durationMs };\n } catch (err: unknown) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n const durationMs = Date.now() - startTime;\n this.recordLog({\n id: runId,\n flowName,\n flowVersion: flow.version,\n status: 'failed',\n startedAt,\n completedAt: new Date().toISOString(),\n durationMs,\n trigger: {\n type: context?.event ?? 'manual',\n userId: context?.userId,\n object: context?.object,\n },\n steps,\n error: errorMessage,\n });\n return { success: false, error: errorMessage, durationMs };\n }\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport { AutomationEngine } from './engine.js';\n\n/**\n * Configuration options for the AutomationServicePlugin.\n */\nexport interface AutomationServicePluginOptions {\n /** Enable debug logging for flow execution */\n debug?: boolean;\n}\n\n/**\n * AutomationServicePlugin — Core engine plugin\n *\n * Responsibilities:\n * 1. init phase: Create engine instance, register as 'automation' service\n * 2. start phase: Trigger 'automation:ready' hook for node plugin registration\n * 3. destroy phase: Clean up resources\n *\n * Does NOT implement any specific nodes — nodes are registered by other plugins\n * via the engine's extension API.\n *\n * @example\n * ```ts\n * import { LiteKernel } from '@objectstack/core';\n * import { AutomationServicePlugin } from '@objectstack/service-automation';\n *\n * const kernel = new LiteKernel();\n * kernel.use(new AutomationServicePlugin());\n * await kernel.bootstrap();\n *\n * const automation = kernel.getService('automation');\n * ```\n */\nexport class AutomationServicePlugin implements Plugin {\n name = 'com.objectstack.service-automation';\n version = '1.0.0';\n type = 'standard' as const;\n dependencies: string[] = [];\n\n private engine?: AutomationEngine;\n private readonly options: AutomationServicePluginOptions;\n\n constructor(options: AutomationServicePluginOptions = {}) {\n this.options = options;\n }\n\n async init(ctx: PluginContext): Promise<void> {\n this.engine = new AutomationEngine(ctx.logger);\n\n // Register as global service — other plugins access via ctx.getService('automation')\n ctx.registerService('automation', this.engine);\n\n if (this.options.debug) {\n ctx.hook('automation:beforeExecute', async (flowName: string) => {\n ctx.logger.debug(`[Automation] Before execute: ${flowName}`);\n });\n }\n\n ctx.logger.info('[Automation] Engine initialized');\n }\n\n async start(ctx: PluginContext): Promise<void> {\n if (!this.engine) return;\n\n // Trigger hook to notify engine is ready — other plugins can start registering nodes\n await ctx.trigger('automation:ready', this.engine);\n\n const nodeTypes = this.engine.getRegisteredNodeTypes();\n ctx.logger.info(\n `[Automation] Engine started with ${nodeTypes.length} node types: ${nodeTypes.join(', ') || '(none)'}`,\n );\n }\n\n async destroy(): Promise<void> {\n this.engine = undefined;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport type { AutomationEngine } from '../engine.js';\n\n/**\n * CRUD Node Plugin — Provides get_record / create_record / update_record / delete_record\n *\n * Dependencies: service-automation (engine)\n *\n * In a full runtime environment these nodes would delegate to ObjectQL (data layer).\n * This MVP implementation provides the extension point structure.\n */\nexport class CrudNodesPlugin implements Plugin {\n name = 'com.objectstack.automation.crud-nodes';\n version = '1.0.0';\n type = 'standard' as const;\n dependencies = ['com.objectstack.service-automation'];\n\n async init(ctx: PluginContext): Promise<void> {\n const engine = ctx.getService<AutomationEngine>('automation');\n\n // get_record node executor\n engine.registerNodeExecutor({\n type: 'get_record',\n async execute(node, _variables, _context) {\n const config = node.config as Record<string, unknown> | undefined;\n // In production, this would query via ObjectQL:\n // const ql = ctx.getService('objectql');\n // const records = await ql.find(config.object, config.filters);\n return {\n success: true,\n output: { records: [], object: config?.object },\n };\n },\n });\n\n // create_record node executor\n engine.registerNodeExecutor({\n type: 'create_record',\n async execute(node, _variables, _context) {\n const config = node.config as Record<string, unknown> | undefined;\n return {\n success: true,\n output: { id: 'new-record-id', object: config?.object },\n };\n },\n });\n\n // update_record node executor\n engine.registerNodeExecutor({\n type: 'update_record',\n async execute(_node, _variables, _context) {\n return { success: true };\n },\n });\n\n // delete_record node executor\n engine.registerNodeExecutor({\n type: 'delete_record',\n async execute(_node, _variables, _context) {\n return { success: true };\n },\n });\n\n ctx.logger.info('[CRUD Nodes] 4 node executors registered');\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport type { AutomationEngine } from '../engine.js';\n\n/**\n * Logic Node Plugin — Provides decision / assignment / loop nodes\n *\n * Dependencies: service-automation (engine)\n */\nexport class LogicNodesPlugin implements Plugin {\n name = 'com.objectstack.automation.logic-nodes';\n version = '1.0.0';\n type = 'standard' as const;\n dependencies = ['com.objectstack.service-automation'];\n\n async init(ctx: PluginContext): Promise<void> {\n const engine = ctx.getService<AutomationEngine>('automation');\n\n // decision node — conditional branching\n engine.registerNodeExecutor({\n type: 'decision',\n async execute(node, variables, _context) {\n const config = node.config as Record<string, unknown> | undefined;\n const conditions = (config?.conditions ?? []) as Array<{ label: string; expression: string }>;\n\n for (const cond of conditions) {\n if (engine.evaluateCondition(cond.expression, variables)) {\n return { success: true, branchLabel: cond.label };\n }\n }\n return { success: true, branchLabel: 'default' };\n },\n });\n\n // assignment node — set variables\n engine.registerNodeExecutor({\n type: 'assignment',\n async execute(node, variables, _context) {\n const config = (node.config ?? {}) as Record<string, unknown>;\n for (const [key, value] of Object.entries(config)) {\n variables.set(key, value);\n }\n return { success: true };\n },\n });\n\n // loop node — iterate over a collection\n engine.registerNodeExecutor({\n type: 'loop',\n async execute(node, variables, _context) {\n const config = node.config as Record<string, unknown> | undefined;\n const collectionName = config?.collection as string | undefined;\n if (collectionName) {\n const collection = variables.get(collectionName);\n if (Array.isArray(collection)) {\n variables.set('$loopItems', collection);\n variables.set('$loopIndex', 0);\n }\n }\n return { success: true };\n },\n });\n\n ctx.logger.info('[Logic Nodes] 3 node executors registered');\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport type { AutomationEngine } from '../engine.js';\n\n/**\n * HTTP + Connector Node Plugin — Provides http_request / connector_action nodes\n *\n * Dependencies: service-automation (engine)\n */\nexport class HttpConnectorPlugin implements Plugin {\n name = 'com.objectstack.automation.http-connector';\n version = '1.0.0';\n type = 'standard' as const;\n dependencies = ['com.objectstack.service-automation'];\n\n async init(ctx: PluginContext): Promise<void> {\n const engine = ctx.getService<AutomationEngine>('automation');\n\n // http_request node executor\n engine.registerNodeExecutor({\n type: 'http_request',\n async execute(node, _variables, _context) {\n const config = node.config as Record<string, unknown> | undefined;\n const url = config?.url as string | undefined;\n const method = (config?.method as string) ?? 'GET';\n const headers = config?.headers as Record<string, string> | undefined;\n const body = config?.body;\n\n if (!url) {\n return { success: false, error: 'http_request: url is required' };\n }\n\n const response = await fetch(url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n const data = await response.json();\n\n return {\n success: response.ok,\n output: { response: data, status: response.status },\n error: response.ok ? undefined : `HTTP ${response.status}`,\n };\n },\n });\n\n // connector_action node — calls a registered connector\n engine.registerNodeExecutor({\n type: 'connector_action',\n async execute(node, _variables, _context) {\n const connectorConfig = node.connectorConfig;\n if (!connectorConfig) {\n return { success: false, error: 'connector_action: connectorConfig is required' };\n }\n\n ctx.logger.info(\n `Connector action: ${connectorConfig.connectorId}.${connectorConfig.actionId}`,\n );\n\n // In production, this would look up the connector from a registry\n // and execute the specified action with the mapped inputs\n return { success: true, output: { connectorResult: {} } };\n },\n });\n\n ctx.logger.info('[HTTP Connector] 2 node executors registered');\n }\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/engine.ts","../src/plugin.ts","../src/plugins/template.ts","../src/plugins/crud-nodes-plugin.ts","../src/plugins/logic-nodes-plugin.ts","../src/plugins/http-connector-plugin.ts","../src/plugins/screen-nodes-plugin.ts"],"sourcesContent":["// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n// Core engine\nexport { AutomationEngine } from './engine.js';\nexport type { NodeExecutor, NodeExecutionResult, FlowTrigger } from './engine.js';\n\n// Kernel plugin\nexport { AutomationServicePlugin } from './plugin.js';\nexport type { AutomationServicePluginOptions } from './plugin.js';\n\n// Node plugins\nexport { CrudNodesPlugin } from './plugins/crud-nodes-plugin.js';\nexport { LogicNodesPlugin } from './plugins/logic-nodes-plugin.js';\nexport { HttpConnectorPlugin } from './plugins/http-connector-plugin.js';\nexport { ScreenNodesPlugin } from './plugins/screen-nodes-plugin.js';\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { FlowParsed, FlowNodeParsed, FlowEdgeParsed } from '@objectstack/spec/automation';\nimport type { ExecutionLog } from '@objectstack/spec/automation';\nimport type { AutomationContext, AutomationResult, IAutomationService } from '@objectstack/spec/contracts';\nimport type { Logger } from '@objectstack/spec/contracts';\nimport { FlowSchema } from '@objectstack/spec/automation';\n\n// ─── Node Executor Interface (Plugin Extension Point) ───────────────\n\n/**\n * Each node type corresponds to a NodeExecutor.\n * Third-party plugins only need to implement this interface and register\n * it with the engine to extend automation capabilities.\n */\nexport interface NodeExecutor {\n /** Corresponds to FlowNodeAction enum value */\n readonly type: string;\n\n /**\n * Execute a node\n * @param node - Current node definition\n * @param variables - Flow variable context (read/write)\n * @param context - Trigger context\n * @returns Execution result (may include output data, branch conditions, etc.)\n */\n execute(\n node: FlowNodeParsed,\n variables: Map<string, unknown>,\n context: AutomationContext,\n ): Promise<NodeExecutionResult>;\n}\n\nexport interface NodeExecutionResult {\n success: boolean;\n output?: Record<string, unknown>;\n error?: string;\n /** Used by decision nodes — returns the selected branch label */\n branchLabel?: string;\n}\n\n// ─── Trigger Interface (Plugin Extension Point) ─────────────────────\n\n/**\n * Trigger interface. Schedule/Event/API triggers are registered via plugins.\n */\nexport interface FlowTrigger {\n readonly type: string;\n start(flowName: string, callback: (ctx: AutomationContext) => Promise<void>): void;\n stop(flowName: string): void;\n}\n\n// ─── Core Automation Engine ─────────────────────────────────────────\n\n/**\n * Internal execution step log entry.\n */\ninterface StepLogEntry {\n nodeId: string;\n nodeType: string;\n nodeLabel?: string;\n status: 'success' | 'failure' | 'skipped';\n startedAt: string;\n completedAt?: string;\n durationMs?: number;\n error?: { code: string; message: string; stack?: string };\n}\n\n/**\n * Internal execution log entry — compatible with ExecutionLog from spec.\n */\ninterface ExecutionLogEntry {\n id: string;\n flowName: string;\n flowVersion?: number;\n status: ExecutionLog['status'];\n startedAt: string;\n completedAt?: string;\n durationMs?: number;\n trigger: { type: string; userId?: string; object?: string; recordId?: string };\n steps: StepLogEntry[];\n variables?: Record<string, unknown>;\n output?: unknown;\n error?: string;\n}\n\nexport class AutomationEngine implements IAutomationService {\n private flows = new Map<string, FlowParsed>();\n private flowEnabled = new Map<string, boolean>();\n private flowVersionHistory = new Map<string, Array<{ version: number; definition: FlowParsed; createdAt: string }>>();\n private nodeExecutors = new Map<string, NodeExecutor>();\n private triggers = new Map<string, FlowTrigger>();\n private executionLogs: ExecutionLogEntry[] = [];\n private maxLogSize = 1000;\n private logger: Logger;\n private runCounter = 0;\n\n constructor(logger: Logger) {\n this.logger = logger;\n }\n\n // ── Plugin Extension API ──────────────────────────────\n\n /** Register a node executor (called by plugins) */\n registerNodeExecutor(executor: NodeExecutor): void {\n if (this.nodeExecutors.has(executor.type)) {\n this.logger.warn(`Node executor '${executor.type}' replaced`);\n }\n this.nodeExecutors.set(executor.type, executor);\n this.logger.info(`Node executor registered: ${executor.type}`);\n }\n\n /** Unregister a node executor (hot-unplug) */\n unregisterNodeExecutor(type: string): void {\n this.nodeExecutors.delete(type);\n this.logger.info(`Node executor unregistered: ${type}`);\n }\n\n /** Register a trigger (called by plugins) */\n registerTrigger(trigger: FlowTrigger): void {\n this.triggers.set(trigger.type, trigger);\n this.logger.info(`Trigger registered: ${trigger.type}`);\n }\n\n /** Unregister a trigger (hot-unplug) */\n unregisterTrigger(type: string): void {\n this.triggers.delete(type);\n this.logger.info(`Trigger unregistered: ${type}`);\n }\n\n /** Get all registered node types */\n getRegisteredNodeTypes(): string[] {\n return [...this.nodeExecutors.keys()];\n }\n\n /** Get all registered trigger types */\n getRegisteredTriggerTypes(): string[] {\n return [...this.triggers.keys()];\n }\n\n // ── IAutomationService Contract Implementation ────────\n\n registerFlow(name: string, definition: unknown): void {\n const parsed = FlowSchema.parse(definition);\n\n // DAG cycle detection\n this.detectCycles(parsed);\n\n // Version history management\n const history = this.flowVersionHistory.get(name) ?? [];\n history.push({\n version: parsed.version,\n definition: parsed,\n createdAt: new Date().toISOString(),\n });\n this.flowVersionHistory.set(name, history);\n\n this.flows.set(name, parsed);\n if (!this.flowEnabled.has(name)) {\n this.flowEnabled.set(name, true);\n }\n this.logger.info(`Flow registered: ${name} (version ${parsed.version})`);\n }\n\n unregisterFlow(name: string): void {\n this.flows.delete(name);\n this.flowEnabled.delete(name);\n this.flowVersionHistory.delete(name);\n this.logger.info(`Flow unregistered: ${name}`);\n }\n\n async listFlows(): Promise<string[]> {\n return [...this.flows.keys()];\n }\n\n async getFlow(name: string): Promise<FlowParsed | null> {\n return this.flows.get(name) ?? null;\n }\n\n async toggleFlow(name: string, enabled: boolean): Promise<void> {\n if (!this.flows.has(name)) {\n throw new Error(`Flow '${name}' not found`);\n }\n this.flowEnabled.set(name, enabled);\n this.logger.info(`Flow '${name}' ${enabled ? 'enabled' : 'disabled'}`);\n }\n\n /** Get flow version history */\n getFlowVersionHistory(name: string): Array<{ version: number; definition: FlowParsed; createdAt: string }> {\n return this.flowVersionHistory.get(name) ?? [];\n }\n\n /** Rollback flow to a specific version */\n rollbackFlow(name: string, version: number): void {\n const history = this.flowVersionHistory.get(name);\n if (!history) {\n throw new Error(`Flow '${name}' has no version history`);\n }\n const entry = history.find(h => h.version === version);\n if (!entry) {\n throw new Error(`Version ${version} not found for flow '${name}'`);\n }\n this.flows.set(name, entry.definition);\n this.logger.info(`Flow '${name}' rolled back to version ${version}`);\n }\n\n async listRuns(flowName: string, options?: { limit?: number; cursor?: string }): Promise<ExecutionLogEntry[]> {\n const limit = options?.limit ?? 20;\n const logs = this.executionLogs.filter(l => l.flowName === flowName);\n return logs.slice(-limit).reverse();\n }\n\n async getRun(runId: string): Promise<ExecutionLogEntry | null> {\n return this.executionLogs.find(l => l.id === runId) ?? null;\n }\n\n async execute(flowName: string, context?: AutomationContext): Promise<AutomationResult> {\n const startTime = Date.now();\n const flow = this.flows.get(flowName);\n\n if (!flow) {\n return { success: false, error: `Flow '${flowName}' not found` };\n }\n\n // Check if flow is disabled\n if (this.flowEnabled.get(flowName) === false) {\n return { success: false, error: `Flow '${flowName}' is disabled` };\n }\n\n // Initialize variable context\n const variables = new Map<string, unknown>();\n if (flow.variables) {\n for (const v of flow.variables) {\n if (v.isInput && context?.params?.[v.name] !== undefined) {\n variables.set(v.name, context.params[v.name]);\n }\n }\n }\n // Inject trigger record\n if (context?.record) {\n variables.set('$record', context.record);\n }\n\n const runId = `run_${++this.runCounter}`;\n const startedAt = new Date().toISOString();\n const steps: StepLogEntry[] = [];\n\n try {\n // Find the start node\n const startNode = flow.nodes.find(n => n.type === 'start');\n if (!startNode) {\n return { success: false, error: 'Flow has no start node' };\n }\n\n // Validate node input schemas before execution\n this.validateNodeInputSchemas(flow, variables);\n\n // DAG traversal execution\n await this.executeNode(startNode, flow, variables, context ?? {}, steps);\n\n // Collect output variables\n const output: Record<string, unknown> = {};\n if (flow.variables) {\n for (const v of flow.variables) {\n if (v.isOutput) {\n output[v.name] = variables.get(v.name);\n }\n }\n }\n\n const durationMs = Date.now() - startTime;\n\n // Record execution log\n this.recordLog({\n id: runId,\n flowName,\n flowVersion: flow.version,\n status: 'completed',\n startedAt,\n completedAt: new Date().toISOString(),\n durationMs,\n trigger: {\n type: context?.event ?? 'manual',\n userId: context?.userId,\n object: context?.object,\n },\n steps,\n output,\n });\n\n return {\n success: true,\n output,\n durationMs,\n };\n } catch (err: unknown) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n\n // Record failed execution log\n const durationMs = Date.now() - startTime;\n this.recordLog({\n id: runId,\n flowName,\n flowVersion: flow.version,\n status: 'failed',\n startedAt,\n completedAt: new Date().toISOString(),\n durationMs,\n trigger: {\n type: context?.event ?? 'manual',\n userId: context?.userId,\n object: context?.object,\n },\n steps,\n error: errorMessage,\n });\n\n // Error handling strategy\n if (flow.errorHandling?.strategy === 'retry') {\n return this.retryExecution(flowName, context, startTime, flow.errorHandling);\n }\n return {\n success: false,\n error: errorMessage,\n durationMs,\n };\n }\n }\n\n // ── DAG Traversal Core ──────────────────────────────────\n\n private recordLog(entry: ExecutionLogEntry): void {\n this.executionLogs.push(entry);\n // Evict oldest logs when exceeding max size\n if (this.executionLogs.length > this.maxLogSize) {\n this.executionLogs.splice(0, this.executionLogs.length - this.maxLogSize);\n }\n }\n\n /**\n * Detect cycles in the flow graph (DAG validation).\n * Uses DFS with coloring (white/gray/black) to detect back edges.\n * Throws an error with cycle details if a cycle is found.\n */\n private detectCycles(flow: FlowParsed): void {\n const WHITE = 0, GRAY = 1, BLACK = 2;\n const color = new Map<string, number>();\n const parent = new Map<string, string>();\n\n // Build adjacency list from edges\n const adj = new Map<string, string[]>();\n for (const node of flow.nodes) {\n color.set(node.id, WHITE);\n adj.set(node.id, []);\n }\n for (const edge of flow.edges) {\n const targets = adj.get(edge.source);\n if (targets) targets.push(edge.target);\n }\n\n const dfs = (nodeId: string): string[] | null => {\n color.set(nodeId, GRAY);\n for (const neighbor of adj.get(nodeId) ?? []) {\n if (color.get(neighbor) === GRAY) {\n // Back edge found — reconstruct cycle\n const cycle = [neighbor, nodeId];\n let cur = nodeId;\n while (cur !== neighbor) {\n cur = parent.get(cur)!;\n if (cur) cycle.push(cur);\n else break;\n }\n return cycle.reverse();\n }\n if (color.get(neighbor) === WHITE) {\n parent.set(neighbor, nodeId);\n const result = dfs(neighbor);\n if (result) return result;\n }\n }\n color.set(nodeId, BLACK);\n return null;\n };\n\n for (const node of flow.nodes) {\n if (color.get(node.id) === WHITE) {\n const cycle = dfs(node.id);\n if (cycle) {\n throw new Error(`Flow contains a cycle: ${cycle.join(' → ')}. Only DAG flows are allowed.`);\n }\n }\n }\n }\n\n /**\n * Get the runtime type name of a value for schema validation.\n */\n private getValueType(value: unknown): string {\n if (Array.isArray(value)) return 'array';\n if (typeof value === 'object' && value !== null) return 'object';\n return typeof value;\n }\n\n /**\n * Validate node input schemas before execution.\n * Checks that node config matches declared inputSchema if present.\n */\n private validateNodeInputSchemas(flow: FlowParsed, _variables: Map<string, unknown>): void {\n for (const node of flow.nodes) {\n if (node.inputSchema && node.config) {\n for (const [paramName, paramDef] of Object.entries(node.inputSchema)) {\n if (paramDef.required && !(paramName in (node.config as Record<string, unknown>))) {\n throw new Error(\n `Node '${node.id}' missing required input parameter '${paramName}'`,\n );\n }\n const value = (node.config as Record<string, unknown>)[paramName];\n if (value !== undefined) {\n const actualType = this.getValueType(value);\n if (actualType !== paramDef.type) {\n throw new Error(\n `Node '${node.id}' parameter '${paramName}' expected type '${paramDef.type}' but got '${actualType}'`,\n );\n }\n }\n }\n }\n }\n }\n\n /**\n * Execute a node with timeout support, fault edge handling, and step logging.\n */\n private async executeNode(\n node: FlowNodeParsed,\n flow: FlowParsed,\n variables: Map<string, unknown>,\n context: AutomationContext,\n steps: StepLogEntry[],\n ): Promise<void> {\n if (node.type === 'end') return;\n\n const stepStart = Date.now();\n const stepStartedAt = new Date().toISOString();\n\n // Find executor\n const executor = this.nodeExecutors.get(node.type);\n if (!executor) {\n // start node without executor is fine — just skip\n if (node.type !== 'start') {\n steps.push({\n nodeId: node.id,\n nodeType: node.type,\n status: 'failure',\n startedAt: stepStartedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - stepStart,\n error: { code: 'NO_EXECUTOR', message: `No executor registered for node type '${node.type}'` },\n });\n throw new Error(`No executor registered for node type '${node.type}'`);\n }\n // Log start node step\n steps.push({\n nodeId: node.id,\n nodeType: node.type,\n status: 'success',\n startedAt: stepStartedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - stepStart,\n });\n } else {\n // Execute node with optional timeout\n let result: NodeExecutionResult;\n try {\n if (node.timeoutMs && node.timeoutMs > 0) {\n result = await this.executeWithTimeout(\n executor.execute(node, variables, context),\n node.timeoutMs,\n node.id,\n );\n } else {\n result = await executor.execute(node, variables, context);\n }\n } catch (execErr: unknown) {\n const errMsg = execErr instanceof Error ? execErr.message : String(execErr);\n steps.push({\n nodeId: node.id,\n nodeType: node.type,\n status: 'failure',\n startedAt: stepStartedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - stepStart,\n error: { code: 'EXECUTION_ERROR', message: errMsg },\n });\n\n // Check for fault edges\n const faultEdge = flow.edges.find(e => e.source === node.id && e.type === 'fault');\n if (faultEdge) {\n variables.set('$error', { nodeId: node.id, message: errMsg });\n const faultTarget = flow.nodes.find(n => n.id === faultEdge.target);\n if (faultTarget) {\n await this.executeNode(faultTarget, flow, variables, context, steps);\n return;\n }\n }\n throw execErr;\n }\n\n if (!result.success) {\n const errMsg = result.error ?? 'Unknown error';\n steps.push({\n nodeId: node.id,\n nodeType: node.type,\n status: 'failure',\n startedAt: stepStartedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - stepStart,\n error: { code: 'NODE_FAILURE', message: errMsg },\n });\n\n // Write error output to variable context for downstream nodes\n variables.set('$error', { nodeId: node.id, message: errMsg, output: result.output });\n\n // Check for fault edges\n const faultEdge = flow.edges.find(e => e.source === node.id && e.type === 'fault');\n if (faultEdge) {\n const faultTarget = flow.nodes.find(n => n.id === faultEdge.target);\n if (faultTarget) {\n await this.executeNode(faultTarget, flow, variables, context, steps);\n return;\n }\n }\n throw new Error(`Node '${node.id}' failed: ${errMsg}`);\n }\n\n // Log successful step\n steps.push({\n nodeId: node.id,\n nodeType: node.type,\n status: 'success',\n startedAt: stepStartedAt,\n completedAt: new Date().toISOString(),\n durationMs: Date.now() - stepStart,\n });\n\n // Write back output variables\n if (result.output) {\n for (const [key, value] of Object.entries(result.output)) {\n variables.set(`${node.id}.${key}`, value);\n }\n }\n }\n\n // Find next nodes — separate conditional and unconditional edges\n const outEdges = flow.edges.filter(\n e => e.source === node.id && e.type !== 'fault',\n );\n\n const conditionalEdges: FlowEdgeParsed[] = [];\n const unconditionalEdges: FlowEdgeParsed[] = [];\n for (const edge of outEdges) {\n if (edge.condition) {\n conditionalEdges.push(edge);\n } else {\n unconditionalEdges.push(edge);\n }\n }\n\n // Conditional edges: evaluate sequentially (mutually exclusive)\n for (const edge of conditionalEdges) {\n if (this.evaluateCondition(edge.condition!, variables)) {\n const nextNode = flow.nodes.find(n => n.id === edge.target);\n if (nextNode) {\n await this.executeNode(nextNode, flow, variables, context, steps);\n }\n }\n }\n\n // Unconditional edges: execute in parallel (Promise.all)\n if (unconditionalEdges.length > 0) {\n const parallelTasks = unconditionalEdges\n .map(edge => flow.nodes.find(n => n.id === edge.target))\n .filter((n): n is FlowNodeParsed => n != null)\n .map(nextNode => this.executeNode(nextNode, flow, variables, context, steps));\n\n await Promise.all(parallelTasks);\n }\n }\n\n /**\n * Execute a promise with timeout using Promise.race.\n */\n private executeWithTimeout(\n promise: Promise<NodeExecutionResult>,\n timeoutMs: number,\n nodeId: string,\n ): Promise<NodeExecutionResult> {\n return Promise.race([\n promise,\n new Promise<NodeExecutionResult>((_, reject) =>\n setTimeout(() => reject(new Error(`Node '${nodeId}' timed out after ${timeoutMs}ms`)), timeoutMs),\n ),\n ]);\n }\n\n /**\n * Safe expression evaluator.\n * Uses simple operator-based parsing without `new Function`.\n * Supports: comparisons (>, <, >=, <=, ==, !=, ===, !==),\n * boolean literals (true, false), and basic arithmetic.\n */\n evaluateCondition(expression: string | { dialect?: string; source?: string; ast?: unknown }, variables: Map<string, unknown>): boolean {\n // M9.5+ wiring: route Expression envelopes through @objectstack/formula\n // ExpressionEngine. CEL is the default; legacy `{var}` template syntax\n // is preserved as a fallback for back-compat.\n const isEnvelope = typeof expression === 'object' && expression != null && 'dialect' in expression;\n const dialect = isEnvelope ? (expression as { dialect?: string }).dialect : undefined;\n const exprStr = typeof expression === 'string' ? expression : ((expression as { source?: string })?.source ?? '');\n\n if (isEnvelope && dialect && dialect !== 'cel' && dialect !== 'flow' && dialect !== 'template') {\n // Other dialects (cron, js) are not boolean predicates here.\n return false;\n }\n\n // CEL path — bind `vars` scope for `{step.result}` style references via\n // the equivalent `vars.step.result` CEL identifier path.\n if (dialect === 'cel' || (isEnvelope && !dialect)) {\n try {\n // eslint-disable-next-line @typescript-eslint/no-require-imports\n const { ExpressionEngine } = require('@objectstack/formula') as typeof import('@objectstack/formula');\n const vars: Record<string, unknown> = {};\n for (const [key, value] of variables) {\n // Convert \"step.result\" keys into nested object paths.\n const segs = key.split('.');\n let cursor = vars;\n for (let i = 0; i < segs.length - 1; i++) {\n if (typeof cursor[segs[i]] !== 'object' || cursor[segs[i]] === null) {\n cursor[segs[i]] = {};\n }\n cursor = cursor[segs[i]] as Record<string, unknown>;\n }\n cursor[segs[segs.length - 1]] = value;\n }\n const result = ExpressionEngine.evaluate(\n { dialect: 'cel', source: exprStr },\n { extra: { vars }, record: vars },\n );\n if (!result.ok) return false;\n return Boolean(result.value);\n } catch {\n return false;\n }\n }\n\n // Legacy template path: {varName} → value, then primitive compare.\n let resolved = exprStr;\n for (const [key, value] of variables) {\n resolved = resolved.split(`{${key}}`).join(String(value));\n }\n resolved = resolved.trim();\n\n try {\n // Boolean literals\n if (resolved === 'true') return true;\n if (resolved === 'false') return false;\n\n // Comparison operators (ordered by length to match longer operators first)\n const operators = ['===', '!==', '>=', '<=', '!=', '==', '>', '<'] as const;\n for (const op of operators) {\n const idx = resolved.indexOf(op);\n if (idx !== -1) {\n const left = resolved.slice(0, idx).trim();\n const right = resolved.slice(idx + op.length).trim();\n return this.compareValues(left, op, right);\n }\n }\n\n // Numeric truthy check\n const numVal = Number(resolved);\n if (!isNaN(numVal)) return numVal !== 0;\n\n return false;\n } catch {\n return false;\n }\n }\n\n /**\n * Compare two string-represented values with an operator.\n */\n private compareValues(left: string, op: string, right: string): boolean {\n const lNum = Number(left);\n const rNum = Number(right);\n const bothNumeric = !isNaN(lNum) && !isNaN(rNum) && left !== '' && right !== '';\n\n if (bothNumeric) {\n switch (op) {\n case '>': return lNum > rNum;\n case '<': return lNum < rNum;\n case '>=': return lNum >= rNum;\n case '<=': return lNum <= rNum;\n case '==': case '===': return lNum === rNum;\n case '!=': case '!==': return lNum !== rNum;\n default: return false;\n }\n }\n // String comparison\n switch (op) {\n case '==': case '===': return left === right;\n case '!=': case '!==': return left !== right;\n case '>': return left > right;\n case '<': return left < right;\n case '>=': return left >= right;\n case '<=': return left <= right;\n default: return false;\n }\n }\n\n /**\n * Retry execution with exponential backoff, jitter, and recursive protection.\n * Uses an iterative loop with an internal retry flag to prevent recursive call stacking.\n */\n private async retryExecution(\n flowName: string,\n context: AutomationContext | undefined,\n startTime: number,\n errorHandling: {\n maxRetries?: number;\n retryDelayMs?: number;\n backoffMultiplier?: number;\n maxRetryDelayMs?: number;\n jitter?: boolean;\n },\n ): Promise<AutomationResult> {\n const maxRetries = errorHandling.maxRetries ?? 3;\n const baseDelay = errorHandling.retryDelayMs ?? 1000;\n const multiplier = errorHandling.backoffMultiplier ?? 1;\n const maxDelay = errorHandling.maxRetryDelayMs ?? 30000;\n const useJitter = errorHandling.jitter ?? false;\n\n let lastError = 'Max retries exceeded';\n for (let i = 0; i < maxRetries; i++) {\n // Calculate delay with exponential backoff\n let delay = Math.min(baseDelay * Math.pow(multiplier, i), maxDelay);\n if (useJitter) {\n delay = delay * (0.5 + Math.random() * 0.5);\n }\n await new Promise(r => setTimeout(r, delay));\n\n // Execute directly without recursion into retryExecution again\n const result = await this.executeWithoutRetry(flowName, context);\n if (result.success) return result;\n lastError = result.error ?? 'Unknown error';\n }\n return { success: false, error: lastError, durationMs: Date.now() - startTime };\n }\n\n /**\n * Execute a flow without triggering retry logic (used by retryExecution to prevent recursion).\n */\n private async executeWithoutRetry(\n flowName: string,\n context?: AutomationContext,\n ): Promise<AutomationResult> {\n const startTime = Date.now();\n const flow = this.flows.get(flowName);\n\n if (!flow) {\n return { success: false, error: `Flow '${flowName}' not found` };\n }\n if (this.flowEnabled.get(flowName) === false) {\n return { success: false, error: `Flow '${flowName}' is disabled` };\n }\n\n const variables = new Map<string, unknown>();\n if (flow.variables) {\n for (const v of flow.variables) {\n if (v.isInput && context?.params?.[v.name] !== undefined) {\n variables.set(v.name, context.params[v.name]);\n }\n }\n }\n if (context?.record) {\n variables.set('$record', context.record);\n }\n\n const runId = `run_${++this.runCounter}`;\n const startedAt = new Date().toISOString();\n const steps: StepLogEntry[] = [];\n\n try {\n const startNode = flow.nodes.find(n => n.type === 'start');\n if (!startNode) {\n return { success: false, error: 'Flow has no start node' };\n }\n\n await this.executeNode(startNode, flow, variables, context ?? {}, steps);\n\n const output: Record<string, unknown> = {};\n if (flow.variables) {\n for (const v of flow.variables) {\n if (v.isOutput) {\n output[v.name] = variables.get(v.name);\n }\n }\n }\n\n const durationMs = Date.now() - startTime;\n this.recordLog({\n id: runId,\n flowName,\n flowVersion: flow.version,\n status: 'completed',\n startedAt,\n completedAt: new Date().toISOString(),\n durationMs,\n trigger: {\n type: context?.event ?? 'manual',\n userId: context?.userId,\n object: context?.object,\n },\n steps,\n output,\n });\n\n return { success: true, output, durationMs };\n } catch (err: unknown) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n const durationMs = Date.now() - startTime;\n this.recordLog({\n id: runId,\n flowName,\n flowVersion: flow.version,\n status: 'failed',\n startedAt,\n completedAt: new Date().toISOString(),\n durationMs,\n trigger: {\n type: context?.event ?? 'manual',\n userId: context?.userId,\n object: context?.object,\n },\n steps,\n error: errorMessage,\n });\n return { success: false, error: errorMessage, durationMs };\n }\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport { AutomationEngine } from './engine.js';\n\n/**\n * Configuration options for the AutomationServicePlugin.\n */\nexport interface AutomationServicePluginOptions {\n /** Enable debug logging for flow execution */\n debug?: boolean;\n}\n\n/**\n * AutomationServicePlugin — Core engine plugin\n *\n * Responsibilities:\n * 1. init phase: Create engine instance, register as 'automation' service\n * 2. start phase: Trigger 'automation:ready' hook for node plugin registration,\n * then pull flow definitions from the ObjectQL schema registry and register\n * them with the engine.\n * 3. destroy phase: Clean up resources\n *\n * Does NOT implement any specific nodes — nodes are registered by other plugins\n * via the engine's extension API.\n *\n * @example\n * ```ts\n * import { LiteKernel } from '@objectstack/core';\n * import { AutomationServicePlugin } from '@objectstack/service-automation';\n *\n * const kernel = new LiteKernel();\n * kernel.use(new AutomationServicePlugin());\n * await kernel.bootstrap();\n *\n * const automation = kernel.getService('automation');\n * ```\n */\nexport class AutomationServicePlugin implements Plugin {\n name = 'com.objectstack.service-automation';\n version = '1.0.0';\n type = 'standard' as const;\n // Soft dependency on metadata: we look it up at start() and tolerate absence.\n // Do NOT declare a hard kernel dependency, so this plugin works in environments\n // where MetadataPlugin is not registered.\n dependencies: string[] = [];\n\n private engine?: AutomationEngine;\n private readonly options: AutomationServicePluginOptions;\n\n constructor(options: AutomationServicePluginOptions = {}) {\n this.options = options;\n }\n\n async init(ctx: PluginContext): Promise<void> {\n this.engine = new AutomationEngine(ctx.logger);\n\n // Register as global service — other plugins access via ctx.getService('automation')\n ctx.registerService('automation', this.engine);\n\n if (this.options.debug) {\n ctx.hook('automation:beforeExecute', async (flowName: string) => {\n ctx.logger.debug(`[Automation] Before execute: ${flowName}`);\n });\n }\n\n ctx.logger.info('[Automation] Engine initialized');\n }\n\n async start(ctx: PluginContext): Promise<void> {\n console.warn('[Automation:start] entering start()');\n if (!this.engine) {\n console.warn('[Automation:start] engine missing, bailing');\n return;\n }\n\n // Trigger hook to notify engine is ready — other plugins can start registering nodes\n await ctx.trigger('automation:ready', this.engine);\n\n const nodeTypes = this.engine.getRegisteredNodeTypes();\n ctx.logger.info(\n `[Automation] Engine started with ${nodeTypes.length} node types: ${nodeTypes.join(', ') || '(none)'}`,\n );\n\n // Pull flow definitions from the ObjectQL schema registry. AppPlugin.init()\n // calls manifest.register(payload), which routes to ql.registerApp() and\n // stores each inline flow under type 'flow'. By the time start() runs,\n // every init() phase has completed, so the registry is fully populated.\n try {\n const ql = ctx.getService<{\n registry?: { listItems?: (type: string) => unknown[] };\n }>('objectql');\n if (!ql) {\n console.warn('[Automation] objectql service not found at start()');\n } else if (!ql.registry) {\n console.warn('[Automation] objectql.registry is undefined at start()');\n } else if (typeof ql.registry.listItems !== 'function') {\n console.warn('[Automation] objectql.registry.listItems is not a function');\n }\n const flows = ql?.registry?.listItems?.('flow') ?? [];\n console.warn(`[Automation] flow pull: registry returned ${flows.length} flow(s)`);\n let registered = 0;\n for (const f of flows) {\n const def = f as { name?: string };\n if (!def?.name) continue;\n try {\n this.engine.registerFlow(def.name, def as never);\n registered++;\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n ctx.logger.warn(`[Automation] failed to register flow ${def.name}: ${msg}`);\n }\n }\n if (registered > 0) {\n ctx.logger.info(`[Automation] Pulled ${registered} flow(s) from ObjectQL registry`);\n }\n } catch (err) {\n const msg = err instanceof Error ? err.message : String(err);\n ctx.logger.warn(`[Automation] flow pull from ObjectQL registry failed: ${msg}`);\n }\n }\n\n async destroy(): Promise<void> {\n this.engine = undefined;\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\n/**\n * Template interpolation helpers shared across node executors.\n *\n * Supported syntax (intentionally minimal — no full expression language):\n *\n * {variable} → variables.get('variable')\n * {variable.path.segment} → walks dotted path on the resolved value\n * {$User.Id} → reads from context.userId\n * {$User.Email} → reads from context.user?.email\n * {NOW()} → ISO timestamp at evaluation time\n * {TODAY()} → YYYY-MM-DD at evaluation time\n * {TODAY() + 90} → date + N days (days only, integer)\n *\n * Anything that fails to resolve becomes the literal `null` value (for\n * single-token templates) or the empty string (for embedded substitution),\n * matching the behavior of common low-code formula engines.\n *\n * The interpolator walks objects, arrays, and primitives recursively so it\n * can be applied wholesale to a node's `config.fields`/`config.filter` blocks.\n */\n\nimport type { AutomationContext } from '@objectstack/spec/contracts';\n\nexport type VariableMap = Map<string, unknown>;\n\n/**\n * Resolve a dotted path against a base value.\n * Returns `undefined` for any missing intermediate node.\n */\nfunction resolvePath(base: unknown, path: string[]): unknown {\n let cur: unknown = base;\n for (const seg of path) {\n if (cur == null) return undefined;\n if (typeof cur !== 'object') return undefined;\n cur = (cur as Record<string, unknown>)[seg];\n }\n return cur;\n}\n\n/**\n * Resolve a single template token (without braces) to a value.\n * Returns `undefined` if the token cannot be resolved.\n */\nfunction resolveToken(token: string, variables: VariableMap, context: AutomationContext): unknown {\n const trimmed = token.trim();\n if (!trimmed) return undefined;\n\n // Built-in date helpers — `NOW()` / `TODAY()` with optional `+ N` day offset.\n // The offset may be a literal integer or any token resolvable from `variables`.\n const dateFnMatch = /^(NOW|TODAY)\\s*\\(\\s*\\)\\s*(?:([+\\-])\\s*(\\S+))?$/.exec(trimmed);\n if (dateFnMatch) {\n const fn = dateFnMatch[1];\n const sign = dateFnMatch[2] === '-' ? -1 : 1;\n const offsetRaw = dateFnMatch[3];\n let offset = 0;\n if (offsetRaw) {\n const asNum = Number(offsetRaw);\n if (!isNaN(asNum)) {\n offset = asNum;\n } else if (variables.has(offsetRaw)) {\n offset = Number(variables.get(offsetRaw)) || 0;\n }\n }\n const now = new Date();\n if (offset) now.setDate(now.getDate() + sign * offset);\n if (fn === 'NOW') return now.toISOString();\n return now.toISOString().slice(0, 10);\n }\n\n // $User.* shortcuts\n if (trimmed.startsWith('$User.')) {\n const path = trimmed.slice('$User.'.length).split('.');\n if (path[0] === 'Id') return context.userId;\n if (path[0] === 'Email') return resolvePath((context as any).user, ['email', ...path.slice(1)]) ?? undefined;\n return resolvePath((context as any).user, path);\n }\n\n // Direct variable / dotted path lookup (fast path, no arithmetic).\n if (/^[A-Za-z_$][\\w$]*(?:\\.[A-Za-z_$][\\w$]*)*$/.test(trimmed)) {\n const segments = trimmed.split('.');\n const head = segments[0];\n if (variables.has(head)) {\n return resolvePath(variables.get(head), segments.slice(1));\n }\n if (variables.has(trimmed)) return variables.get(trimmed);\n return undefined;\n }\n\n // Arithmetic / mixed expression: substitute variable references (foo, foo.bar)\n // with their numeric/string literal forms, then evaluate via Function().\n // Restricted to a safe character set (digits, basic operators, parentheses,\n // dots and identifier characters) — never executed on raw user input.\n if (!/^[\\w\\s+\\-*/%().,?:<>=!&|\"'$]+$/.test(trimmed)) return undefined;\n let safe = trimmed;\n safe = safe.replace(/([A-Za-z_$][\\w$]*(?:\\.[A-Za-z_$][\\w$]*)*)/g, (match) => {\n // Don't substitute reserved literals\n if (match === 'true' || match === 'false' || match === 'null' || match === 'undefined') return match;\n const segs = match.split('.');\n const head = segs[0];\n let val: unknown;\n if (variables.has(head)) val = resolvePath(variables.get(head), segs.slice(1));\n else if (variables.has(match)) val = variables.get(match);\n if (val === undefined || val === null) return 'null';\n if (typeof val === 'number' || typeof val === 'boolean') return String(val);\n return JSON.stringify(String(val));\n });\n try {\n // eslint-disable-next-line @typescript-eslint/no-implied-eval, no-new-func\n const fn = new Function(`\"use strict\"; return (${safe});`);\n return fn();\n } catch {\n return undefined;\n }\n}\n\n/**\n * Replace `{...}` tokens in a string with resolved values.\n * - When the entire string is a single token, returns the raw value (preserving type).\n * - Otherwise concatenates string substitutions, with `null`/`undefined` rendered as ''.\n */\nexport function interpolateString(\n input: string,\n variables: VariableMap,\n context: AutomationContext,\n): unknown {\n if (!input.includes('{')) return input;\n const single = /^\\{([^{}]+)\\}$/.exec(input);\n if (single) {\n const value = resolveToken(single[1], variables, context);\n return value;\n }\n return input.replace(/\\{([^{}]+)\\}/g, (_match, expr) => {\n const value = resolveToken(expr, variables, context);\n if (value === undefined || value === null) return '';\n return String(value);\n });\n}\n\n/**\n * Recursively interpolate template tokens in arbitrary JSON-like values.\n */\nexport function interpolate<T = unknown>(\n value: T,\n variables: VariableMap,\n context: AutomationContext,\n): T {\n if (typeof value === 'string') {\n return interpolateString(value, variables, context) as unknown as T;\n }\n if (Array.isArray(value)) {\n return value.map(v => interpolate(v, variables, context)) as unknown as T;\n }\n if (value && typeof value === 'object') {\n const out: Record<string, unknown> = {};\n for (const [k, v] of Object.entries(value as Record<string, unknown>)) {\n out[k] = interpolate(v, variables, context);\n }\n return out as unknown as T;\n }\n return value;\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport type { IDataEngine } from '@objectstack/spec/contracts';\nimport type { AutomationEngine } from '../engine.js';\nimport { interpolate } from './template.js';\n\n/**\n * CRUD Node Plugin — wires `get_record` / `create_record` / `update_record` /\n * `delete_record` flow nodes to the runtime data layer (ObjectQL / IDataEngine).\n *\n * Each executor:\n * 1. Interpolates `{var}` / `{var.path}` / `{$User.*}` / `{NOW()}` tokens in\n * `node.config` against the running flow's variable context.\n * 2. Calls the resolved data engine via `ctx.getService('data')`.\n * 3. Writes the result back to the variable context under `outputVariable`\n * (or under `<nodeId>.id` / `<nodeId>.records` by default), so downstream\n * nodes can reference fields like `{leadRecord.company}`.\n *\n * If no data engine is registered, executors degrade to a no-op success so\n * test environments without ObjectQL still complete the flow without errors.\n */\nexport class CrudNodesPlugin implements Plugin {\n name = 'com.objectstack.automation.crud-nodes';\n version = '1.0.0';\n type = 'standard' as const;\n dependencies = ['com.objectstack.service-automation'];\n\n async init(ctx: PluginContext): Promise<void> {\n const engine = ctx.getService<AutomationEngine>('automation');\n const getData = (): IDataEngine | undefined => {\n try {\n return ctx.getService<IDataEngine>('data') ?? ctx.getService<IDataEngine>('objectql');\n } catch {\n return undefined;\n }\n };\n\n // ── get_record ────────────────────────────────────────\n engine.registerNodeExecutor({\n type: 'get_record',\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const objectName = String(cfg.objectName ?? cfg.object ?? '');\n if (!objectName) return { success: false, error: 'get_record: objectName required' };\n\n const filter = interpolate(cfg.filter ?? cfg.filters ?? {}, variables, context) as Record<string, unknown>;\n const fields = cfg.fields as string[] | undefined;\n const limit = typeof cfg.limit === 'number' ? cfg.limit : undefined;\n const outputVariable = cfg.outputVariable as string | undefined;\n\n const data = getData();\n if (!data) {\n ctx.logger.warn(`[get_record] no data engine; skipping ${objectName}`);\n return { success: true, output: { records: [], object: objectName } };\n }\n\n try {\n if (limit && limit > 1) {\n const records = await data.find(objectName, { where: filter, fields, limit });\n if (outputVariable) variables.set(outputVariable, records);\n return { success: true, output: { records, object: objectName } };\n }\n const record = await data.findOne(objectName, { where: filter, fields });\n if (outputVariable) variables.set(outputVariable, record);\n return { success: true, output: { record, id: record?.id, object: objectName } };\n } catch (err) {\n return { success: false, error: `get_record(${objectName}) failed: ${(err as Error).message}` };\n }\n },\n });\n\n // ── create_record ─────────────────────────────────────\n engine.registerNodeExecutor({\n type: 'create_record',\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const objectName = String(cfg.objectName ?? cfg.object ?? '');\n if (!objectName) return { success: false, error: 'create_record: objectName required' };\n\n const fields = interpolate(cfg.fields ?? {}, variables, context) as Record<string, unknown>;\n const outputVariable = cfg.outputVariable as string | undefined;\n\n const data = getData();\n if (!data) {\n ctx.logger.warn(`[create_record] no data engine; skipping ${objectName}`);\n if (outputVariable) variables.set(outputVariable, `mock-${objectName}-${Date.now()}`);\n return { success: true, output: { id: `mock-${objectName}-${Date.now()}`, object: objectName } };\n }\n\n try {\n const created = await data.insert(objectName, fields);\n const insertedId = Array.isArray(created) ? created[0]?.id : created?.id ?? created;\n if (outputVariable) variables.set(outputVariable, insertedId);\n return { success: true, output: { id: insertedId, record: created, object: objectName } };\n } catch (err) {\n return { success: false, error: `create_record(${objectName}) failed: ${(err as Error).message}` };\n }\n },\n });\n\n // ── update_record ─────────────────────────────────────\n engine.registerNodeExecutor({\n type: 'update_record',\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const objectName = String(cfg.objectName ?? cfg.object ?? '');\n if (!objectName) return { success: false, error: 'update_record: objectName required' };\n\n const filter = interpolate(cfg.filter ?? cfg.filters ?? {}, variables, context) as Record<string, unknown>;\n const fields = interpolate(cfg.fields ?? {}, variables, context) as Record<string, unknown>;\n\n const data = getData();\n if (!data) {\n ctx.logger.warn(`[update_record] no data engine; skipping ${objectName}`);\n return { success: true };\n }\n\n try {\n const result = await data.update(objectName, fields, { where: filter });\n return { success: true, output: { result, object: objectName } };\n } catch (err) {\n return { success: false, error: `update_record(${objectName}) failed: ${(err as Error).message}` };\n }\n },\n });\n\n // ── delete_record ─────────────────────────────────────\n engine.registerNodeExecutor({\n type: 'delete_record',\n async execute(node, variables, context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const objectName = String(cfg.objectName ?? cfg.object ?? '');\n if (!objectName) return { success: false, error: 'delete_record: objectName required' };\n\n const filter = interpolate(cfg.filter ?? cfg.filters ?? {}, variables, context) as Record<string, unknown>;\n\n const data = getData();\n if (!data) return { success: true };\n\n try {\n const result = await data.delete(objectName, { where: filter });\n return { success: true, output: { result, object: objectName } };\n } catch (err) {\n return { success: false, error: `delete_record(${objectName}) failed: ${(err as Error).message}` };\n }\n },\n });\n\n ctx.logger.info('[CRUD Nodes] 4 node executors registered (data-backed)');\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport type { AutomationEngine } from '../engine.js';\n\n/**\n * Logic Node Plugin — Provides decision / assignment / loop nodes\n *\n * Dependencies: service-automation (engine)\n */\nexport class LogicNodesPlugin implements Plugin {\n name = 'com.objectstack.automation.logic-nodes';\n version = '1.0.0';\n type = 'standard' as const;\n dependencies = ['com.objectstack.service-automation'];\n\n async init(ctx: PluginContext): Promise<void> {\n const engine = ctx.getService<AutomationEngine>('automation');\n\n // decision node — conditional branching\n engine.registerNodeExecutor({\n type: 'decision',\n async execute(node, variables, _context) {\n const config = node.config as Record<string, unknown> | undefined;\n const conditions = (config?.conditions ?? []) as Array<{ label: string; expression: string }>;\n\n for (const cond of conditions) {\n if (engine.evaluateCondition(cond.expression, variables)) {\n return { success: true, branchLabel: cond.label };\n }\n }\n return { success: true, branchLabel: 'default' };\n },\n });\n\n // assignment node — set variables\n engine.registerNodeExecutor({\n type: 'assignment',\n async execute(node, variables, _context) {\n const config = (node.config ?? {}) as Record<string, unknown>;\n for (const [key, value] of Object.entries(config)) {\n variables.set(key, value);\n }\n return { success: true };\n },\n });\n\n // loop node — iterate over a collection\n engine.registerNodeExecutor({\n type: 'loop',\n async execute(node, variables, _context) {\n const config = node.config as Record<string, unknown> | undefined;\n const collectionName = config?.collection as string | undefined;\n if (collectionName) {\n const collection = variables.get(collectionName);\n if (Array.isArray(collection)) {\n variables.set('$loopItems', collection);\n variables.set('$loopIndex', 0);\n }\n }\n return { success: true };\n },\n });\n\n ctx.logger.info('[Logic Nodes] 3 node executors registered');\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport type { AutomationEngine } from '../engine.js';\n\n/**\n * HTTP + Connector Node Plugin — Provides http_request / connector_action nodes\n *\n * Dependencies: service-automation (engine)\n */\nexport class HttpConnectorPlugin implements Plugin {\n name = 'com.objectstack.automation.http-connector';\n version = '1.0.0';\n type = 'standard' as const;\n dependencies = ['com.objectstack.service-automation'];\n\n async init(ctx: PluginContext): Promise<void> {\n const engine = ctx.getService<AutomationEngine>('automation');\n\n // http_request node executor\n engine.registerNodeExecutor({\n type: 'http_request',\n async execute(node, _variables, _context) {\n const config = node.config as Record<string, unknown> | undefined;\n const url = config?.url as string | undefined;\n const method = (config?.method as string) ?? 'GET';\n const headers = config?.headers as Record<string, string> | undefined;\n const body = config?.body;\n\n if (!url) {\n return { success: false, error: 'http_request: url is required' };\n }\n\n const response = await fetch(url, {\n method,\n headers,\n body: body ? JSON.stringify(body) : undefined,\n });\n const data = await response.json();\n\n return {\n success: response.ok,\n output: { response: data, status: response.status },\n error: response.ok ? undefined : `HTTP ${response.status}`,\n };\n },\n });\n\n // connector_action node — calls a registered connector\n engine.registerNodeExecutor({\n type: 'connector_action',\n async execute(node, _variables, _context) {\n const connectorConfig = node.connectorConfig;\n if (!connectorConfig) {\n return { success: false, error: 'connector_action: connectorConfig is required' };\n }\n\n ctx.logger.info(\n `Connector action: ${connectorConfig.connectorId}.${connectorConfig.actionId}`,\n );\n\n // In production, this would look up the connector from a registry\n // and execute the specified action with the mapped inputs\n return { success: true, output: { connectorResult: {} } };\n },\n });\n\n ctx.logger.info('[HTTP Connector] 2 node executors registered');\n }\n}\n","// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.\n\nimport type { Plugin, PluginContext } from '@objectstack/core';\nimport type { AutomationEngine } from '../engine.js';\n\n/**\n * Screen / Script Node Plugin — Provides 'screen' and 'script' executors.\n *\n * - 'screen' nodes are pass-through on the server. The engine already injects\n * `isInput: true` flow variables from `context.params` into the top-level\n * variables map before execution begins, so screen nodes have no remaining\n * server-side work.\n * - 'script' nodes dispatch by `config.actionType`. Currently only 'email'\n * has a (logger-backed) implementation; unknown action types still succeed\n * so flows can continue and downstream nodes can react.\n *\n * Dependencies: service-automation (engine)\n */\nexport class ScreenNodesPlugin implements Plugin {\n name = 'com.objectstack.automation.screen-nodes';\n version = '1.0.0';\n type = 'standard' as const;\n dependencies = ['com.objectstack.service-automation'];\n\n async init(ctx: PluginContext): Promise<void> {\n const engine = ctx.getService<AutomationEngine>('automation');\n\n // screen — server-side pass-through (input vars already injected by engine).\n engine.registerNodeExecutor({\n type: 'screen',\n async execute(_node, _variables, _context) {\n return { success: true };\n },\n });\n\n // script — dispatch by actionType.\n engine.registerNodeExecutor({\n type: 'script',\n async execute(node, _variables, _context) {\n const cfg = (node.config ?? {}) as Record<string, unknown>;\n const actionType = (cfg.actionType as string | undefined) ?? 'noop';\n if (actionType === 'email') {\n ctx.logger.info(\n `[Script:email] template=${String(cfg.template)} ` +\n `recipients=${JSON.stringify(cfg.recipients)} ` +\n `vars=${JSON.stringify(cfg.variables)}`,\n );\n return {\n success: true,\n output: {\n actionType,\n template: cfg.template,\n recipients: cfg.recipients,\n },\n };\n }\n ctx.logger.info(`[Script:${actionType}] node=${node.id} executed (no-op handler)`);\n return { success: true, output: { actionType } };\n },\n });\n\n ctx.logger.info('[Screen/Script Nodes] 2 node executors registered');\n }\n}\n"],"mappings":"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","names":[]}
package/dist/index.d.cts CHANGED
@@ -145,7 +145,11 @@ declare class AutomationEngine implements IAutomationService {
145
145
  * Supports: comparisons (>, <, >=, <=, ==, !=, ===, !==),
146
146
  * boolean literals (true, false), and basic arithmetic.
147
147
  */
148
- evaluateCondition(expression: string, variables: Map<string, unknown>): boolean;
148
+ evaluateCondition(expression: string | {
149
+ dialect?: string;
150
+ source?: string;
151
+ ast?: unknown;
152
+ }, variables: Map<string, unknown>): boolean;
149
153
  /**
150
154
  * Compare two string-represented values with an operator.
151
155
  */
@@ -173,7 +177,9 @@ interface AutomationServicePluginOptions {
173
177
  *
174
178
  * Responsibilities:
175
179
  * 1. init phase: Create engine instance, register as 'automation' service
176
- * 2. start phase: Trigger 'automation:ready' hook for node plugin registration
180
+ * 2. start phase: Trigger 'automation:ready' hook for node plugin registration,
181
+ * then pull flow definitions from the ObjectQL schema registry and register
182
+ * them with the engine.
177
183
  * 3. destroy phase: Clean up resources
178
184
  *
179
185
  * Does NOT implement any specific nodes — nodes are registered by other plugins
@@ -205,12 +211,19 @@ declare class AutomationServicePlugin implements Plugin {
205
211
  }
206
212
 
207
213
  /**
208
- * CRUD Node Plugin — Provides get_record / create_record / update_record / delete_record
214
+ * CRUD Node Plugin — wires `get_record` / `create_record` / `update_record` /
215
+ * `delete_record` flow nodes to the runtime data layer (ObjectQL / IDataEngine).
209
216
  *
210
- * Dependencies: service-automation (engine)
217
+ * Each executor:
218
+ * 1. Interpolates `{var}` / `{var.path}` / `{$User.*}` / `{NOW()}` tokens in
219
+ * `node.config` against the running flow's variable context.
220
+ * 2. Calls the resolved data engine via `ctx.getService('data')`.
221
+ * 3. Writes the result back to the variable context under `outputVariable`
222
+ * (or under `<nodeId>.id` / `<nodeId>.records` by default), so downstream
223
+ * nodes can reference fields like `{leadRecord.company}`.
211
224
  *
212
- * In a full runtime environment these nodes would delegate to ObjectQL (data layer).
213
- * This MVP implementation provides the extension point structure.
225
+ * If no data engine is registered, executors degrade to a no-op success so
226
+ * test environments without ObjectQL still complete the flow without errors.
214
227
  */
215
228
  declare class CrudNodesPlugin implements Plugin {
216
229
  name: string;
@@ -246,4 +259,25 @@ declare class HttpConnectorPlugin implements Plugin {
246
259
  init(ctx: PluginContext): Promise<void>;
247
260
  }
248
261
 
249
- export { AutomationEngine, AutomationServicePlugin, type AutomationServicePluginOptions, CrudNodesPlugin, type FlowTrigger, HttpConnectorPlugin, LogicNodesPlugin, type NodeExecutionResult, type NodeExecutor };
262
+ /**
263
+ * Screen / Script Node Plugin — Provides 'screen' and 'script' executors.
264
+ *
265
+ * - 'screen' nodes are pass-through on the server. The engine already injects
266
+ * `isInput: true` flow variables from `context.params` into the top-level
267
+ * variables map before execution begins, so screen nodes have no remaining
268
+ * server-side work.
269
+ * - 'script' nodes dispatch by `config.actionType`. Currently only 'email'
270
+ * has a (logger-backed) implementation; unknown action types still succeed
271
+ * so flows can continue and downstream nodes can react.
272
+ *
273
+ * Dependencies: service-automation (engine)
274
+ */
275
+ declare class ScreenNodesPlugin implements Plugin {
276
+ name: string;
277
+ version: string;
278
+ type: "standard";
279
+ dependencies: string[];
280
+ init(ctx: PluginContext): Promise<void>;
281
+ }
282
+
283
+ export { AutomationEngine, AutomationServicePlugin, type AutomationServicePluginOptions, CrudNodesPlugin, type FlowTrigger, HttpConnectorPlugin, LogicNodesPlugin, type NodeExecutionResult, type NodeExecutor, ScreenNodesPlugin };
package/dist/index.d.ts CHANGED
@@ -145,7 +145,11 @@ declare class AutomationEngine implements IAutomationService {
145
145
  * Supports: comparisons (>, <, >=, <=, ==, !=, ===, !==),
146
146
  * boolean literals (true, false), and basic arithmetic.
147
147
  */
148
- evaluateCondition(expression: string, variables: Map<string, unknown>): boolean;
148
+ evaluateCondition(expression: string | {
149
+ dialect?: string;
150
+ source?: string;
151
+ ast?: unknown;
152
+ }, variables: Map<string, unknown>): boolean;
149
153
  /**
150
154
  * Compare two string-represented values with an operator.
151
155
  */
@@ -173,7 +177,9 @@ interface AutomationServicePluginOptions {
173
177
  *
174
178
  * Responsibilities:
175
179
  * 1. init phase: Create engine instance, register as 'automation' service
176
- * 2. start phase: Trigger 'automation:ready' hook for node plugin registration
180
+ * 2. start phase: Trigger 'automation:ready' hook for node plugin registration,
181
+ * then pull flow definitions from the ObjectQL schema registry and register
182
+ * them with the engine.
177
183
  * 3. destroy phase: Clean up resources
178
184
  *
179
185
  * Does NOT implement any specific nodes — nodes are registered by other plugins
@@ -205,12 +211,19 @@ declare class AutomationServicePlugin implements Plugin {
205
211
  }
206
212
 
207
213
  /**
208
- * CRUD Node Plugin — Provides get_record / create_record / update_record / delete_record
214
+ * CRUD Node Plugin — wires `get_record` / `create_record` / `update_record` /
215
+ * `delete_record` flow nodes to the runtime data layer (ObjectQL / IDataEngine).
209
216
  *
210
- * Dependencies: service-automation (engine)
217
+ * Each executor:
218
+ * 1. Interpolates `{var}` / `{var.path}` / `{$User.*}` / `{NOW()}` tokens in
219
+ * `node.config` against the running flow's variable context.
220
+ * 2. Calls the resolved data engine via `ctx.getService('data')`.
221
+ * 3. Writes the result back to the variable context under `outputVariable`
222
+ * (or under `<nodeId>.id` / `<nodeId>.records` by default), so downstream
223
+ * nodes can reference fields like `{leadRecord.company}`.
211
224
  *
212
- * In a full runtime environment these nodes would delegate to ObjectQL (data layer).
213
- * This MVP implementation provides the extension point structure.
225
+ * If no data engine is registered, executors degrade to a no-op success so
226
+ * test environments without ObjectQL still complete the flow without errors.
214
227
  */
215
228
  declare class CrudNodesPlugin implements Plugin {
216
229
  name: string;
@@ -246,4 +259,25 @@ declare class HttpConnectorPlugin implements Plugin {
246
259
  init(ctx: PluginContext): Promise<void>;
247
260
  }
248
261
 
249
- export { AutomationEngine, AutomationServicePlugin, type AutomationServicePluginOptions, CrudNodesPlugin, type FlowTrigger, HttpConnectorPlugin, LogicNodesPlugin, type NodeExecutionResult, type NodeExecutor };
262
+ /**
263
+ * Screen / Script Node Plugin — Provides 'screen' and 'script' executors.
264
+ *
265
+ * - 'screen' nodes are pass-through on the server. The engine already injects
266
+ * `isInput: true` flow variables from `context.params` into the top-level
267
+ * variables map before execution begins, so screen nodes have no remaining
268
+ * server-side work.
269
+ * - 'script' nodes dispatch by `config.actionType`. Currently only 'email'
270
+ * has a (logger-backed) implementation; unknown action types still succeed
271
+ * so flows can continue and downstream nodes can react.
272
+ *
273
+ * Dependencies: service-automation (engine)
274
+ */
275
+ declare class ScreenNodesPlugin implements Plugin {
276
+ name: string;
277
+ version: string;
278
+ type: "standard";
279
+ dependencies: string[];
280
+ init(ctx: PluginContext): Promise<void>;
281
+ }
282
+
283
+ export { AutomationEngine, AutomationServicePlugin, type AutomationServicePluginOptions, CrudNodesPlugin, type FlowTrigger, HttpConnectorPlugin, LogicNodesPlugin, type NodeExecutionResult, type NodeExecutor, ScreenNodesPlugin };