@motiadev/core 0.8.2-beta.140-246724 → 0.8.2-beta.140-131607

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -2,6 +2,7 @@ export * from './src/types';
2
2
  export { createServer, MotiaServer } from './src/server';
3
3
  export { createStepHandlers, MotiaEventManager } from './src/step-handlers';
4
4
  export { createEventManager } from './src/event-manager';
5
+ export { queueManager } from './src/queue-manager';
5
6
  export { Logger } from './src/logger';
6
7
  export { createStateAdapter } from './src/state/create-state-adapter';
7
8
  export { setupCronHandlers, CronManager } from './src/cron-handler';
@@ -15,3 +16,5 @@ export { getProjectIdentifier, getUserIdentifier, isAnalyticsEnabled, trackEvent
15
16
  export { Motia } from './src/motia';
16
17
  export { NoPrinter, Printer } from './src/printer';
17
18
  export { NoTracer } from './src/observability/no-tracer';
19
+ export { config } from './src/config';
20
+ export { validateInfrastructureConfig, type InfrastructureValidationError, type InfrastructureValidationResult, } from './src/infrastructure-validator';
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ 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.NoTracer = exports.Printer = exports.NoPrinter = exports.trackEvent = exports.isAnalyticsEnabled = exports.getUserIdentifier = exports.getProjectIdentifier = exports.createMermaidGenerator = exports.getStreamConfig = exports.getStepConfig = exports.LockedData = exports.isNoopStep = exports.isEventStep = exports.isCronStep = exports.isApiStep = exports.setupCronHandlers = exports.createStateAdapter = exports.Logger = exports.createEventManager = exports.createStepHandlers = exports.createServer = void 0;
17
+ exports.validateInfrastructureConfig = exports.config = exports.NoTracer = exports.Printer = exports.NoPrinter = exports.trackEvent = exports.isAnalyticsEnabled = exports.getUserIdentifier = exports.getProjectIdentifier = exports.createMermaidGenerator = exports.getStreamConfig = exports.getStepConfig = exports.LockedData = exports.isNoopStep = exports.isEventStep = exports.isCronStep = exports.isApiStep = exports.setupCronHandlers = exports.createStateAdapter = exports.Logger = exports.queueManager = exports.createEventManager = exports.createStepHandlers = exports.createServer = void 0;
18
18
  __exportStar(require("./src/types"), exports);
19
19
  var server_1 = require("./src/server");
20
20
  Object.defineProperty(exports, "createServer", { enumerable: true, get: function () { return server_1.createServer; } });
@@ -22,6 +22,8 @@ var step_handlers_1 = require("./src/step-handlers");
22
22
  Object.defineProperty(exports, "createStepHandlers", { enumerable: true, get: function () { return step_handlers_1.createStepHandlers; } });
23
23
  var event_manager_1 = require("./src/event-manager");
24
24
  Object.defineProperty(exports, "createEventManager", { enumerable: true, get: function () { return event_manager_1.createEventManager; } });
25
+ var queue_manager_1 = require("./src/queue-manager");
26
+ Object.defineProperty(exports, "queueManager", { enumerable: true, get: function () { return queue_manager_1.queueManager; } });
25
27
  var logger_1 = require("./src/logger");
26
28
  Object.defineProperty(exports, "Logger", { enumerable: true, get: function () { return logger_1.Logger; } });
27
29
  var create_state_adapter_1 = require("./src/state/create-state-adapter");
@@ -50,3 +52,7 @@ Object.defineProperty(exports, "NoPrinter", { enumerable: true, get: function ()
50
52
  Object.defineProperty(exports, "Printer", { enumerable: true, get: function () { return printer_1.Printer; } });
51
53
  var no_tracer_1 = require("./src/observability/no-tracer");
52
54
  Object.defineProperty(exports, "NoTracer", { enumerable: true, get: function () { return no_tracer_1.NoTracer; } });
55
+ var config_1 = require("./src/config");
56
+ Object.defineProperty(exports, "config", { enumerable: true, get: function () { return config_1.config; } });
57
+ var infrastructure_validator_1 = require("./src/infrastructure-validator");
58
+ Object.defineProperty(exports, "validateInfrastructureConfig", { enumerable: true, get: function () { return infrastructure_validator_1.validateInfrastructureConfig; } });
@@ -1,5 +1,5 @@
1
1
  import { Motia } from './motia';
2
- import { Step } from './types';
2
+ import { Step, InfrastructureConfig } from './types';
3
3
  import { Logger } from './logger';
4
4
  import { Tracer } from './observability';
5
5
  type CallStepFileOptions = {
@@ -9,6 +9,7 @@ type CallStepFileOptions = {
9
9
  contextInFirstArg?: boolean;
10
10
  logger: Logger;
11
11
  tracer: Tracer;
12
+ infrastructure?: Partial<InfrastructureConfig>;
12
13
  };
13
14
  export declare const callStepFile: <TData>(options: CallStepFileOptions, motia: Motia) => Promise<TData | undefined>;
14
15
  export {};
@@ -31,7 +31,7 @@ const getLanguageBasedRunner = (stepFilePath = '') => {
31
31
  throw Error(`Unsupported file extension ${stepFilePath}`);
32
32
  };
33
33
  const callStepFile = (options, motia) => {
34
- const { step, traceId, data, tracer, logger, contextInFirstArg = false } = options;
34
+ const { step, traceId, data, tracer, logger, contextInFirstArg = false, infrastructure } = options;
35
35
  const flows = step.config.flows;
36
36
  return new Promise((resolve, reject) => {
37
37
  const streamConfig = motia.lockedData.getStreams();
@@ -39,6 +39,7 @@ const callStepFile = (options, motia) => {
39
39
  const jsonData = JSON.stringify({ data, flows, traceId, contextInFirstArg, streams });
40
40
  const { runner, command, args } = getLanguageBasedRunner(step.filePath);
41
41
  let result;
42
+ let timeoutId;
42
43
  const processManager = new process_manager_1.ProcessManager({
43
44
  command,
44
45
  args: [...args, runner, step.filePath, jsonData],
@@ -52,6 +53,21 @@ const callStepFile = (options, motia) => {
52
53
  type: step.config.type,
53
54
  streams: streams.length,
54
55
  });
56
+ const timeoutSeconds = infrastructure?.handler?.timeout;
57
+ if (timeoutSeconds) {
58
+ timeoutId = setTimeout(() => {
59
+ processManager.kill();
60
+ const errorMessage = `Step execution timed out after ${timeoutSeconds} seconds`;
61
+ logger.error(errorMessage, { step: step.config.name, timeout: timeoutSeconds });
62
+ tracer.end({ message: errorMessage });
63
+ (0, utils_1.trackEvent)('step_execution_timeout', {
64
+ stepName: step.config.name,
65
+ traceId,
66
+ timeout: timeoutSeconds,
67
+ });
68
+ reject(new Error(errorMessage));
69
+ }, timeoutSeconds * 1000);
70
+ }
55
71
  processManager
56
72
  .spawn()
57
73
  .then(() => {
@@ -63,13 +79,12 @@ const callStepFile = (options, motia) => {
63
79
  traceId,
64
80
  message: err.message,
65
81
  });
66
- }
67
- if (err) {
68
82
  tracer.end({
69
83
  message: err.message,
70
84
  code: err.code,
71
85
  stack: err.stack?.replace(new RegExp(`${motia.lockedData.baseDir}/`), ''),
72
86
  });
87
+ reject(new Error(err.message || 'Handler execution failed'));
73
88
  }
74
89
  else {
75
90
  tracer.end();
@@ -147,6 +162,8 @@ const callStepFile = (options, motia) => {
147
162
  });
148
163
  processManager.onStderr((data) => logger.error(Buffer.from(data).toString()));
149
164
  processManager.onProcessClose((code) => {
165
+ if (timeoutId)
166
+ clearTimeout(timeoutId);
150
167
  processManager.close();
151
168
  if (code !== 0 && code !== null) {
152
169
  const error = { message: `Process exited with code ${code}`, code };
@@ -160,6 +177,8 @@ const callStepFile = (options, motia) => {
160
177
  }
161
178
  });
162
179
  processManager.onProcessError((error) => {
180
+ if (timeoutId)
181
+ clearTimeout(timeoutId);
163
182
  processManager.close();
164
183
  tracer.end({
165
184
  message: error.message,
@@ -181,6 +200,8 @@ const callStepFile = (options, motia) => {
181
200
  });
182
201
  })
183
202
  .catch((error) => {
203
+ if (timeoutId)
204
+ clearTimeout(timeoutId);
184
205
  tracer.end({
185
206
  message: error.message,
186
207
  code: error.code,
@@ -0,0 +1,2 @@
1
+ import { Config } from './types/app-config-types';
2
+ export declare const config: (config: Config) => Config;
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.config = void 0;
4
+ const config = (config) => config;
5
+ exports.config = config;
@@ -1,26 +1,32 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createEventManager = void 0;
4
- const logger_1 = require("./logger");
4
+ const queue_manager_1 = require("./queue-manager");
5
+ const defaults_1 = require("./infrastructure-validator/defaults");
5
6
  const createEventManager = () => {
6
7
  const handlers = {};
7
8
  const emit = async (event, file) => {
8
- const eventHandlers = handlers[event.topic] ?? [];
9
- const { logger, ...rest } = event;
10
- logger.debug('[Flow Emit] Event emitted', { handlers: eventHandlers.length, data: rest, file });
11
- eventHandlers.map((eventHandler) => eventHandler.handler(event));
9
+ await queue_manager_1.queueManager.enqueue(event.topic, event);
12
10
  };
13
11
  const subscribe = (config) => {
14
- const { event, handlerName, handler, filePath } = config;
12
+ const { event, handler, filePath } = config;
15
13
  if (!handlers[event]) {
16
14
  handlers[event] = [];
17
15
  }
18
- logger_1.globalLogger.debug('[Flow Sub] Subscribing to event', { event, handlerName });
19
16
  handlers[event].push({ filePath, handler: handler });
17
+ queue_manager_1.queueManager.subscribe(event, handler, defaults_1.DEFAULT_QUEUE_CONFIG, filePath);
20
18
  };
21
19
  const unsubscribe = (config) => {
22
20
  const { filePath, event } = config;
23
- handlers[event] = handlers[event]?.filter((handler) => handler.filePath !== filePath);
21
+ const eventHandlers = handlers[event];
22
+ if (!eventHandlers) {
23
+ return;
24
+ }
25
+ const handlerToRemove = eventHandlers.find((h) => h.filePath === filePath);
26
+ if (handlerToRemove) {
27
+ queue_manager_1.queueManager.unsubscribe(event, handlerToRemove.handler);
28
+ }
29
+ handlers[event] = eventHandlers.filter((handler) => handler.filePath !== filePath);
24
30
  };
25
31
  return { emit, subscribe, unsubscribe };
26
32
  };
@@ -0,0 +1,3 @@
1
+ import { InfrastructureConfig, QueueConfig } from '../types';
2
+ export declare const DEFAULT_QUEUE_CONFIG: QueueConfig;
3
+ export declare function getQueueConfigWithDefaults(infrastructure?: InfrastructureConfig): QueueConfig;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_QUEUE_CONFIG = void 0;
4
+ exports.getQueueConfigWithDefaults = getQueueConfigWithDefaults;
5
+ exports.DEFAULT_QUEUE_CONFIG = {
6
+ type: 'standard',
7
+ maxRetries: 3,
8
+ visibilityTimeout: 900,
9
+ messageGroupId: null,
10
+ delaySeconds: 0,
11
+ };
12
+ function getQueueConfigWithDefaults(infrastructure) {
13
+ return {
14
+ ...exports.DEFAULT_QUEUE_CONFIG,
15
+ ...(infrastructure?.queue || {}),
16
+ };
17
+ }
@@ -0,0 +1,3 @@
1
+ export { AWS_LAMBDA_LIMITS, AWS_LAMBDA_CPU_RATIO, getProportionalCpu, handlerBaseSchema, handlerSchema, queueSchema, infrastructureSchema, createInfrastructureSchema, } from './schemas';
2
+ export { type InfrastructureValidationError, type InfrastructureValidationResult } from './types';
3
+ export { validateInfrastructureConfig } from './validations';
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateInfrastructureConfig = exports.createInfrastructureSchema = exports.infrastructureSchema = exports.queueSchema = exports.handlerSchema = exports.handlerBaseSchema = exports.getProportionalCpu = exports.AWS_LAMBDA_CPU_RATIO = exports.AWS_LAMBDA_LIMITS = void 0;
4
+ var schemas_1 = require("./schemas");
5
+ Object.defineProperty(exports, "AWS_LAMBDA_LIMITS", { enumerable: true, get: function () { return schemas_1.AWS_LAMBDA_LIMITS; } });
6
+ Object.defineProperty(exports, "AWS_LAMBDA_CPU_RATIO", { enumerable: true, get: function () { return schemas_1.AWS_LAMBDA_CPU_RATIO; } });
7
+ Object.defineProperty(exports, "getProportionalCpu", { enumerable: true, get: function () { return schemas_1.getProportionalCpu; } });
8
+ Object.defineProperty(exports, "handlerBaseSchema", { enumerable: true, get: function () { return schemas_1.handlerBaseSchema; } });
9
+ Object.defineProperty(exports, "handlerSchema", { enumerable: true, get: function () { return schemas_1.handlerSchema; } });
10
+ Object.defineProperty(exports, "queueSchema", { enumerable: true, get: function () { return schemas_1.queueSchema; } });
11
+ Object.defineProperty(exports, "infrastructureSchema", { enumerable: true, get: function () { return schemas_1.infrastructureSchema; } });
12
+ Object.defineProperty(exports, "createInfrastructureSchema", { enumerable: true, get: function () { return schemas_1.createInfrastructureSchema; } });
13
+ var validations_1 = require("./validations");
14
+ Object.defineProperty(exports, "validateInfrastructureConfig", { enumerable: true, get: function () { return validations_1.validateInfrastructureConfig; } });
@@ -0,0 +1,308 @@
1
+ import { z } from 'zod';
2
+ import { ZodInput } from '../types';
3
+ export declare const AWS_LAMBDA_LIMITS: {
4
+ readonly MIN_RAM_MB: 128;
5
+ readonly MAX_RAM_MB: 10240;
6
+ readonly MIN_TIMEOUT_SECONDS: 1;
7
+ readonly MAX_TIMEOUT_SECONDS: 900;
8
+ };
9
+ export declare const AWS_LAMBDA_CPU_RATIO: Record<number, number>;
10
+ export declare function getProportionalCpu(ramMb: number): number;
11
+ export declare const handlerBaseSchema: z.ZodObject<{
12
+ ram: z.ZodNumber;
13
+ timeout: z.ZodNumber;
14
+ cpu: z.ZodOptional<z.ZodNumber>;
15
+ }, "strip", z.ZodTypeAny, {
16
+ ram: number;
17
+ timeout: number;
18
+ cpu?: number | undefined;
19
+ }, {
20
+ ram: number;
21
+ timeout: number;
22
+ cpu?: number | undefined;
23
+ }>;
24
+ export declare const handlerSchema: z.ZodEffects<z.ZodObject<{
25
+ ram: z.ZodOptional<z.ZodNumber>;
26
+ timeout: z.ZodOptional<z.ZodNumber>;
27
+ cpu: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
28
+ }, "strip", z.ZodTypeAny, {
29
+ ram?: number | undefined;
30
+ timeout?: number | undefined;
31
+ cpu?: number | undefined;
32
+ }, {
33
+ ram?: number | undefined;
34
+ timeout?: number | undefined;
35
+ cpu?: number | undefined;
36
+ }>, {
37
+ ram?: number | undefined;
38
+ timeout?: number | undefined;
39
+ cpu?: number | undefined;
40
+ }, {
41
+ ram?: number | undefined;
42
+ timeout?: number | undefined;
43
+ cpu?: number | undefined;
44
+ }>;
45
+ export declare const queueSchema: z.ZodObject<{
46
+ type: z.ZodEnum<["standard", "fifo"]>;
47
+ visibilityTimeout: z.ZodNumber;
48
+ messageGroupId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
49
+ maxRetries: z.ZodNumber;
50
+ delaySeconds: z.ZodOptional<z.ZodNumber>;
51
+ }, "strip", z.ZodTypeAny, {
52
+ type: "standard" | "fifo";
53
+ visibilityTimeout: number;
54
+ maxRetries: number;
55
+ messageGroupId?: string | null | undefined;
56
+ delaySeconds?: number | undefined;
57
+ }, {
58
+ type: "standard" | "fifo";
59
+ visibilityTimeout: number;
60
+ maxRetries: number;
61
+ messageGroupId?: string | null | undefined;
62
+ delaySeconds?: number | undefined;
63
+ }>;
64
+ export declare const infrastructureSchema: z.ZodEffects<z.ZodObject<{
65
+ handler: z.ZodOptional<z.ZodEffects<z.ZodObject<{
66
+ ram: z.ZodOptional<z.ZodNumber>;
67
+ timeout: z.ZodOptional<z.ZodNumber>;
68
+ cpu: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
69
+ }, "strip", z.ZodTypeAny, {
70
+ ram?: number | undefined;
71
+ timeout?: number | undefined;
72
+ cpu?: number | undefined;
73
+ }, {
74
+ ram?: number | undefined;
75
+ timeout?: number | undefined;
76
+ cpu?: number | undefined;
77
+ }>, {
78
+ ram?: number | undefined;
79
+ timeout?: number | undefined;
80
+ cpu?: number | undefined;
81
+ }, {
82
+ ram?: number | undefined;
83
+ timeout?: number | undefined;
84
+ cpu?: number | undefined;
85
+ }>>;
86
+ queue: z.ZodOptional<z.ZodObject<{
87
+ type: z.ZodOptional<z.ZodEnum<["standard", "fifo"]>>;
88
+ visibilityTimeout: z.ZodOptional<z.ZodNumber>;
89
+ messageGroupId: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
90
+ maxRetries: z.ZodOptional<z.ZodNumber>;
91
+ delaySeconds: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
92
+ }, "strip", z.ZodTypeAny, {
93
+ type?: "standard" | "fifo" | undefined;
94
+ visibilityTimeout?: number | undefined;
95
+ messageGroupId?: string | null | undefined;
96
+ maxRetries?: number | undefined;
97
+ delaySeconds?: number | undefined;
98
+ }, {
99
+ type?: "standard" | "fifo" | undefined;
100
+ visibilityTimeout?: number | undefined;
101
+ messageGroupId?: string | null | undefined;
102
+ maxRetries?: number | undefined;
103
+ delaySeconds?: number | undefined;
104
+ }>>;
105
+ }, "strip", z.ZodTypeAny, {
106
+ handler?: {
107
+ ram?: number | undefined;
108
+ timeout?: number | undefined;
109
+ cpu?: number | undefined;
110
+ } | undefined;
111
+ queue?: {
112
+ type?: "standard" | "fifo" | undefined;
113
+ visibilityTimeout?: number | undefined;
114
+ messageGroupId?: string | null | undefined;
115
+ maxRetries?: number | undefined;
116
+ delaySeconds?: number | undefined;
117
+ } | undefined;
118
+ }, {
119
+ handler?: {
120
+ ram?: number | undefined;
121
+ timeout?: number | undefined;
122
+ cpu?: number | undefined;
123
+ } | undefined;
124
+ queue?: {
125
+ type?: "standard" | "fifo" | undefined;
126
+ visibilityTimeout?: number | undefined;
127
+ messageGroupId?: string | null | undefined;
128
+ maxRetries?: number | undefined;
129
+ delaySeconds?: number | undefined;
130
+ } | undefined;
131
+ }>, {
132
+ handler?: {
133
+ ram?: number | undefined;
134
+ timeout?: number | undefined;
135
+ cpu?: number | undefined;
136
+ } | undefined;
137
+ queue?: {
138
+ type?: "standard" | "fifo" | undefined;
139
+ visibilityTimeout?: number | undefined;
140
+ messageGroupId?: string | null | undefined;
141
+ maxRetries?: number | undefined;
142
+ delaySeconds?: number | undefined;
143
+ } | undefined;
144
+ }, {
145
+ handler?: {
146
+ ram?: number | undefined;
147
+ timeout?: number | undefined;
148
+ cpu?: number | undefined;
149
+ } | undefined;
150
+ queue?: {
151
+ type?: "standard" | "fifo" | undefined;
152
+ visibilityTimeout?: number | undefined;
153
+ messageGroupId?: string | null | undefined;
154
+ maxRetries?: number | undefined;
155
+ delaySeconds?: number | undefined;
156
+ } | undefined;
157
+ }>;
158
+ export declare const createInfrastructureSchema: (inputSchema?: ZodInput) => z.ZodEffects<z.ZodEffects<z.ZodObject<{
159
+ handler: z.ZodOptional<z.ZodEffects<z.ZodObject<{
160
+ ram: z.ZodOptional<z.ZodNumber>;
161
+ timeout: z.ZodOptional<z.ZodNumber>;
162
+ cpu: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
163
+ }, "strip", z.ZodTypeAny, {
164
+ ram?: number | undefined;
165
+ timeout?: number | undefined;
166
+ cpu?: number | undefined;
167
+ }, {
168
+ ram?: number | undefined;
169
+ timeout?: number | undefined;
170
+ cpu?: number | undefined;
171
+ }>, {
172
+ ram?: number | undefined;
173
+ timeout?: number | undefined;
174
+ cpu?: number | undefined;
175
+ }, {
176
+ ram?: number | undefined;
177
+ timeout?: number | undefined;
178
+ cpu?: number | undefined;
179
+ }>>;
180
+ queue: z.ZodOptional<z.ZodObject<{
181
+ type: z.ZodOptional<z.ZodEnum<["standard", "fifo"]>>;
182
+ visibilityTimeout: z.ZodOptional<z.ZodNumber>;
183
+ messageGroupId: z.ZodOptional<z.ZodOptional<z.ZodNullable<z.ZodString>>>;
184
+ maxRetries: z.ZodOptional<z.ZodNumber>;
185
+ delaySeconds: z.ZodOptional<z.ZodOptional<z.ZodNumber>>;
186
+ }, "strip", z.ZodTypeAny, {
187
+ type?: "standard" | "fifo" | undefined;
188
+ visibilityTimeout?: number | undefined;
189
+ messageGroupId?: string | null | undefined;
190
+ maxRetries?: number | undefined;
191
+ delaySeconds?: number | undefined;
192
+ }, {
193
+ type?: "standard" | "fifo" | undefined;
194
+ visibilityTimeout?: number | undefined;
195
+ messageGroupId?: string | null | undefined;
196
+ maxRetries?: number | undefined;
197
+ delaySeconds?: number | undefined;
198
+ }>>;
199
+ }, "strip", z.ZodTypeAny, {
200
+ handler?: {
201
+ ram?: number | undefined;
202
+ timeout?: number | undefined;
203
+ cpu?: number | undefined;
204
+ } | undefined;
205
+ queue?: {
206
+ type?: "standard" | "fifo" | undefined;
207
+ visibilityTimeout?: number | undefined;
208
+ messageGroupId?: string | null | undefined;
209
+ maxRetries?: number | undefined;
210
+ delaySeconds?: number | undefined;
211
+ } | undefined;
212
+ }, {
213
+ handler?: {
214
+ ram?: number | undefined;
215
+ timeout?: number | undefined;
216
+ cpu?: number | undefined;
217
+ } | undefined;
218
+ queue?: {
219
+ type?: "standard" | "fifo" | undefined;
220
+ visibilityTimeout?: number | undefined;
221
+ messageGroupId?: string | null | undefined;
222
+ maxRetries?: number | undefined;
223
+ delaySeconds?: number | undefined;
224
+ } | undefined;
225
+ }>, {
226
+ handler?: {
227
+ ram?: number | undefined;
228
+ timeout?: number | undefined;
229
+ cpu?: number | undefined;
230
+ } | undefined;
231
+ queue?: {
232
+ type?: "standard" | "fifo" | undefined;
233
+ visibilityTimeout?: number | undefined;
234
+ messageGroupId?: string | null | undefined;
235
+ maxRetries?: number | undefined;
236
+ delaySeconds?: number | undefined;
237
+ } | undefined;
238
+ }, {
239
+ handler?: {
240
+ ram?: number | undefined;
241
+ timeout?: number | undefined;
242
+ cpu?: number | undefined;
243
+ } | undefined;
244
+ queue?: {
245
+ type?: "standard" | "fifo" | undefined;
246
+ visibilityTimeout?: number | undefined;
247
+ messageGroupId?: string | null | undefined;
248
+ maxRetries?: number | undefined;
249
+ delaySeconds?: number | undefined;
250
+ } | undefined;
251
+ }>, {
252
+ handler?: {
253
+ ram?: number | undefined;
254
+ timeout?: number | undefined;
255
+ cpu?: number | undefined;
256
+ } | undefined;
257
+ queue?: {
258
+ type?: "standard" | "fifo" | undefined;
259
+ visibilityTimeout?: number | undefined;
260
+ messageGroupId?: string | null | undefined;
261
+ maxRetries?: number | undefined;
262
+ delaySeconds?: number | undefined;
263
+ } | undefined;
264
+ }, {
265
+ handler?: {
266
+ ram?: number | undefined;
267
+ timeout?: number | undefined;
268
+ cpu?: number | undefined;
269
+ } | undefined;
270
+ queue?: {
271
+ type?: "standard" | "fifo" | undefined;
272
+ visibilityTimeout?: number | undefined;
273
+ messageGroupId?: string | null | undefined;
274
+ maxRetries?: number | undefined;
275
+ delaySeconds?: number | undefined;
276
+ } | undefined;
277
+ }>;
278
+ export declare const createQueueSchema: (inputSchema?: ZodInput) => z.ZodEffects<z.ZodObject<{
279
+ type: z.ZodEnum<["standard", "fifo"]>;
280
+ visibilityTimeout: z.ZodNumber;
281
+ messageGroupId: z.ZodOptional<z.ZodNullable<z.ZodString>>;
282
+ maxRetries: z.ZodNumber;
283
+ delaySeconds: z.ZodOptional<z.ZodNumber>;
284
+ }, "strip", z.ZodTypeAny, {
285
+ type: "standard" | "fifo";
286
+ visibilityTimeout: number;
287
+ maxRetries: number;
288
+ messageGroupId?: string | null | undefined;
289
+ delaySeconds?: number | undefined;
290
+ }, {
291
+ type: "standard" | "fifo";
292
+ visibilityTimeout: number;
293
+ maxRetries: number;
294
+ messageGroupId?: string | null | undefined;
295
+ delaySeconds?: number | undefined;
296
+ }>, {
297
+ type: "standard" | "fifo";
298
+ visibilityTimeout: number;
299
+ maxRetries: number;
300
+ messageGroupId?: string | null | undefined;
301
+ delaySeconds?: number | undefined;
302
+ }, {
303
+ type: "standard" | "fifo";
304
+ visibilityTimeout: number;
305
+ maxRetries: number;
306
+ messageGroupId?: string | null | undefined;
307
+ delaySeconds?: number | undefined;
308
+ }>;
@@ -0,0 +1,177 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createQueueSchema = exports.createInfrastructureSchema = exports.infrastructureSchema = exports.queueSchema = exports.handlerSchema = exports.handlerBaseSchema = exports.AWS_LAMBDA_CPU_RATIO = exports.AWS_LAMBDA_LIMITS = void 0;
4
+ exports.getProportionalCpu = getProportionalCpu;
5
+ const zod_1 = require("zod");
6
+ exports.AWS_LAMBDA_LIMITS = {
7
+ MIN_RAM_MB: 128,
8
+ MAX_RAM_MB: 10240,
9
+ MIN_TIMEOUT_SECONDS: 1,
10
+ MAX_TIMEOUT_SECONDS: 900,
11
+ };
12
+ exports.AWS_LAMBDA_CPU_RATIO = {
13
+ 128: 0.0625,
14
+ 256: 0.125,
15
+ 512: 0.25,
16
+ 1024: 0.5,
17
+ 1536: 0.75,
18
+ 2048: 1,
19
+ 3008: 1.5,
20
+ 4096: 2,
21
+ 5120: 2.5,
22
+ 6144: 3,
23
+ 7168: 3.5,
24
+ 8192: 4,
25
+ 9216: 4.5,
26
+ 10240: 5,
27
+ };
28
+ function getProportionalCpu(ramMb) {
29
+ const ramValues = Object.keys(exports.AWS_LAMBDA_CPU_RATIO)
30
+ .map(Number)
31
+ .sort((a, b) => a - b);
32
+ const exact = exports.AWS_LAMBDA_CPU_RATIO[ramMb];
33
+ if (exact !== undefined) {
34
+ return exact;
35
+ }
36
+ for (let i = 0; i < ramValues.length - 1; i++) {
37
+ const lower = ramValues[i];
38
+ const upper = ramValues[i + 1];
39
+ if (ramMb > lower && ramMb < upper) {
40
+ const lowerCpu = exports.AWS_LAMBDA_CPU_RATIO[lower];
41
+ const upperCpu = exports.AWS_LAMBDA_CPU_RATIO[upper];
42
+ const ratio = (ramMb - lower) / (upper - lower);
43
+ return lowerCpu + (upperCpu - lowerCpu) * ratio;
44
+ }
45
+ }
46
+ return ramMb <= ramValues[0]
47
+ ? exports.AWS_LAMBDA_CPU_RATIO[ramValues[0]]
48
+ : exports.AWS_LAMBDA_CPU_RATIO[ramValues[ramValues.length - 1]];
49
+ }
50
+ exports.handlerBaseSchema = zod_1.z.object({
51
+ ram: zod_1.z
52
+ .number()
53
+ .min(exports.AWS_LAMBDA_LIMITS.MIN_RAM_MB, `RAM must be at least ${exports.AWS_LAMBDA_LIMITS.MIN_RAM_MB} MB`)
54
+ .max(exports.AWS_LAMBDA_LIMITS.MAX_RAM_MB, `RAM cannot exceed ${exports.AWS_LAMBDA_LIMITS.MAX_RAM_MB} MB`),
55
+ timeout: zod_1.z
56
+ .number()
57
+ .min(exports.AWS_LAMBDA_LIMITS.MIN_TIMEOUT_SECONDS, `Timeout must be at least ${exports.AWS_LAMBDA_LIMITS.MIN_TIMEOUT_SECONDS}s`)
58
+ .max(exports.AWS_LAMBDA_LIMITS.MAX_TIMEOUT_SECONDS, `Timeout cannot exceed ${exports.AWS_LAMBDA_LIMITS.MAX_TIMEOUT_SECONDS}s`),
59
+ cpu: zod_1.z.number().optional(),
60
+ });
61
+ exports.handlerSchema = exports.handlerBaseSchema.partial().superRefine((handler, ctx) => {
62
+ if (handler.cpu === undefined || handler.ram === undefined) {
63
+ return;
64
+ }
65
+ const expectedCpu = getProportionalCpu(handler.ram);
66
+ const tolerance = 0.1;
67
+ if (Math.abs(handler.cpu - expectedCpu) > tolerance) {
68
+ ctx.addIssue({
69
+ code: zod_1.z.ZodIssueCode.custom,
70
+ path: ['handler', 'cpu'],
71
+ message: `CPU (${handler.cpu} vCPU) is not proportional to RAM (${handler.ram} MB). Expected approximately ${expectedCpu.toFixed(2)} vCPU`,
72
+ });
73
+ }
74
+ });
75
+ exports.queueSchema = zod_1.z.object({
76
+ type: zod_1.z.enum(['standard', 'fifo']),
77
+ visibilityTimeout: zod_1.z.number(),
78
+ messageGroupId: zod_1.z.string().nullable().optional(),
79
+ maxRetries: zod_1.z.number().min(0, 'maxRetries cannot be negative'),
80
+ delaySeconds: zod_1.z
81
+ .number()
82
+ .min(0, 'delaySeconds cannot be negative')
83
+ .max(900, 'delaySeconds cannot exceed 900 seconds (15 minutes)')
84
+ .optional(),
85
+ });
86
+ exports.infrastructureSchema = zod_1.z
87
+ .object({
88
+ handler: exports.handlerSchema.optional(),
89
+ queue: exports.queueSchema.partial().optional(),
90
+ })
91
+ .superRefine((config, ctx) => {
92
+ if (config.queue?.visibilityTimeout !== undefined && config.handler?.timeout !== undefined) {
93
+ if (config.queue.visibilityTimeout <= config.handler.timeout) {
94
+ ctx.addIssue({
95
+ code: zod_1.z.ZodIssueCode.custom,
96
+ path: ['queue', 'visibilityTimeout'],
97
+ message: `Visibility timeout (${config.queue.visibilityTimeout}s) must be greater than handler timeout (${config.handler.timeout}s) to prevent premature message redelivery`,
98
+ });
99
+ }
100
+ }
101
+ if (config.queue?.type === 'fifo' && !config.queue?.messageGroupId) {
102
+ ctx.addIssue({
103
+ code: zod_1.z.ZodIssueCode.custom,
104
+ path: ['queue', 'messageGroupId'],
105
+ message: 'messageGroupId is required when queue type is "fifo"',
106
+ });
107
+ }
108
+ });
109
+ function validateMessageGroupId(messageGroupId, inputSchema, ctx, pathPrefix = ['queue', 'messageGroupId']) {
110
+ if (!messageGroupId || messageGroupId === 'traceId') {
111
+ return;
112
+ }
113
+ if (!inputSchema) {
114
+ ctx.addIssue({
115
+ code: zod_1.z.ZodIssueCode.custom,
116
+ path: pathPrefix,
117
+ message: `Cannot validate messageGroupId "${messageGroupId}" - step has no input schema defined`,
118
+ });
119
+ return;
120
+ }
121
+ if (messageGroupId.includes('.') || messageGroupId.includes('[')) {
122
+ ctx.addIssue({
123
+ code: zod_1.z.ZodIssueCode.custom,
124
+ path: pathPrefix,
125
+ message: `messageGroupId "${messageGroupId}" must be a simple field path. Nested paths and template expressions are not supported`,
126
+ });
127
+ return;
128
+ }
129
+ try {
130
+ if (inputSchema instanceof zod_1.z.ZodObject) {
131
+ const shape = inputSchema.shape;
132
+ if (!shape || !(messageGroupId in shape)) {
133
+ ctx.addIssue({
134
+ code: zod_1.z.ZodIssueCode.custom,
135
+ path: pathPrefix,
136
+ message: `messageGroupId "${messageGroupId}" does not exist in step's input schema`,
137
+ });
138
+ }
139
+ }
140
+ else if (typeof inputSchema === 'object' && inputSchema !== null && 'properties' in inputSchema) {
141
+ const properties = inputSchema.properties;
142
+ if (!properties || typeof properties !== 'object' || !(messageGroupId in properties)) {
143
+ ctx.addIssue({
144
+ code: zod_1.z.ZodIssueCode.custom,
145
+ path: pathPrefix,
146
+ message: `messageGroupId "${messageGroupId}" does not exist in step's input schema`,
147
+ });
148
+ }
149
+ }
150
+ else {
151
+ ctx.addIssue({
152
+ code: zod_1.z.ZodIssueCode.custom,
153
+ path: pathPrefix,
154
+ message: `Failed to validate messageGroupId "${messageGroupId}" against input schema: inputSchema is not a valid schema object`,
155
+ });
156
+ }
157
+ }
158
+ catch (error) {
159
+ ctx.addIssue({
160
+ code: zod_1.z.ZodIssueCode.custom,
161
+ path: pathPrefix,
162
+ message: `Failed to validate messageGroupId "${messageGroupId}" against input schema: ${error instanceof Error ? error.message : 'Unknown error'}`,
163
+ });
164
+ }
165
+ }
166
+ const createInfrastructureSchema = (inputSchema) => {
167
+ return exports.infrastructureSchema.superRefine((config, ctx) => {
168
+ validateMessageGroupId(config.queue?.messageGroupId, inputSchema, ctx, ['queue', 'messageGroupId']);
169
+ });
170
+ };
171
+ exports.createInfrastructureSchema = createInfrastructureSchema;
172
+ const createQueueSchema = (inputSchema) => {
173
+ return exports.queueSchema.superRefine((config, ctx) => {
174
+ validateMessageGroupId(config.messageGroupId, inputSchema, ctx, ['messageGroupId']);
175
+ });
176
+ };
177
+ exports.createQueueSchema = createQueueSchema;
@@ -0,0 +1,20 @@
1
+ export type InfrastructureValidationError = {
2
+ path: string;
3
+ message: string;
4
+ };
5
+ export type InfrastructureValidationResult = {
6
+ success: true;
7
+ } | {
8
+ success: false;
9
+ errors?: InfrastructureValidationError[];
10
+ };
11
+ export type QueueValidationError = {
12
+ path: string;
13
+ message: string;
14
+ };
15
+ export type QueueValidationResult = {
16
+ success: true;
17
+ } | {
18
+ success: false;
19
+ errors?: QueueValidationError[];
20
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,4 @@
1
+ import { InfrastructureValidationResult, QueueValidationResult } from './types';
2
+ import { ZodInput } from '../types';
3
+ export declare const validateQueueConfig: (queueConfig: unknown) => QueueValidationResult;
4
+ export declare const validateInfrastructureConfig: (infrastructureConfig: unknown, inputSchema?: ZodInput) => InfrastructureValidationResult;
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateInfrastructureConfig = exports.validateQueueConfig = void 0;
4
+ const zod_1 = require("zod");
5
+ const schemas_1 = require("./schemas");
6
+ const validateQueueConfig = (queueConfig) => {
7
+ try {
8
+ const schema = (0, schemas_1.createQueueSchema)();
9
+ schema.parse(queueConfig);
10
+ return { success: true };
11
+ }
12
+ catch (error) {
13
+ if (error instanceof zod_1.z.ZodError) {
14
+ const errors = error.errors.map((err) => ({
15
+ path: err.path.length > 0 ? err.path.join('.') : 'queue',
16
+ message: err.message,
17
+ }));
18
+ return {
19
+ success: false,
20
+ errors,
21
+ };
22
+ }
23
+ return {
24
+ success: false,
25
+ errors: [
26
+ { path: 'queue', message: `Unexpected validation error: ${error instanceof Error ? error.message : 'Unknown error'}` },
27
+ ],
28
+ };
29
+ }
30
+ };
31
+ exports.validateQueueConfig = validateQueueConfig;
32
+ const validateInfrastructureConfig = (infrastructureConfig, inputSchema) => {
33
+ try {
34
+ const schema = (0, schemas_1.createInfrastructureSchema)(inputSchema);
35
+ schema.parse(infrastructureConfig);
36
+ return { success: true };
37
+ }
38
+ catch (error) {
39
+ if (error instanceof zod_1.z.ZodError) {
40
+ const errors = error.errors.map((err) => ({
41
+ path: err.path.length > 0 ? err.path.join('.') : 'infrastructure',
42
+ message: err.message,
43
+ }));
44
+ return {
45
+ success: false,
46
+ errors,
47
+ };
48
+ }
49
+ return {
50
+ success: false,
51
+ errors: [
52
+ {
53
+ path: 'infrastructure',
54
+ message: `Unexpected validation error: ${error instanceof Error ? error.message : 'Unknown error'}`,
55
+ },
56
+ ],
57
+ };
58
+ }
59
+ };
60
+ exports.validateInfrastructureConfig = validateInfrastructureConfig;
@@ -0,0 +1,17 @@
1
+ import { Event, QueueConfig, Handler } from './types';
2
+ declare class QueueManager {
3
+ private queues;
4
+ private subscriptions;
5
+ private lockedGroups;
6
+ private processingTimers;
7
+ private processQueue;
8
+ private processMessage;
9
+ private removeMessageFromQueue;
10
+ private startQueueProcessor;
11
+ enqueue<TData>(topic: string, event: Event<TData>): Promise<void>;
12
+ subscribe(topic: string, handler: Handler, queueConfig: QueueConfig, subscriptionId: string): void;
13
+ unsubscribe(topic: string, handler: Handler): void;
14
+ reset(): void;
15
+ }
16
+ export declare const queueManager: QueueManager;
17
+ export {};
@@ -0,0 +1,176 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.queueManager = void 0;
4
+ const crypto_1 = require("crypto");
5
+ const logger_1 = require("./logger");
6
+ function extractMessageGroupId(event, messageGroupId) {
7
+ if (!messageGroupId) {
8
+ return undefined;
9
+ }
10
+ if (messageGroupId === 'traceId') {
11
+ return event.traceId;
12
+ }
13
+ try {
14
+ const data = event.data;
15
+ const value = data[messageGroupId];
16
+ return value !== undefined && value !== null ? String(value) : undefined;
17
+ }
18
+ catch {
19
+ return undefined;
20
+ }
21
+ }
22
+ class QueueManager {
23
+ constructor() {
24
+ this.queues = {};
25
+ this.subscriptions = {};
26
+ this.lockedGroups = new Set();
27
+ this.processingTimers = {};
28
+ }
29
+ processQueue(topic) {
30
+ const queue = this.queues[topic];
31
+ if (!queue || queue.length === 0) {
32
+ return;
33
+ }
34
+ const now = Date.now();
35
+ const visibleMessages = queue.filter((msg) => msg.visibleAt <= now);
36
+ for (const message of visibleMessages) {
37
+ if (message.queueConfig.type === 'fifo' && message.messageGroupId) {
38
+ const lockKey = `${topic}:${message.messageGroupId}`;
39
+ if (this.lockedGroups.has(lockKey)) {
40
+ continue;
41
+ }
42
+ this.lockedGroups.add(lockKey);
43
+ this.processMessage(topic, message, lockKey);
44
+ }
45
+ else {
46
+ this.processMessage(topic, message);
47
+ }
48
+ }
49
+ }
50
+ processMessage(topic, message, lockKey) {
51
+ const handlers = this.subscriptions[topic] || [];
52
+ const handler = handlers.find((s) => s.internalSubscriptionId === message.internalSubscriptionId) ||
53
+ handlers.find((s) => s.subscriptionId === message.subscriptionId) ||
54
+ handlers[0];
55
+ if (!handler) {
56
+ this.removeMessageFromQueue(topic, message.id);
57
+ if (lockKey) {
58
+ this.lockedGroups.delete(lockKey);
59
+ }
60
+ return;
61
+ }
62
+ const visibilityTimeoutMs = handler.queueConfig.visibilityTimeout * 1000;
63
+ message.visibleAt = Date.now() + visibilityTimeoutMs;
64
+ handler.handler(message.event)
65
+ .then(() => {
66
+ this.removeMessageFromQueue(topic, message.id);
67
+ if (lockKey) {
68
+ this.lockedGroups.delete(lockKey);
69
+ }
70
+ })
71
+ .catch((error) => {
72
+ message.attempts++;
73
+ if (message.attempts >= handler.queueConfig.maxRetries) {
74
+ logger_1.globalLogger.error('[Queue DLQ] Message moved to dead-letter queue after max retries', {
75
+ topic,
76
+ messageId: message.id,
77
+ attempts: message.attempts,
78
+ error: error instanceof Error ? error.message : 'Unknown error',
79
+ });
80
+ this.removeMessageFromQueue(topic, message.id);
81
+ if (lockKey) {
82
+ this.lockedGroups.delete(lockKey);
83
+ }
84
+ }
85
+ else {
86
+ message.visibleAt = Date.now() + handler.queueConfig.visibilityTimeout * 1000;
87
+ if (lockKey) {
88
+ this.lockedGroups.delete(lockKey);
89
+ }
90
+ }
91
+ });
92
+ }
93
+ removeMessageFromQueue(topic, messageId) {
94
+ const queue = this.queues[topic];
95
+ if (!queue) {
96
+ return;
97
+ }
98
+ const index = queue.findIndex((msg) => msg.id === messageId);
99
+ if (index !== -1) {
100
+ queue.splice(index, 1);
101
+ }
102
+ if (queue.length === 0) {
103
+ delete this.queues[topic];
104
+ if (this.processingTimers[topic]) {
105
+ clearInterval(this.processingTimers[topic]);
106
+ delete this.processingTimers[topic];
107
+ }
108
+ }
109
+ }
110
+ startQueueProcessor(topic) {
111
+ if (this.processingTimers[topic]) {
112
+ return;
113
+ }
114
+ this.processingTimers[topic] = setInterval(() => {
115
+ this.processQueue(topic);
116
+ }, 100);
117
+ }
118
+ async enqueue(topic, event) {
119
+ const handlers = this.subscriptions[topic] || [];
120
+ if (handlers.length === 0) {
121
+ return;
122
+ }
123
+ for (const subscription of handlers) {
124
+ const messageGroupId = extractMessageGroupId(event, subscription.queueConfig.messageGroupId);
125
+ const delayMs = subscription.queueConfig.delaySeconds * 1000;
126
+ const visibleAt = Date.now() + delayMs;
127
+ const queuedMessage = {
128
+ id: (0, crypto_1.randomUUID)(),
129
+ event,
130
+ attempts: 0,
131
+ visibleAt,
132
+ messageGroupId,
133
+ queueConfig: subscription.queueConfig,
134
+ subscriptionId: subscription.subscriptionId,
135
+ internalSubscriptionId: subscription.internalSubscriptionId,
136
+ };
137
+ if (!this.queues[topic]) {
138
+ this.queues[topic] = [];
139
+ }
140
+ this.queues[topic].push(queuedMessage);
141
+ this.startQueueProcessor(topic);
142
+ }
143
+ setImmediate(() => this.processQueue(topic));
144
+ }
145
+ subscribe(topic, handler, queueConfig, subscriptionId) {
146
+ if (!this.subscriptions[topic]) {
147
+ this.subscriptions[topic] = [];
148
+ }
149
+ const internalSubscriptionId = (0, crypto_1.randomUUID)();
150
+ this.subscriptions[topic].push({ handler, queueConfig, subscriptionId, internalSubscriptionId });
151
+ if (this.queues[topic] && this.queues[topic].length > 0) {
152
+ this.startQueueProcessor(topic);
153
+ }
154
+ }
155
+ unsubscribe(topic, handler) {
156
+ if (!this.subscriptions[topic]) {
157
+ return;
158
+ }
159
+ this.subscriptions[topic] = this.subscriptions[topic].filter((sub) => sub.handler !== handler);
160
+ if (this.subscriptions[topic].length === 0) {
161
+ delete this.subscriptions[topic];
162
+ if (this.processingTimers[topic]) {
163
+ clearInterval(this.processingTimers[topic]);
164
+ delete this.processingTimers[topic];
165
+ }
166
+ }
167
+ }
168
+ reset() {
169
+ Object.values(this.processingTimers).forEach((timer) => clearInterval(timer));
170
+ this.queues = {};
171
+ this.subscriptions = {};
172
+ this.lockedGroups = new Set();
173
+ this.processingTimers = {};
174
+ }
175
+ }
176
+ exports.queueManager = new QueueManager();
@@ -3,6 +3,7 @@ import http from 'http';
3
3
  import { Server as WsServer } from 'ws';
4
4
  import { CronManager } from './cron-handler';
5
5
  import { LockedData } from './locked-data';
6
+ import { Motia } from './motia';
6
7
  import { Printer } from './printer';
7
8
  import { StateAdapter } from './state/state-adapter';
8
9
  import { MotiaEventManager } from './step-handlers';
@@ -16,6 +17,7 @@ export type MotiaServer = {
16
17
  addRoute: (step: Step<ApiRouteConfig>) => void;
17
18
  cronManager: CronManager;
18
19
  motiaEventManager: MotiaEventManager;
20
+ motia: Motia;
19
21
  };
20
22
  type MotiaServerConfig = {
21
23
  isVerbose: boolean;
@@ -229,6 +229,6 @@ const createServer = (lockedData, eventManager, state, config) => {
229
229
  cronManager.close();
230
230
  socketServer.close();
231
231
  };
232
- return { app, server, socketServer, close, removeRoute, addRoute, cronManager, motiaEventManager };
232
+ return { app, server, socketServer, close, removeRoute, addRoute, cronManager, motiaEventManager, motia };
233
233
  };
234
234
  exports.createServer = createServer;
@@ -3,11 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createStepHandlers = void 0;
4
4
  const call_step_file_1 = require("./call-step-file");
5
5
  const logger_1 = require("./logger");
6
+ const defaults_1 = require("./infrastructure-validator/defaults");
7
+ const validations_1 = require("./infrastructure-validator/validations");
8
+ const queue_manager_1 = require("./queue-manager");
6
9
  const createStepHandlers = (motia) => {
7
10
  const eventSteps = motia.lockedData.eventSteps();
11
+ const handlerMap = new Map();
8
12
  logger_1.globalLogger.debug(`[step handler] creating step handlers for ${eventSteps.length} steps`);
9
13
  const removeLogger = (event) => {
10
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
11
14
  const { logger, tracer, ...rest } = event;
12
15
  return rest;
13
16
  };
@@ -15,34 +18,50 @@ const createStepHandlers = (motia) => {
15
18
  const { config, filePath } = step;
16
19
  const { subscribes, name } = config;
17
20
  logger_1.globalLogger.debug('[step handler] establishing step subscriptions', { filePath, step: step.config.name });
18
- subscribes.forEach((subscribe) => {
19
- motia.eventManager.subscribe({
20
- filePath,
21
- event: subscribe,
22
- handlerName: step.config.name,
23
- handler: async (event) => {
24
- const { data, traceId } = event;
25
- const logger = event.logger.child({ step: step.config.name });
26
- const tracer = event.tracer.child(step, logger);
27
- logger_1.globalLogger.debug('[step handler] received event', { event: removeLogger(event), step: name });
28
- try {
29
- await (0, call_step_file_1.callStepFile)({ step, data, traceId, tracer, logger }, motia);
30
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
31
- }
32
- catch (error) {
33
- const message = typeof error === 'string' ? error : error.message;
34
- logger.error(message);
35
- }
36
- },
21
+ if (config.infrastructure) {
22
+ logger_1.globalLogger.debug('[step handler] validating infrastructure config', {
23
+ step: name,
24
+ hasInputSchema: !!config.input,
25
+ infrastructure: config.infrastructure,
26
+ });
27
+ const validationResult = (0, validations_1.validateInfrastructureConfig)(config.infrastructure, config.input);
28
+ if (!validationResult.success && validationResult.errors) {
29
+ logger_1.globalLogger.error('[step handler] Infrastructure configuration validation failed', {
30
+ step: name,
31
+ filePath,
32
+ errors: validationResult.errors,
33
+ });
34
+ return;
35
+ }
36
+ logger_1.globalLogger.debug('[step handler] infrastructure config validated successfully', {
37
+ step: name,
37
38
  });
39
+ }
40
+ const queueConfig = (0, defaults_1.getQueueConfigWithDefaults)(config.infrastructure);
41
+ const handlers = [];
42
+ subscribes.forEach((subscribe) => {
43
+ const handler = async (event) => {
44
+ const { data, traceId } = event;
45
+ const logger = event.logger.child({ step: step.config.name });
46
+ const tracer = event.tracer.child(step, logger);
47
+ logger_1.globalLogger.debug('[step handler] received event', { event: removeLogger(event), step: name });
48
+ await (0, call_step_file_1.callStepFile)({ step, data, traceId, tracer, logger, infrastructure: config.infrastructure }, motia);
49
+ };
50
+ queue_manager_1.queueManager.subscribe(subscribe, handler, queueConfig, subscribe);
51
+ handlers.push({ topic: subscribe, handler });
38
52
  });
53
+ handlerMap.set(filePath, handlers);
39
54
  };
40
55
  const removeHandler = (step) => {
41
- const { config, filePath } = step;
42
- const { subscribes } = config;
43
- subscribes.forEach((subscribe) => {
44
- motia.eventManager.unsubscribe({ filePath, event: subscribe });
45
- });
56
+ const { filePath } = step;
57
+ const handlers = handlerMap.get(filePath);
58
+ if (handlers) {
59
+ handlers.forEach(({ topic, handler }) => {
60
+ queue_manager_1.queueManager.unsubscribe(topic, handler);
61
+ logger_1.globalLogger.debug('[step handler] unsubscribed handler', { filePath, topic, step: step.config.name });
62
+ });
63
+ handlerMap.delete(filePath);
64
+ }
46
65
  };
47
66
  eventSteps.forEach(createHandler);
48
67
  return { removeHandler, createHandler };
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.validateStep = void 0;
4
4
  const zod_1 = require("zod");
5
+ const schemas_1 = require("./infrastructure-validator/schemas");
5
6
  const objectSchema = zod_1.z.object({
6
7
  type: zod_1.z.literal('object'),
7
8
  properties: zod_1.z.record(zod_1.z.any()),
@@ -60,6 +61,7 @@ const eventSchema = zod_1.z
60
61
  input: zod_1.z.union([jsonSchema, zod_1.z.object({}), zod_1.z.null()]).optional(),
61
62
  flows: zod_1.z.array(zod_1.z.string()).optional(),
62
63
  includeFiles: zod_1.z.array(zod_1.z.string()).optional(),
64
+ infrastructure: schemas_1.infrastructureSchema.optional(),
63
65
  })
64
66
  .strict();
65
67
  const apiSchema = zod_1.z
@@ -0,0 +1,22 @@
1
+ import { Motia } from '../motia';
2
+ export type Runtime = {
3
+ steps: string;
4
+ streams: string;
5
+ runtime: any;
6
+ };
7
+ export type WorkbenchPlugin = {
8
+ packageName: string;
9
+ componentName?: string;
10
+ label?: string;
11
+ labelIcon?: string;
12
+ position?: 'bottom' | 'top';
13
+ props?: Record<string, any>;
14
+ };
15
+ export type MotiaPlugin = {
16
+ workbench: WorkbenchPlugin[];
17
+ };
18
+ export type MotiaPluginBuilder = (motia: Motia) => MotiaPlugin;
19
+ export type Config = {
20
+ runtimes?: Runtime[];
21
+ plugins?: MotiaPluginBuilder[];
22
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -1,6 +1,7 @@
1
1
  import { z, ZodAny, ZodArray, ZodObject } from 'zod';
2
2
  import { Logger } from './logger';
3
3
  import { Tracer } from './observability';
4
+ export * from './types/app-config-types';
4
5
  export type ZodInput = ZodObject<any> | ZodArray<any>;
5
6
  export type InternalStateManager = {
6
7
  get<T>(groupId: string, key: string): Promise<T | null>;
@@ -29,6 +30,22 @@ export type Emit = string | {
29
30
  label?: string;
30
31
  conditional?: boolean;
31
32
  };
33
+ export type HandlerConfig = {
34
+ ram: number;
35
+ cpu?: number;
36
+ timeout: number;
37
+ };
38
+ export type QueueConfig = {
39
+ type: 'fifo' | 'standard';
40
+ messageGroupId?: string | null;
41
+ maxRetries: number;
42
+ visibilityTimeout: number;
43
+ delaySeconds: number;
44
+ };
45
+ export type InfrastructureConfig = {
46
+ handler?: Partial<HandlerConfig>;
47
+ queue?: Partial<QueueConfig>;
48
+ };
32
49
  export type EventConfig = {
33
50
  type: 'event';
34
51
  name: string;
@@ -44,6 +61,7 @@ export type EventConfig = {
44
61
  * Needs to be relative to the step file.
45
62
  */
46
63
  includeFiles?: string[];
64
+ infrastructure?: Partial<InfrastructureConfig>;
47
65
  };
48
66
  export type NoopConfig = {
49
67
  type: 'noop';
package/dist/src/types.js CHANGED
@@ -1,2 +1,17 @@
1
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 __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
2
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./types/app-config-types"), exports);
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@motiadev/core",
3
3
  "description": "Core functionality for the Motia framework, providing the foundation for building event-driven workflows.",
4
4
  "main": "dist/index.js",
5
- "version": "0.8.2-beta.140-246724",
5
+ "version": "0.8.2-beta.140-131607",
6
6
  "dependencies": {
7
7
  "@amplitude/analytics-node": "^1.3.8",
8
8
  "body-parser": "^1.20.3",