@motiadev/core 0.0.19 → 0.0.21

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.
Files changed (46) hide show
  1. package/dist/index.d.ts +2 -3
  2. package/dist/index.js +2 -3
  3. package/dist/src/call-step-file.d.ts +3 -2
  4. package/dist/src/call-step-file.js +13 -9
  5. package/dist/src/cron-handler.d.ts +12 -2
  6. package/dist/src/cron-handler.js +23 -18
  7. package/dist/src/event-manager.js +9 -4
  8. package/dist/src/flows-endpoint.d.ts +4 -3
  9. package/dist/src/flows-endpoint.js +5 -4
  10. package/dist/src/get-step-config.d.ts +2 -0
  11. package/dist/src/get-step-config.js +22 -0
  12. package/dist/src/guards.d.ts +1 -1
  13. package/dist/src/guards.js +0 -1
  14. package/dist/src/locked-data.d.ts +26 -0
  15. package/dist/src/locked-data.js +144 -0
  16. package/dist/src/logger.d.ts +1 -1
  17. package/dist/src/node/get-module-export.js +5 -2
  18. package/dist/src/node/logger.d.ts +11 -6
  19. package/dist/src/node/logger.js +29 -16
  20. package/dist/src/node/node-runner.js +3 -4
  21. package/dist/src/node/rpc.d.ts +2 -0
  22. package/dist/src/node/rpc.js +11 -1
  23. package/dist/src/printer.d.ts +17 -0
  24. package/dist/src/printer.js +74 -0
  25. package/dist/src/python/logger.py +3 -3
  26. package/dist/src/python/python-runner.py +21 -16
  27. package/dist/src/python/rpc.py +26 -15
  28. package/dist/src/python/rpc_state_manager.py +12 -11
  29. package/dist/src/ruby/get_config.rb +12 -22
  30. package/dist/src/ruby/logger.rb +23 -16
  31. package/dist/src/ruby/rpc.rb +123 -0
  32. package/dist/src/ruby/rpc_state_manager.rb +25 -0
  33. package/dist/src/ruby/ruby_runner.rb +42 -31
  34. package/dist/src/server.d.ts +8 -10
  35. package/dist/src/server.js +31 -13
  36. package/dist/src/step-handler-rpc-processor.d.ts +1 -0
  37. package/dist/src/step-handler-rpc-processor.js +11 -1
  38. package/dist/src/step-handlers.d.ts +8 -2
  39. package/dist/src/step-handlers.js +34 -20
  40. package/dist/src/step-validator.d.ts +14 -0
  41. package/dist/src/step-validator.js +99 -0
  42. package/dist/src/types.d.ts +20 -26
  43. package/dist/src/utils.d.ts +2 -0
  44. package/dist/src/utils.js +15 -0
  45. package/package.json +2 -1
  46. package/dist/src/ruby/state_adapter.rb +0 -62
package/dist/index.d.ts CHANGED
@@ -4,8 +4,7 @@ export * from './src/step-handlers';
4
4
  export * from './src/event-manager';
5
5
  export * from './src/logger';
6
6
  export * from './src/state/create-state-adapter';
7
- export * from './src/python/get-python-config';
8
- export * from './src/ruby/get-ruby-config';
9
- export * from './src/node/get-config';
10
7
  export * from './src/cron-handler';
11
8
  export * from './src/guards';
9
+ export * from './src/locked-data';
10
+ export * from './src/get-step-config';
package/dist/index.js CHANGED
@@ -20,8 +20,7 @@ __exportStar(require("./src/step-handlers"), exports);
20
20
  __exportStar(require("./src/event-manager"), exports);
21
21
  __exportStar(require("./src/logger"), exports);
22
22
  __exportStar(require("./src/state/create-state-adapter"), exports);
23
- __exportStar(require("./src/python/get-python-config"), exports);
24
- __exportStar(require("./src/ruby/get-ruby-config"), exports);
25
- __exportStar(require("./src/node/get-config"), exports);
26
23
  __exportStar(require("./src/cron-handler"), exports);
27
24
  __exportStar(require("./src/guards"), exports);
25
+ __exportStar(require("./src/locked-data"), exports);
26
+ __exportStar(require("./src/get-step-config"), exports);
@@ -1,2 +1,3 @@
1
- import { Event, EventManager, InternalStateManager } from './types';
2
- export declare const callStepFile: <TData>(stepPath: string, step: string, event: Event<TData>, eventManager: EventManager, state: InternalStateManager) => Promise<void>;
1
+ import { LockedData } from './locked-data';
2
+ import { Event, EventConfig, EventManager, InternalStateManager, Step } from './types';
3
+ export declare const callStepFile: <TData>(step: Step<EventConfig>, lockedData: LockedData, event: Event<TData>, eventManager: EventManager, state: InternalStateManager) => Promise<void>;
@@ -7,6 +7,7 @@ exports.callStepFile = void 0;
7
7
  const step_handler_rpc_processor_1 = require("./step-handler-rpc-processor");
8
8
  const child_process_1 = require("child_process");
9
9
  const path_1 = __importDefault(require("path"));
10
+ const utils_1 = require("./utils");
10
11
  const nodeRunner = path_1.default.join(__dirname, 'node', 'node-runner.js');
11
12
  const pythonRunner = path_1.default.join(__dirname, 'python', 'python-runner.py');
12
13
  const rubyRunner = path_1.default.join(__dirname, 'ruby', 'ruby_runner.rb');
@@ -25,28 +26,31 @@ const getLanguageBasedRunner = (stepFilePath = '') => {
25
26
  }
26
27
  throw Error(`Unsupported file extension ${stepFilePath}`);
27
28
  };
28
- const callStepFile = (stepPath, step, event, eventManager, state) => {
29
+ const callStepFile = (step, lockedData, event, eventManager, state) => {
29
30
  return new Promise((resolve, reject) => {
30
31
  const jsonData = JSON.stringify({ ...event });
31
- const { runner, command } = getLanguageBasedRunner(stepPath);
32
- const child = (0, child_process_1.spawn)(command, [runner, stepPath, jsonData], {
32
+ const { runner, command } = getLanguageBasedRunner(step.filePath);
33
+ const child = (0, child_process_1.spawn)(command, [runner, step.filePath, jsonData], {
33
34
  stdio: [undefined, undefined, undefined, 'ipc'],
34
35
  });
35
36
  const rpcProcessor = new step_handler_rpc_processor_1.RpcProcessor(child);
36
- rpcProcessor.handler('log', async (input) => {
37
- event.logger.log(input);
38
- });
37
+ rpcProcessor.handler('close', async () => child.kill());
38
+ rpcProcessor.handler('log', async (input) => event.logger.log(input));
39
39
  rpcProcessor.handler('state.get', (input) => state.get(input.traceId, input.key));
40
40
  rpcProcessor.handler('state.set', (input) => state.set(input.traceId, input.key, input.value));
41
41
  rpcProcessor.handler('state.delete', (input) => state.delete(input.traceId, input.key));
42
42
  rpcProcessor.handler('state.clear', (input) => state.clear(input.traceId));
43
- rpcProcessor.handler('emit', (input) => {
43
+ rpcProcessor.handler('emit', async (input) => {
44
+ if (!(0, utils_1.isAllowedToEmit)(step, input.type)) {
45
+ lockedData.printer.printInvalidEmit(step, input.type);
46
+ return;
47
+ }
44
48
  return eventManager.emit({
45
49
  ...input,
46
50
  traceId: event.traceId,
47
51
  flows: event.flows,
48
52
  logger: event.logger,
49
- }, stepPath);
53
+ }, step.filePath);
50
54
  });
51
55
  rpcProcessor.init();
52
56
  child.stdout?.on('data', (data) => {
@@ -60,7 +64,7 @@ const callStepFile = (stepPath, step, event, eventManager, state) => {
60
64
  });
61
65
  child.stderr?.on('data', (data) => event.logger.error(Buffer.from(data).toString(), { step }));
62
66
  child.on('close', (code) => {
63
- if (code !== 0) {
67
+ if (code !== 0 && code !== null) {
64
68
  reject(new Error(`Process exited with code ${code}`));
65
69
  }
66
70
  else {
@@ -1,3 +1,13 @@
1
- import { EventManager, Step } from './types';
2
1
  import { Server } from 'socket.io';
3
- export declare const setupCronHandlers: (steps: Step[], eventManager: EventManager, socketServer?: Server) => () => void;
2
+ import { LockedData } from './locked-data';
3
+ import { EventManager, Step, CronConfig } from './types';
4
+ export type CronManager = {
5
+ createCronJob: (step: Step<CronConfig>) => void;
6
+ removeCronJob: (step: Step<CronConfig>) => void;
7
+ close: () => void;
8
+ };
9
+ export declare const setupCronHandlers: (lockedData: LockedData, eventManager: EventManager, socketServer: Server) => {
10
+ createCronJob: (step: Step<CronConfig>) => void;
11
+ removeCronJob: (step: Step<CronConfig>) => void;
12
+ close: () => void;
13
+ };
@@ -34,13 +34,12 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.setupCronHandlers = void 0;
37
- const logger_1 = require("./logger");
38
37
  const cron = __importStar(require("node-cron"));
39
- const guards_1 = require("./guards");
38
+ const logger_1 = require("./logger");
40
39
  const get_module_export_1 = require("./node/get-module-export");
41
- const cronJobs = new Map();
42
- const setupCronHandlers = (steps, eventManager, socketServer) => {
43
- steps.filter(guards_1.isCronStep).forEach(async (step) => {
40
+ const setupCronHandlers = (lockedData, eventManager, socketServer) => {
41
+ const cronJobs = new Map();
42
+ const createCronJob = (step) => {
44
43
  const { config, filePath } = step;
45
44
  const { cron: cronExpression } = config;
46
45
  if (!cron.validate(cronExpression)) {
@@ -58,24 +57,21 @@ const setupCronHandlers = (steps, eventManager, socketServer) => {
58
57
  const task = cron.schedule(cronExpression, async () => {
59
58
  const traceId = Math.random().toString(36).substring(7);
60
59
  const logger = new logger_1.Logger(traceId, config.flows, step.config.name, socketServer);
60
+ const flows = config.flows;
61
61
  try {
62
62
  const handler = await (0, get_module_export_1.getModuleExport)(filePath, 'handler');
63
63
  const emit = async (event) => {
64
- await eventManager.emit({
65
- ...event,
66
- traceId,
67
- flows: config.flows,
68
- logger,
69
- }, filePath);
64
+ await eventManager.emit({ ...event, traceId, flows, logger }, filePath);
70
65
  };
71
66
  if (handler) {
72
67
  await handler({ emit, logger, traceId });
73
68
  }
74
69
  else {
75
- await emit({
76
- type: config.emits[0],
77
- data: { timestamp: Date.now() },
78
- });
70
+ const data = { timestamp: Date.now() };
71
+ await Promise.all(config.emits.map((item) => {
72
+ const type = typeof item === 'string' ? item : item.type;
73
+ emit({ type, data });
74
+ }));
79
75
  }
80
76
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
81
77
  }
@@ -86,11 +82,20 @@ const setupCronHandlers = (steps, eventManager, socketServer) => {
86
82
  });
87
83
  }
88
84
  });
89
- cronJobs.set(step.config.name, task);
90
- });
91
- return () => {
85
+ cronJobs.set(step.filePath, task);
86
+ };
87
+ const removeCronJob = (step) => {
88
+ const task = cronJobs.get(step.filePath);
89
+ if (task) {
90
+ task.stop();
91
+ cronJobs.delete(step.filePath);
92
+ }
93
+ };
94
+ const close = () => {
92
95
  cronJobs.forEach((task) => task.stop());
93
96
  cronJobs.clear();
94
97
  };
98
+ lockedData.cronSteps().forEach(createCronJob);
99
+ return { createCronJob, removeCronJob, close };
95
100
  };
96
101
  exports.setupCronHandlers = setupCronHandlers;
@@ -8,15 +8,20 @@ const createEventManager = () => {
8
8
  const eventHandlers = handlers[event.type] ?? [];
9
9
  const { logger, ...rest } = event;
10
10
  logger.debug('[Flow Emit] Event emitted', { handlers: eventHandlers.length, data: rest, file });
11
- eventHandlers.map((handler) => handler(event));
11
+ eventHandlers.map((eventHandler) => eventHandler.handler(event));
12
12
  };
13
- const subscribe = (event, handlerName, handler) => {
13
+ const subscribe = (config) => {
14
+ const { event, handlerName, handler, filePath } = config;
14
15
  if (!handlers[event]) {
15
16
  handlers[event] = [];
16
17
  }
17
18
  logger_1.globalLogger.debug('[Flow Sub] Subscribing to event', { event, handlerName });
18
- handlers[event].push(handler);
19
+ handlers[event].push({ filePath, handler: handler });
19
20
  };
20
- return { emit, subscribe };
21
+ const unsubscribe = (config) => {
22
+ const { filePath, event } = config;
23
+ handlers[event] = handlers[event].filter((handler) => handler.filePath !== filePath);
24
+ };
25
+ return { emit, subscribe, unsubscribe };
21
26
  };
22
27
  exports.createEventManager = createEventManager;
@@ -1,5 +1,6 @@
1
1
  import { Express } from 'express';
2
- import { Emit, LockedData } from './types';
2
+ import { Emit } from './types';
3
+ import { LockedData } from './locked-data';
3
4
  type FlowListResponse = {
4
5
  id: string;
5
6
  name: string;
@@ -34,6 +35,6 @@ type FlowResponse = FlowListResponse & {
34
35
  steps: FlowStepResponse[];
35
36
  edges: FlowEdge[];
36
37
  };
37
- export declare const flowsEndpoint: (flows: LockedData["flows"], app: Express) => void;
38
- export declare const generateFlowsList: (flows: LockedData["flows"]) => FlowResponse[];
38
+ export declare const flowsEndpoint: (lockedData: LockedData, app: Express) => void;
39
+ export declare const generateFlowsList: (lockedData: LockedData) => FlowResponse[];
39
40
  export {};
@@ -9,12 +9,13 @@ const fs_1 = __importDefault(require("fs"));
9
9
  const zod_to_json_schema_1 = __importDefault(require("zod-to-json-schema"));
10
10
  const guards_1 = require("./guards");
11
11
  const get_step_language_1 = require("./get-step-language");
12
- const flowsEndpoint = (flows, app) => {
13
- const list = (0, exports.generateFlowsList)(flows);
12
+ const flowsEndpoint = (lockedData, app) => {
14
13
  app.get('/flows', async (_, res) => {
14
+ const list = (0, exports.generateFlowsList)(lockedData);
15
15
  res.status(200).send(list.map(({ id, name }) => ({ id, name })));
16
16
  });
17
17
  app.get('/flows/:id', async (req, res) => {
18
+ const list = (0, exports.generateFlowsList)(lockedData);
18
19
  const { id } = req.params;
19
20
  const flow = list.find((flow) => flow.id === id);
20
21
  if (!flow) {
@@ -26,8 +27,8 @@ const flowsEndpoint = (flows, app) => {
26
27
  });
27
28
  };
28
29
  exports.flowsEndpoint = flowsEndpoint;
29
- const generateFlowsList = (flows) => {
30
- return Object.entries(flows).map(([flowId, flow]) => generateFlow(flowId, flow.steps));
30
+ const generateFlowsList = (lockedData) => {
31
+ return Object.entries(lockedData.flows).map(([flowId, flow]) => generateFlow(flowId, flow.steps));
31
32
  };
32
33
  exports.generateFlowsList = generateFlowsList;
33
34
  // Helper functions
@@ -0,0 +1,2 @@
1
+ import { StepConfig } from './types';
2
+ export declare const getStepConfig: (filePath: string) => Promise<StepConfig | null>;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getStepConfig = void 0;
4
+ const get_config_1 = require("./node/get-config");
5
+ const get_python_config_1 = require("./python/get-python-config");
6
+ const get_ruby_config_1 = require("./ruby/get-ruby-config");
7
+ const getStepConfig = async (filePath) => {
8
+ const isRb = filePath.endsWith('.rb');
9
+ const isPython = filePath.endsWith('.py');
10
+ const isNode = filePath.endsWith('.js') || filePath.endsWith('.ts');
11
+ if (isRb) {
12
+ return (0, get_ruby_config_1.getRubyConfig)(filePath);
13
+ }
14
+ else if (isPython) {
15
+ return (0, get_python_config_1.getPythonConfig)(filePath);
16
+ }
17
+ else if (isNode) {
18
+ return (0, get_config_1.getNodeFileConfig)(filePath);
19
+ }
20
+ return null;
21
+ };
22
+ exports.getStepConfig = getStepConfig;
@@ -1,5 +1,5 @@
1
1
  import { ApiRouteConfig, EventConfig, NoopConfig, Step, CronConfig } from './types';
2
2
  export declare const isApiStep: (step: Step) => step is Step<ApiRouteConfig>;
3
- export declare const isEventStep: (step: Step) => step is Step<EventConfig<any>>;
3
+ export declare const isEventStep: (step: Step) => step is Step<EventConfig>;
4
4
  export declare const isNoopStep: (step: Step) => step is Step<NoopConfig>;
5
5
  export declare const isCronStep: (step: Step) => step is Step<CronConfig>;
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.isCronStep = exports.isNoopStep = exports.isEventStep = exports.isApiStep = void 0;
4
4
  const isApiStep = (step) => step.config.type === 'api';
5
5
  exports.isApiStep = isApiStep;
6
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
7
6
  const isEventStep = (step) => step.config.type === 'event';
8
7
  exports.isEventStep = isEventStep;
9
8
  const isNoopStep = (step) => step.config.type === 'noop';
@@ -0,0 +1,26 @@
1
+ import { ZodObject } from 'zod';
2
+ import { ApiRouteConfig, CronConfig, EventConfig, Flow, Step } from './types';
3
+ import { Printer } from './printer';
4
+ type FlowEvent = 'flow-created' | 'flow-removed' | 'flow-updated';
5
+ export declare class LockedData {
6
+ readonly baseDir: string;
7
+ flows: Record<string, Flow>;
8
+ activeSteps: Step[];
9
+ devSteps: Step[];
10
+ readonly printer: Printer;
11
+ private stepsMap;
12
+ private handlers;
13
+ constructor(baseDir: string);
14
+ on(event: FlowEvent, handler: (flowName: string) => void): void;
15
+ eventSteps(): Step<EventConfig<ZodObject<any>>>[];
16
+ apiSteps(): Step<ApiRouteConfig>[];
17
+ cronSteps(): Step<CronConfig>[];
18
+ updateStep(oldStep: Step, newStep: Step): boolean;
19
+ createStep(step: Step): boolean;
20
+ deleteStep(step: Step): void;
21
+ private createFlow;
22
+ private removeFlow;
23
+ private onFlowUpdated;
24
+ private isValidStep;
25
+ }
26
+ export {};
@@ -0,0 +1,144 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LockedData = void 0;
4
+ const guards_1 = require("./guards");
5
+ const step_validator_1 = require("./step-validator");
6
+ const printer_1 = require("./printer");
7
+ class LockedData {
8
+ constructor(baseDir) {
9
+ this.baseDir = baseDir;
10
+ this.flows = {};
11
+ this.activeSteps = [];
12
+ this.devSteps = [];
13
+ this.stepsMap = {};
14
+ this.printer = new printer_1.Printer(baseDir);
15
+ this.handlers = {
16
+ 'flow-created': [],
17
+ 'flow-removed': [],
18
+ 'flow-updated': [],
19
+ };
20
+ }
21
+ on(event, handler) {
22
+ this.handlers[event].push(handler);
23
+ }
24
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
25
+ eventSteps() {
26
+ return this.activeSteps.filter(guards_1.isEventStep);
27
+ }
28
+ apiSteps() {
29
+ return this.activeSteps.filter(guards_1.isApiStep);
30
+ }
31
+ cronSteps() {
32
+ return this.activeSteps.filter(guards_1.isCronStep);
33
+ }
34
+ updateStep(oldStep, newStep) {
35
+ if (!this.isValidStep(newStep)) {
36
+ this.deleteStep(oldStep);
37
+ return false;
38
+ }
39
+ if (oldStep.config.type !== newStep.config.type) {
40
+ this.activeSteps = this.activeSteps.filter((s) => s.filePath !== oldStep.filePath);
41
+ this.devSteps = this.devSteps.filter((s) => s.filePath !== oldStep.filePath);
42
+ if (newStep.config.virtualEmits) {
43
+ this.devSteps.push(newStep);
44
+ }
45
+ else {
46
+ this.activeSteps.push(newStep);
47
+ }
48
+ }
49
+ const savedStep = this.stepsMap[newStep.filePath];
50
+ const addedFlows = newStep.config.flows?.filter((flowName) => !oldStep.config.flows?.includes(flowName)) ?? [];
51
+ const removedFlows = oldStep.config.flows?.filter((flowName) => !newStep.config.flows?.includes(flowName)) ?? [];
52
+ const untouchedFlows = oldStep.config.flows?.filter((flowName) => newStep.config.flows?.includes(flowName)) ?? [];
53
+ untouchedFlows.forEach((flowName) => this.onFlowUpdated(flowName));
54
+ for (const flowName of addedFlows) {
55
+ if (!this.flows[flowName]) {
56
+ const flow = this.createFlow(flowName);
57
+ flow.steps.push(savedStep);
58
+ }
59
+ else {
60
+ this.flows[flowName].steps.push(savedStep);
61
+ this.onFlowUpdated(flowName);
62
+ }
63
+ }
64
+ for (const flowName of removedFlows) {
65
+ const flowSteps = this.flows[flowName].steps;
66
+ this.flows[flowName].steps = flowSteps.filter(({ filePath }) => filePath !== newStep.filePath);
67
+ if (this.flows[flowName].steps.length === 0) {
68
+ this.removeFlow(flowName);
69
+ }
70
+ else {
71
+ this.onFlowUpdated(flowName);
72
+ }
73
+ }
74
+ savedStep.config = newStep.config;
75
+ this.printer.printStepUpdated(newStep);
76
+ return true;
77
+ }
78
+ createStep(step) {
79
+ if (!this.isValidStep(step)) {
80
+ return false;
81
+ }
82
+ this.stepsMap[step.filePath] = step;
83
+ if (step.config.virtualEmits) {
84
+ this.devSteps.push(step);
85
+ }
86
+ else {
87
+ this.activeSteps.push(step);
88
+ }
89
+ for (const flowName of step.config.flows ?? []) {
90
+ if (!this.flows[flowName]) {
91
+ const flow = this.createFlow(flowName);
92
+ flow.steps.push(step);
93
+ }
94
+ else {
95
+ this.flows[flowName].steps.push(step);
96
+ this.onFlowUpdated(flowName);
97
+ }
98
+ }
99
+ this.printer.printStepCreated(step);
100
+ return true;
101
+ }
102
+ deleteStep(step) {
103
+ // Remove step from active and dev steps
104
+ this.activeSteps = this.activeSteps.filter(({ filePath }) => filePath !== step.filePath);
105
+ this.devSteps = this.devSteps.filter(({ filePath }) => filePath !== step.filePath);
106
+ delete this.stepsMap[step.filePath];
107
+ for (const flowName of step.config.flows ?? []) {
108
+ const stepFlows = this.flows[flowName]?.steps;
109
+ if (stepFlows) {
110
+ this.flows[flowName].steps = stepFlows.filter(({ filePath }) => filePath !== step.filePath);
111
+ }
112
+ if (this.flows[flowName].steps.length === 0) {
113
+ this.removeFlow(flowName);
114
+ }
115
+ else {
116
+ this.onFlowUpdated(flowName);
117
+ }
118
+ }
119
+ this.printer.printStepRemoved(step);
120
+ }
121
+ createFlow(flowName) {
122
+ const flow = { name: flowName, description: '', steps: [] };
123
+ this.flows[flowName] = flow;
124
+ this.handlers['flow-created'].forEach((handler) => handler(flowName));
125
+ this.printer.printFlowCreated(flowName);
126
+ return flow;
127
+ }
128
+ removeFlow(flowName) {
129
+ delete this.flows[flowName];
130
+ this.handlers['flow-removed'].forEach((handler) => handler(flowName));
131
+ this.printer.printFlowRemoved(flowName);
132
+ }
133
+ onFlowUpdated(flowName) {
134
+ this.handlers['flow-updated'].forEach((handler) => handler(flowName));
135
+ }
136
+ isValidStep(step) {
137
+ const validationResult = (0, step_validator_1.validateStep)(step);
138
+ if (!validationResult.success) {
139
+ this.printer.printValidationError(step.filePath, validationResult);
140
+ }
141
+ return validationResult.success;
142
+ }
143
+ }
144
+ exports.LockedData = LockedData;
@@ -12,7 +12,7 @@ export declare class Logger extends BaseLogger {
12
12
  private readonly flows;
13
13
  private readonly file;
14
14
  private emitLog;
15
- constructor(traceId: string, flows: string[], file: string, socketServer?: Server);
15
+ constructor(traceId: string, flows: string[] | undefined, file: string, socketServer?: Server);
16
16
  log(message: any): void;
17
17
  info: (message: string, args?: unknown) => void;
18
18
  error: (message: string, args?: unknown) => void;
@@ -53,13 +53,16 @@ require('ts-node').register({
53
53
  const getModuleExport = async (filePath, exportName) => {
54
54
  try {
55
55
  const resolvedFilePath = require.resolve(filePath);
56
+ if (resolvedFilePath in require.cache) {
57
+ delete require.cache[resolvedFilePath];
58
+ }
56
59
  // eslint-disable-next-line @typescript-eslint/no-require-imports
57
60
  const module = require(resolvedFilePath);
58
61
  return module[exportName];
59
62
  }
60
63
  catch (error) {
61
- console.error(`Failed to extract ${exportName} from ${filePath}:`, error);
62
- throw new Error(`No ${exportName} found in step ${filePath}`);
64
+ console.error(`Failed to extract ${exportName} from`, error);
65
+ return undefined;
63
66
  }
64
67
  };
65
68
  exports.getModuleExport = getModuleExport;
@@ -1,8 +1,13 @@
1
+ import { RpcSender } from './rpc';
1
2
  export declare class Logger {
2
- private logger;
3
- constructor(traceId: string, flows: string[], file?: string);
4
- info: (message: string, args: Record<string, unknown>) => void;
5
- error: (message: string, args: Record<string, unknown>) => void;
6
- debug: (message: string, args: Record<string, unknown>) => void;
7
- warn: (message: string, args: Record<string, unknown>) => void;
3
+ private readonly traceId;
4
+ private readonly flows;
5
+ private readonly fileName;
6
+ private readonly sender;
7
+ constructor(traceId: string, flows: string[], fileName: string, sender: RpcSender);
8
+ private _log;
9
+ info(message: string, args?: Record<string, unknown>): void;
10
+ error(message: string, args?: Record<string, unknown>): void;
11
+ debug(message: string, args?: Record<string, unknown>): void;
12
+ warn(message: string, args?: Record<string, unknown>): void;
8
13
  }
@@ -1,23 +1,36 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.Logger = void 0;
7
- const pino_1 = __importDefault(require("pino"));
8
4
  class Logger {
9
- constructor(traceId, flows, file) {
10
- this.info = (message, args) => this.logger.info(args, message);
11
- this.error = (message, args) => this.logger.error(args, message);
12
- this.debug = (message, args) => this.logger.debug(args, message);
13
- this.warn = (message, args) => this.logger.warn(args, message);
14
- this.logger = (0, pino_1.default)({
15
- level: 'info',
16
- formatters: { level: (level) => ({ level }) },
17
- errorKey: 'error',
18
- base: null,
19
- mixin: () => ({ traceId, flows, file }),
20
- });
5
+ constructor(traceId, flows, fileName, sender) {
6
+ this.traceId = traceId;
7
+ this.flows = flows;
8
+ this.fileName = fileName;
9
+ this.sender = sender;
10
+ }
11
+ _log(level, message, args) {
12
+ const logEntry = {
13
+ ...(args || {}),
14
+ level,
15
+ time: Date.now(),
16
+ traceId: this.traceId,
17
+ flows: this.flows,
18
+ file: this.fileName,
19
+ msg: message,
20
+ };
21
+ this.sender.sendNoWait('log', logEntry);
22
+ }
23
+ info(message, args) {
24
+ this._log('info', message, args);
25
+ }
26
+ error(message, args) {
27
+ this._log('error', message, args);
28
+ }
29
+ debug(message, args) {
30
+ this._log('debug', message, args);
31
+ }
32
+ warn(message, args) {
33
+ this._log('warn', message, args);
21
34
  }
22
35
  }
23
36
  exports.Logger = Logger;
@@ -21,7 +21,7 @@ function parseArgs(arg) {
21
21
  return arg;
22
22
  }
23
23
  }
24
- async function runTypescriptModule(filePath, args) {
24
+ async function runTypescriptModule(filePath, event) {
25
25
  try {
26
26
  // eslint-disable-next-line @typescript-eslint/no-require-imports
27
27
  const module = require(path_1.default.resolve(filePath));
@@ -29,17 +29,16 @@ async function runTypescriptModule(filePath, args) {
29
29
  if (typeof module.handler !== 'function') {
30
30
  throw new Error(`Function handler not found in module ${filePath}`);
31
31
  }
32
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
33
- const { stateConfig, ...event } = args;
34
32
  const { traceId, flows } = event;
35
- const logger = new logger_1.Logger(traceId, flows, filePath.split('/').pop());
36
33
  const sender = new rpc_1.RpcSender(process);
34
+ const logger = new logger_1.Logger(traceId, flows, filePath, sender);
37
35
  const state = new rpc_state_manager_1.RpcStateManager(sender);
38
36
  const emit = async (data) => sender.send('emit', data);
39
37
  const context = { traceId, flows, logger, state, emit };
40
38
  sender.init();
41
39
  // Call the function with provided arguments
42
40
  await module.handler(event.data, context);
41
+ await sender.close();
43
42
  process.exit(0);
44
43
  }
45
44
  catch (error) {
@@ -2,6 +2,8 @@ export declare class RpcSender {
2
2
  private readonly process;
3
3
  private readonly pendingRequests;
4
4
  constructor(process: NodeJS.Process);
5
+ close(): Promise<void>;
5
6
  send<T>(method: string, args: unknown): Promise<T>;
7
+ sendNoWait(method: string, args: unknown): void;
6
8
  init(): void;
7
9
  }
@@ -11,13 +11,23 @@ class RpcSender {
11
11
  this.process = process;
12
12
  this.pendingRequests = {};
13
13
  }
14
+ async close() {
15
+ const outstandingRequests = Object.values(this.pendingRequests);
16
+ if (outstandingRequests.length > 0) {
17
+ console.error('Process ended while there are some promises outstanding');
18
+ this.process.exit(1);
19
+ }
20
+ }
14
21
  send(method, args) {
15
22
  return new Promise((resolve, reject) => {
16
23
  const id = crypto_1.default.randomUUID();
17
24
  this.pendingRequests[id] = { resolve, reject, method, args };
18
- process.send?.({ type: 'rpc_request', id, method, args });
25
+ this.process.send?.({ type: 'rpc_request', id, method, args });
19
26
  });
20
27
  }
28
+ sendNoWait(method, args) {
29
+ this.process.send?.({ type: 'rpc_request', method, args });
30
+ }
21
31
  init() {
22
32
  this.process.on('message', (msg) => {
23
33
  if (msg.type === 'rpc_response') {
@@ -0,0 +1,17 @@
1
+ import { ValidationError } from './step-validator';
2
+ import { Step } from './types';
3
+ export declare class Printer {
4
+ private readonly baseDir;
5
+ constructor(baseDir: string);
6
+ printInvalidEmit(step: Step, emit: string): void;
7
+ printStepCreated(step: Step): void;
8
+ printStepUpdated(step: Step): void;
9
+ printStepRemoved(step: Step): void;
10
+ printFlowCreated(flowName: string): void;
11
+ printFlowUpdated(flowName: string): void;
12
+ printFlowRemoved(flowName: string): void;
13
+ printValidationError(stepPath: string, validationError: ValidationError): void;
14
+ private getRelativePath;
15
+ private getStepType;
16
+ private getStepPath;
17
+ }