@motiadev/core 0.0.6 → 0.0.7-build.20250529212805

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 (124) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +142 -0
  3. package/dist/index.d.ts +13 -8
  4. package/dist/index.js +28 -8
  5. package/dist/src/call-step-file.d.ts +17 -2
  6. package/dist/src/call-step-file.js +88 -47
  7. package/dist/src/cron-handler.d.ts +13 -0
  8. package/dist/src/cron-handler.js +98 -0
  9. package/dist/src/event-manager.js +10 -5
  10. package/dist/src/flows-config-endpoint.d.ts +2 -0
  11. package/dist/src/flows-config-endpoint.js +51 -0
  12. package/dist/src/flows-endpoint.d.ts +11 -7
  13. package/dist/src/flows-endpoint.js +53 -22
  14. package/dist/src/generate-trace-id.d.ts +2 -0
  15. package/dist/src/generate-trace-id.js +14 -0
  16. package/dist/src/get-step-config.d.ts +4 -0
  17. package/dist/src/get-step-config.js +88 -0
  18. package/dist/src/guards.d.ts +3 -2
  19. package/dist/src/guards.js +3 -1
  20. package/dist/src/locked-data.d.ts +60 -0
  21. package/dist/src/locked-data.js +278 -0
  22. package/dist/src/logger-factory.d.ts +15 -0
  23. package/dist/src/logger-factory.js +14 -0
  24. package/dist/src/logger.d.ts +21 -15
  25. package/dist/src/logger.js +49 -29
  26. package/dist/src/mermaid-generator.d.ts +4 -0
  27. package/dist/src/mermaid-generator.js +203 -0
  28. package/dist/src/node/get-config.d.ts +1 -2
  29. package/dist/src/node/get-config.js +51 -8
  30. package/dist/src/node/logger.d.ts +10 -6
  31. package/dist/src/node/logger.js +27 -16
  32. package/dist/src/node/middleware-compose.d.ts +1 -0
  33. package/dist/src/node/middleware-compose.js +12 -0
  34. package/dist/src/node/node-runner.js +28 -9
  35. package/dist/src/node/rpc-state-manager.d.ts +3 -2
  36. package/dist/src/node/rpc-state-manager.js +5 -2
  37. package/dist/src/node/rpc.d.ts +3 -1
  38. package/dist/src/node/rpc.js +21 -3
  39. package/dist/src/pretty-print.d.ts +1 -0
  40. package/dist/src/pretty-print.js +66 -0
  41. package/dist/src/printer.d.ts +45 -0
  42. package/dist/src/printer.js +121 -0
  43. package/dist/src/process-communication/communication-config.d.ts +7 -0
  44. package/dist/src/process-communication/communication-config.js +12 -0
  45. package/dist/src/process-communication/process-manager.d.ts +30 -0
  46. package/dist/src/process-communication/process-manager.js +88 -0
  47. package/dist/src/process-communication/rpc-processor-interface.d.ts +9 -0
  48. package/dist/src/process-communication/rpc-processor-interface.js +2 -0
  49. package/dist/src/python/communication_factory.py +35 -0
  50. package/dist/src/python/context.py +21 -0
  51. package/dist/src/python/get-config.py +31 -13
  52. package/dist/src/python/ipc_communication.py +145 -0
  53. package/dist/src/python/logger.py +4 -6
  54. package/dist/src/python/middleware.py +17 -0
  55. package/dist/src/python/python-runner.py +105 -39
  56. package/dist/src/python/rpc.py +33 -68
  57. package/dist/src/python/rpc_communication.py +125 -0
  58. package/dist/src/python/rpc_state_manager.py +49 -11
  59. package/dist/src/python/type_definitions.py +34 -0
  60. package/dist/src/python/validation.py +56 -0
  61. package/dist/src/ruby/get-config.rb +61 -0
  62. package/dist/src/ruby/logger.rb +21 -16
  63. package/dist/src/ruby/rpc.rb +118 -0
  64. package/dist/src/ruby/rpc_state_manager.rb +41 -0
  65. package/dist/src/ruby/ruby-runner.rb +94 -0
  66. package/dist/src/server.d.ts +14 -12
  67. package/dist/src/server.js +149 -28
  68. package/dist/src/socket-server.d.ts +39 -0
  69. package/dist/src/socket-server.js +76 -0
  70. package/dist/src/state/adapters/default-state-adapter.d.ts +7 -3
  71. package/dist/src/state/adapters/default-state-adapter.js +37 -14
  72. package/dist/src/state/adapters/memory-state-adapter.d.ts +14 -0
  73. package/dist/src/state/adapters/memory-state-adapter.js +58 -0
  74. package/dist/src/state/create-state-adapter.d.ts +5 -6
  75. package/dist/src/state/create-state-adapter.js +2 -10
  76. package/dist/src/state/state-adapter.d.ts +4 -4
  77. package/dist/src/state-stream.d.ts +19 -0
  78. package/dist/src/state-stream.js +28 -0
  79. package/dist/src/step-handler-rpc-processor.d.ts +13 -4
  80. package/dist/src/step-handler-rpc-processor.js +33 -3
  81. package/dist/src/step-handler-rpc-stdin-processor.d.ts +22 -0
  82. package/dist/src/step-handler-rpc-stdin-processor.js +84 -0
  83. package/dist/src/step-handlers.d.ts +7 -2
  84. package/dist/src/step-handlers.js +47 -19
  85. package/dist/src/step-validator.d.ts +14 -0
  86. package/dist/src/step-validator.js +138 -0
  87. package/dist/src/steps/emit.step.d.ts +11 -0
  88. package/dist/src/steps/emit.step.js +27 -0
  89. package/dist/src/steps/emit.step.ts +34 -0
  90. package/dist/src/steps/index.d.ts +2 -0
  91. package/dist/src/steps/index.js +16 -0
  92. package/dist/src/streams/api-endpoints.d.ts +2 -0
  93. package/dist/src/streams/api-endpoints.js +71 -0
  94. package/dist/src/streams/flows-stream.d.ts +14 -0
  95. package/dist/src/streams/flows-stream.js +29 -0
  96. package/dist/src/streams/logs-stream.d.ts +16 -0
  97. package/dist/src/streams/logs-stream.js +22 -0
  98. package/dist/src/types/generate-type-from-schema.d.ts +2 -0
  99. package/dist/src/types/generate-type-from-schema.js +28 -0
  100. package/dist/src/types/generate-types-from-response.d.ts +2 -0
  101. package/dist/src/types/generate-types-from-response.js +13 -0
  102. package/dist/src/types/generate-types.d.ts +12 -0
  103. package/dist/src/types/generate-types.js +108 -0
  104. package/dist/src/types/merge-schemas.d.ts +3 -0
  105. package/dist/src/types/merge-schemas.js +59 -0
  106. package/dist/src/types/schema.types.d.ts +20 -0
  107. package/dist/src/types/schema.types.js +10 -0
  108. package/dist/src/types-stream.d.ts +34 -0
  109. package/dist/src/types-stream.js +2 -0
  110. package/dist/src/types.d.ts +93 -47
  111. package/dist/src/utils.d.ts +2 -0
  112. package/dist/src/utils.js +12 -0
  113. package/package.json +20 -9
  114. package/requirements.txt +1 -0
  115. package/dist/src/node/get-module-export.d.ts +0 -1
  116. package/dist/src/node/get-module-export.js +0 -63
  117. package/dist/src/python/get-python-config.d.ts +0 -2
  118. package/dist/src/python/get-python-config.js +0 -34
  119. package/dist/src/ruby/get_config.rb +0 -78
  120. package/dist/src/ruby/ruby_runner.rb +0 -80
  121. package/dist/src/ruby/state_adapter.rb +0 -62
  122. package/dist/src/state/adapters/redis-state-adapter.d.ts +0 -19
  123. package/dist/src/state/adapters/redis-state-adapter.js +0 -44
  124. package/tsconfig.json +0 -20
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Motia
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,142 @@
1
+ # @motiadev/core
2
+
3
+ Core functionality for the Motia framework, providing the foundation for building event-driven workflows.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @motiadev/core
9
+ # or
10
+ yarn add @motiadev/core
11
+ # or
12
+ pnpm add @motiadev/core
13
+ ```
14
+
15
+ ## Overview
16
+
17
+ `@motiadev/core` is the foundation of the Motia framework, providing:
18
+
19
+ - Event-driven architecture with pub/sub capabilities
20
+ - Multi-language support (TypeScript, Python, Ruby)
21
+ - State management
22
+ - Cron job scheduling
23
+ - API route handling
24
+ - Logging infrastructure
25
+
26
+ ## Key Components
27
+
28
+ ### Server
29
+
30
+ Create and manage an HTTP server for handling API requests:
31
+
32
+ ```typescript
33
+ import { createServer } from '@motiadev/core'
34
+
35
+ const server = await createServer(lockedData, eventManager, stateAdapter, config)
36
+ ```
37
+
38
+ ### Event Management
39
+
40
+ Publish and subscribe to events across your application:
41
+
42
+ ```typescript
43
+ import { createEventManager } from '@motiadev/core'
44
+
45
+ const eventManager = createEventManager()
46
+
47
+ // Subscribe to events
48
+ eventManager.subscribe({
49
+ event: 'user.created',
50
+ handlerName: 'sendWelcomeEmail',
51
+ filePath: '/path/to/handler.ts',
52
+ handler: (event) => {
53
+ // Handle the event
54
+ }
55
+ })
56
+
57
+ // Emit events
58
+ eventManager.emit({
59
+ topic: 'user.created',
60
+ data: { userId: '123' },
61
+ traceId: 'trace-123',
62
+ logger: logger
63
+ })
64
+ ```
65
+
66
+ ### Step Handlers
67
+
68
+ Create handlers for different types of steps (API, Event, Cron):
69
+
70
+ ```typescript
71
+ import { createStepHandlers } from '@motiadev/core'
72
+
73
+ const stepHandlers = createStepHandlers(lockedData, eventManager, state, config)
74
+ ```
75
+
76
+ ### State Management
77
+
78
+ Manage application state with different adapters:
79
+
80
+ ```typescript
81
+ import { createStateAdapter } from '@motiadev/core'
82
+
83
+ const stateAdapter = createStateAdapter({
84
+ adapter: 'redis',
85
+ host: 'localhost',
86
+ port: 6379
87
+ })
88
+
89
+ // Use state in your handlers
90
+ await state.set(traceId, 'key', value)
91
+ const value = await state.get(traceId, 'key')
92
+ ```
93
+
94
+ ### Cron Jobs
95
+
96
+ Schedule and manage cron jobs:
97
+
98
+ ```typescript
99
+ import { setupCronHandlers } from '@motiadev/core'
100
+
101
+ const cronManager = setupCronHandlers(lockedData, eventManager, state, loggerFactory)
102
+ ```
103
+
104
+ ### Logging
105
+
106
+ Use the built-in logging system:
107
+
108
+ ```typescript
109
+ import { globalLogger } from '@motiadev/core'
110
+
111
+ globalLogger.info('Application started')
112
+ globalLogger.error('Something went wrong', { error: err })
113
+ ```
114
+
115
+ ## Multi-language Support
116
+
117
+ Motia supports writing step handlers in multiple languages:
118
+
119
+ - TypeScript/JavaScript
120
+ - Python
121
+ - Ruby
122
+
123
+ Each language has its own runner that communicates with the core framework.
124
+
125
+ ## Types
126
+
127
+ The package exports TypeScript types for all components:
128
+
129
+ ```typescript
130
+ import {
131
+ Event,
132
+ FlowContext,
133
+ ApiRouteConfig,
134
+ EventConfig,
135
+ CronConfig
136
+ } from '@motiadev/core'
137
+ ```
138
+
139
+ ## License
140
+
141
+ This package is part of the Motia framework and is licensed under the same terms.
142
+ ```
package/dist/index.d.ts CHANGED
@@ -1,9 +1,14 @@
1
1
  export * from './src/types';
2
- export * from './src/server';
3
- export * from './src/step-handlers';
4
- export * from './src/event-manager';
5
- export * from './src/logger';
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';
2
+ export { createServer, MotiaServer } from './src/server';
3
+ export { createStepHandlers, MotiaEventManager } from './src/step-handlers';
4
+ export { createEventManager } from './src/event-manager';
5
+ export { globalLogger, Logger } from './src/logger';
6
+ export { createStateAdapter } from './src/state/create-state-adapter';
7
+ export { setupCronHandlers, CronManager } from './src/cron-handler';
8
+ export { isApiStep, isCronStep, isEventStep, isNoopStep } from './src/guards';
9
+ export { LockedData } from './src/locked-data';
10
+ export { getStepConfig, getStreamConfig } from './src/get-step-config';
11
+ export { StateAdapter } from './src/state/state-adapter';
12
+ export { createMermaidGenerator } from './src/mermaid-generator';
13
+ export { StateStream } from './src/state-stream';
14
+ export { StateStreamConfig, IStateStream } from './src/types-stream';
package/dist/index.js CHANGED
@@ -14,12 +14,32 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.StateStream = exports.createMermaidGenerator = exports.getStreamConfig = exports.getStepConfig = exports.LockedData = exports.isNoopStep = exports.isEventStep = exports.isCronStep = exports.isApiStep = exports.setupCronHandlers = exports.createStateAdapter = exports.Logger = exports.globalLogger = exports.createEventManager = exports.createStepHandlers = exports.createServer = void 0;
17
18
  __exportStar(require("./src/types"), exports);
18
- __exportStar(require("./src/server"), exports);
19
- __exportStar(require("./src/step-handlers"), exports);
20
- __exportStar(require("./src/event-manager"), exports);
21
- __exportStar(require("./src/logger"), exports);
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);
19
+ var server_1 = require("./src/server");
20
+ Object.defineProperty(exports, "createServer", { enumerable: true, get: function () { return server_1.createServer; } });
21
+ var step_handlers_1 = require("./src/step-handlers");
22
+ Object.defineProperty(exports, "createStepHandlers", { enumerable: true, get: function () { return step_handlers_1.createStepHandlers; } });
23
+ var event_manager_1 = require("./src/event-manager");
24
+ Object.defineProperty(exports, "createEventManager", { enumerable: true, get: function () { return event_manager_1.createEventManager; } });
25
+ var logger_1 = require("./src/logger");
26
+ Object.defineProperty(exports, "globalLogger", { enumerable: true, get: function () { return logger_1.globalLogger; } });
27
+ Object.defineProperty(exports, "Logger", { enumerable: true, get: function () { return logger_1.Logger; } });
28
+ var create_state_adapter_1 = require("./src/state/create-state-adapter");
29
+ Object.defineProperty(exports, "createStateAdapter", { enumerable: true, get: function () { return create_state_adapter_1.createStateAdapter; } });
30
+ var cron_handler_1 = require("./src/cron-handler");
31
+ Object.defineProperty(exports, "setupCronHandlers", { enumerable: true, get: function () { return cron_handler_1.setupCronHandlers; } });
32
+ var guards_1 = require("./src/guards");
33
+ Object.defineProperty(exports, "isApiStep", { enumerable: true, get: function () { return guards_1.isApiStep; } });
34
+ Object.defineProperty(exports, "isCronStep", { enumerable: true, get: function () { return guards_1.isCronStep; } });
35
+ Object.defineProperty(exports, "isEventStep", { enumerable: true, get: function () { return guards_1.isEventStep; } });
36
+ Object.defineProperty(exports, "isNoopStep", { enumerable: true, get: function () { return guards_1.isNoopStep; } });
37
+ var locked_data_1 = require("./src/locked-data");
38
+ Object.defineProperty(exports, "LockedData", { enumerable: true, get: function () { return locked_data_1.LockedData; } });
39
+ var get_step_config_1 = require("./src/get-step-config");
40
+ Object.defineProperty(exports, "getStepConfig", { enumerable: true, get: function () { return get_step_config_1.getStepConfig; } });
41
+ Object.defineProperty(exports, "getStreamConfig", { enumerable: true, get: function () { return get_step_config_1.getStreamConfig; } });
42
+ var mermaid_generator_1 = require("./src/mermaid-generator");
43
+ Object.defineProperty(exports, "createMermaidGenerator", { enumerable: true, get: function () { return mermaid_generator_1.createMermaidGenerator; } });
44
+ var state_stream_1 = require("./src/state-stream");
45
+ Object.defineProperty(exports, "StateStream", { enumerable: true, get: function () { return state_stream_1.StateStream; } });
@@ -1,2 +1,17 @@
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 { EventManager, InternalStateManager, Step } from './types';
2
+ import { LockedData } from './locked-data';
3
+ import { BaseLogger } from './logger';
4
+ import { Printer } from './printer';
5
+ type CallStepFileOptions = {
6
+ step: Step;
7
+ logger: BaseLogger;
8
+ eventManager: EventManager;
9
+ state: InternalStateManager;
10
+ traceId: string;
11
+ lockedData: LockedData;
12
+ printer: Printer;
13
+ data?: any;
14
+ contextInFirstArg: boolean;
15
+ };
16
+ export declare const callStepFile: <TData>(options: CallStepFileOptions) => Promise<TData | undefined>;
17
+ export {};
@@ -4,68 +4,109 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.callStepFile = void 0;
7
- const step_handler_rpc_processor_1 = require("./step-handler-rpc-processor");
8
- const child_process_1 = require("child_process");
9
7
  const path_1 = __importDefault(require("path"));
10
- const nodeRunner = path_1.default.join(__dirname, 'node', 'node-runner.js');
11
- const pythonRunner = path_1.default.join(__dirname, 'python', 'python-runner.py');
12
- const rubyRunner = path_1.default.join(__dirname, 'ruby', 'ruby_runner.rb');
8
+ const utils_1 = require("./utils");
9
+ const process_manager_1 = require("./process-communication/process-manager");
13
10
  const getLanguageBasedRunner = (stepFilePath = '') => {
14
11
  const isPython = stepFilePath.endsWith('.py');
15
12
  const isRuby = stepFilePath.endsWith('.rb');
16
13
  const isNode = stepFilePath.endsWith('.js') || stepFilePath.endsWith('.ts');
17
14
  if (isPython) {
18
- return { runner: pythonRunner, command: 'python' };
15
+ const pythonRunner = path_1.default.join(__dirname, 'python', 'python-runner.py');
16
+ return { runner: pythonRunner, command: 'python', args: [] };
19
17
  }
20
18
  else if (isRuby) {
21
- return { runner: rubyRunner, command: 'ruby' };
19
+ const rubyRunner = path_1.default.join(__dirname, 'ruby', 'ruby-runner.rb');
20
+ return { runner: rubyRunner, command: 'ruby', args: [] };
22
21
  }
23
22
  else if (isNode) {
24
- return { runner: nodeRunner, command: 'node' };
23
+ if (process.env._MOTIA_TEST_MODE === 'true') {
24
+ const nodeRunner = path_1.default.join(__dirname, 'node', 'node-runner.ts');
25
+ return { runner: nodeRunner, command: 'node', args: ['-r', 'ts-node/register'] };
26
+ }
27
+ const nodeRunner = path_1.default.join(__dirname, 'node', 'node-runner.js');
28
+ return { runner: nodeRunner, command: 'node', args: [] };
25
29
  }
26
30
  throw Error(`Unsupported file extension ${stepFilePath}`);
27
31
  };
28
- const callStepFile = (stepPath, step, event, eventManager, state) => {
32
+ const callStepFile = (options) => {
33
+ const { step, printer, eventManager, state, traceId, data, contextInFirstArg, lockedData } = options;
34
+ const logger = options.logger.child({ step: step.config.name });
35
+ const flows = step.config.flows;
29
36
  return new Promise((resolve, reject) => {
30
- const jsonData = JSON.stringify({ ...event });
31
- const { runner, command } = getLanguageBasedRunner(stepPath);
32
- const child = (0, child_process_1.spawn)(command, [runner, stepPath, jsonData], {
33
- stdio: [undefined, undefined, undefined, 'ipc'],
37
+ const streamConfig = lockedData.getStreams();
38
+ const streams = Object.keys(streamConfig).map((name) => ({ name }));
39
+ const jsonData = JSON.stringify({ data, flows, traceId, contextInFirstArg, streams });
40
+ const { runner, command, args } = getLanguageBasedRunner(step.filePath);
41
+ let result;
42
+ // Create process manager with unified communication handling
43
+ const processManager = new process_manager_1.ProcessManager({
44
+ command,
45
+ args: [...args, runner, step.filePath, jsonData],
46
+ logger,
47
+ context: 'StepExecution',
34
48
  });
35
- const rpcProcessor = new step_handler_rpc_processor_1.RpcProcessor(child);
36
- rpcProcessor.handler('log', async (input) => {
37
- event.logger.log(input);
38
- });
39
- rpcProcessor.handler('state.get', (input) => state.get(input.traceId, input.key));
40
- rpcProcessor.handler('state.set', (input) => state.set(input.traceId, input.key, input.value));
41
- rpcProcessor.handler('state.delete', (input) => state.delete(input.traceId, input.key));
42
- rpcProcessor.handler('state.clear', (input) => state.clear(input.traceId));
43
- rpcProcessor.handler('emit', (input) => {
44
- return eventManager.emit({
45
- ...input,
46
- traceId: event.traceId,
47
- flows: event.flows,
48
- logger: event.logger,
49
- }, stepPath);
50
- });
51
- rpcProcessor.init();
52
- child.stdout?.on('data', (data) => {
53
- try {
54
- const message = JSON.parse(data.toString());
55
- event.logger.log(message);
56
- }
57
- catch (error) {
58
- event.logger.info(Buffer.from(data).toString(), { step });
59
- }
60
- });
61
- child.stderr?.on('data', (data) => event.logger.error(Buffer.from(data).toString(), { step }));
62
- child.on('close', (code) => {
63
- if (code !== 0) {
64
- reject(new Error(`Process exited with code ${code}`));
65
- }
66
- else {
67
- resolve();
68
- }
49
+ processManager
50
+ .spawn()
51
+ .then(() => {
52
+ // Register all step handlers
53
+ processManager.handler('close', async () => processManager.kill());
54
+ processManager.handler('log', async (input) => logger.log(input));
55
+ processManager.handler('state.get', (input) => state.get(input.traceId, input.key));
56
+ processManager.handler('state.set', (input) => state.set(input.traceId, input.key, input.value));
57
+ processManager.handler('state.delete', (input) => state.delete(input.traceId, input.key));
58
+ processManager.handler('state.clear', (input) => state.clear(input.traceId));
59
+ processManager.handler(`state.getGroup`, (input) => state.getGroup(input.groupId));
60
+ processManager.handler('result', async (input) => {
61
+ result = input;
62
+ });
63
+ processManager.handler('emit', async (input) => {
64
+ if (!(0, utils_1.isAllowedToEmit)(step, input.topic)) {
65
+ return printer.printInvalidEmit(step, input.topic);
66
+ }
67
+ return eventManager.emit({ ...input, traceId, flows: step.config.flows, logger }, step.filePath);
68
+ });
69
+ Object.entries(streamConfig).forEach(([name, streamFactory]) => {
70
+ const stateStream = streamFactory();
71
+ processManager.handler(`streams.${name}.get`, (input) => stateStream.get(input.groupId, input.id));
72
+ processManager.handler(`streams.${name}.set`, (input) => stateStream.set(input.groupId, input.id, input.data));
73
+ processManager.handler(`streams.${name}.delete`, (input) => stateStream.delete(input.groupId, input.id));
74
+ processManager.handler(`streams.${name}.getGroup`, (input) => stateStream.getGroup(input.groupId));
75
+ });
76
+ processManager.onStdout((data) => {
77
+ try {
78
+ const message = JSON.parse(data.toString());
79
+ logger.log(message);
80
+ }
81
+ catch {
82
+ logger.info(Buffer.from(data).toString());
83
+ }
84
+ });
85
+ // Handle stderr
86
+ processManager.onStderr((data) => logger.error(Buffer.from(data).toString()));
87
+ // Handle process close
88
+ processManager.onProcessClose((code) => {
89
+ processManager.close();
90
+ if (code !== 0 && code !== null) {
91
+ reject(`Process exited with code ${code}`);
92
+ }
93
+ else {
94
+ resolve(result);
95
+ }
96
+ });
97
+ // Handle process errors
98
+ processManager.onProcessError((error) => {
99
+ processManager.close();
100
+ if (error.code === 'ENOENT') {
101
+ reject(`Executable ${command} not found`);
102
+ }
103
+ else {
104
+ reject(error);
105
+ }
106
+ });
107
+ })
108
+ .catch((error) => {
109
+ reject(`Failed to spawn process: ${error}`);
69
110
  });
70
111
  });
71
112
  };
@@ -0,0 +1,13 @@
1
+ import { LoggerFactory } from './logger-factory';
2
+ import { LockedData } from './locked-data';
3
+ import { CronConfig, EventManager, InternalStateManager, Step } 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, state: InternalStateManager, loggerFactory: LoggerFactory) => {
10
+ createCronJob: (step: Step<CronConfig>) => void;
11
+ removeCronJob: (step: Step<CronConfig>) => void;
12
+ close: () => void;
13
+ };
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.setupCronHandlers = void 0;
37
+ const cron = __importStar(require("node-cron"));
38
+ const call_step_file_1 = require("./call-step-file");
39
+ const generate_trace_id_1 = require("./generate-trace-id");
40
+ const logger_1 = require("./logger");
41
+ const setupCronHandlers = (lockedData, eventManager, state, loggerFactory) => {
42
+ const cronJobs = new Map();
43
+ const printer = lockedData.printer;
44
+ const createCronJob = (step) => {
45
+ const { config, filePath } = step;
46
+ const { cron: cronExpression, name: stepName, flows } = config;
47
+ if (!cron.validate(cronExpression)) {
48
+ logger_1.globalLogger.error('[cron handler] invalid cron expression', {
49
+ expression: cronExpression,
50
+ step: stepName,
51
+ });
52
+ return;
53
+ }
54
+ logger_1.globalLogger.debug('[cron handler] setting up cron job', {
55
+ filePath,
56
+ step: stepName,
57
+ cron: cronExpression,
58
+ });
59
+ const task = cron.schedule(cronExpression, async () => {
60
+ const traceId = (0, generate_trace_id_1.generateTraceId)();
61
+ const logger = loggerFactory.create({ traceId, flows, stepName });
62
+ try {
63
+ await (0, call_step_file_1.callStepFile)({
64
+ contextInFirstArg: true,
65
+ lockedData,
66
+ step,
67
+ eventManager,
68
+ printer,
69
+ state,
70
+ traceId,
71
+ logger,
72
+ });
73
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
74
+ }
75
+ catch (error) {
76
+ logger.error('[cron handler] error executing cron job', {
77
+ error: error.message,
78
+ step: step.config.name,
79
+ });
80
+ }
81
+ });
82
+ cronJobs.set(step.filePath, task);
83
+ };
84
+ const removeCronJob = (step) => {
85
+ const task = cronJobs.get(step.filePath);
86
+ if (task) {
87
+ task.stop();
88
+ cronJobs.delete(step.filePath);
89
+ }
90
+ };
91
+ const close = () => {
92
+ cronJobs.forEach((task) => task.stop());
93
+ cronJobs.clear();
94
+ };
95
+ lockedData.cronSteps().forEach(createCronJob);
96
+ return { createCronJob, removeCronJob, close };
97
+ };
98
+ exports.setupCronHandlers = setupCronHandlers;
@@ -5,18 +5,23 @@ const logger_1 = require("./logger");
5
5
  const createEventManager = () => {
6
6
  const handlers = {};
7
7
  const emit = async (event, file) => {
8
- const eventHandlers = handlers[event.type] ?? [];
8
+ const eventHandlers = handlers[event.topic] ?? [];
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;
@@ -0,0 +1,2 @@
1
+ import { Express } from 'express';
2
+ export declare const flowsConfigEndpoint: (app: Express, baseDir: string) => void;
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.flowsConfigEndpoint = void 0;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const flowsConfigEndpoint = (app, baseDir) => {
10
+ const configPath = path_1.default.join(baseDir, 'motia-workbench.json');
11
+ const getConfig = () => {
12
+ if (fs_1.default.existsSync(configPath)) {
13
+ return JSON.parse(fs_1.default.readFileSync(configPath, 'utf8'));
14
+ }
15
+ return {};
16
+ };
17
+ app.post('/flows/:id/config', (req, res) => {
18
+ const newFlowConfig = req.body;
19
+ try {
20
+ const existingConfig = getConfig();
21
+ const updatedConfig = {
22
+ ...existingConfig,
23
+ };
24
+ Object.entries(newFlowConfig).forEach(([flowName, filePathPositions]) => {
25
+ updatedConfig[flowName] = {
26
+ ...(updatedConfig[flowName] || {}),
27
+ ...filePathPositions,
28
+ };
29
+ });
30
+ fs_1.default.writeFileSync(configPath, JSON.stringify(updatedConfig, null, 2));
31
+ res.status(200).send({ message: 'Flow config saved successfully' });
32
+ }
33
+ catch (error) {
34
+ console.error('Error saving flow config:', error.message);
35
+ res.status(500).json({ error: 'Failed to save flow config' });
36
+ }
37
+ });
38
+ app.get('/flows/:id/config', (req, res) => {
39
+ const { id } = req.params;
40
+ try {
41
+ const allFlowsConfig = getConfig();
42
+ const flowConfig = allFlowsConfig[id] || {};
43
+ res.status(200).send(flowConfig);
44
+ }
45
+ catch (error) {
46
+ console.error('Error reading flow config:', error.message);
47
+ res.status(400).send({ error: 'Failed to read flow config' });
48
+ }
49
+ });
50
+ };
51
+ exports.flowsConfigEndpoint = flowsConfigEndpoint;
@@ -1,13 +1,15 @@
1
1
  import { Express } from 'express';
2
- import { Emit, LockedData } from './types';
2
+ import { LockedData } from './locked-data';
3
+ import { Emit, Step } from './types';
3
4
  type FlowListResponse = {
4
5
  id: string;
5
6
  name: string;
6
7
  };
7
8
  export type EdgeData = {
8
- label?: string;
9
9
  variant: 'event' | 'virtual';
10
- emitType: string;
10
+ topic: string;
11
+ label?: string;
12
+ labelVariant?: 'default' | 'conditional';
11
13
  };
12
14
  type FlowEdge = {
13
15
  id: string;
@@ -18,21 +20,23 @@ type FlowEdge = {
18
20
  type FlowStepResponse = {
19
21
  id: string;
20
22
  name: string;
21
- type: 'event' | 'api' | 'noop';
23
+ type: 'event' | 'api' | 'noop' | 'cron';
22
24
  description?: string;
23
25
  subscribes?: string[];
24
26
  emits: Emit[];
25
27
  virtualEmits?: Emit[];
26
28
  action?: 'webhook';
27
29
  webhookUrl?: string;
28
- bodySchema?: any;
30
+ bodySchema?: unknown;
29
31
  language?: string;
30
32
  nodeComponentPath?: string;
33
+ filePath?: string;
34
+ cronExpression?: string;
31
35
  };
32
36
  type FlowResponse = FlowListResponse & {
33
37
  steps: FlowStepResponse[];
34
38
  edges: FlowEdge[];
35
39
  };
36
- export declare const flowsEndpoint: (flows: LockedData["flows"], app: Express) => void;
37
- export declare const generateFlowsList: (flows: LockedData["flows"]) => FlowResponse[];
40
+ export declare const flowsEndpoint: (lockedData: LockedData, app: Express) => void;
41
+ export declare const generateFlow: (flowId: string, flowSteps: Step[]) => FlowResponse;
38
42
  export {};