@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
@@ -1,19 +1,17 @@
1
+ import { CronManager } from './cron-handler';
1
2
  import { Express } from 'express';
2
3
  import http from 'http';
3
4
  import { Server as SocketIOServer } from 'socket.io';
4
5
  import { StateAdapter } from './state/state-adapter';
5
- import { EventManager, LockedData, Step } from './types';
6
- type ServerOptions = {
7
- steps: Step[];
8
- flows: LockedData['flows'];
9
- eventManager: EventManager;
10
- state: StateAdapter;
11
- };
12
- type ServerOutput = {
6
+ import { ApiRouteConfig, EventManager, Step } from './types';
7
+ import { LockedData } from './locked-data';
8
+ export type MotiaServer = {
13
9
  app: Express;
14
10
  server: http.Server;
15
11
  socketServer: SocketIOServer;
16
12
  close: () => Promise<void>;
13
+ removeRoute: (step: Step<ApiRouteConfig>) => void;
14
+ addRoute: (step: Step<ApiRouteConfig>) => void;
15
+ cronManager: CronManager;
17
16
  };
18
- export declare const createServer: (options: ServerOptions) => Promise<ServerOutput>;
19
- export {};
17
+ export declare const createServer: (lockedData: LockedData, eventManager: EventManager, state: StateAdapter) => Promise<MotiaServer>;
@@ -15,13 +15,13 @@ const guards_1 = require("./guards");
15
15
  const logger_1 = require("./logger");
16
16
  const get_module_export_1 = require("./node/get-module-export");
17
17
  const steps_1 = require("./steps");
18
- const createServer = async (options) => {
19
- const { flows, steps, eventManager, state } = options;
18
+ const utils_1 = require("./utils");
19
+ const createServer = async (lockedData, eventManager, state) => {
20
20
  const app = (0, express_1.default)();
21
21
  const server = http_1.default.createServer(app);
22
22
  const io = new socket_io_1.Server(server);
23
- const allSteps = [...steps_1.systemSteps, ...steps];
24
- const cleanupCronJobs = (0, cron_handler_1.setupCronHandlers)(allSteps, eventManager, io);
23
+ const allSteps = [...steps_1.systemSteps, ...lockedData.activeSteps];
24
+ const cronManager = (0, cron_handler_1.setupCronHandlers)(lockedData, eventManager, io);
25
25
  const asyncHandler = (step, flows) => {
26
26
  return async (req, res) => {
27
27
  const traceId = (0, crypto_1.randomUUID)();
@@ -35,6 +35,10 @@ const createServer = async (options) => {
35
35
  queryParams: req.query,
36
36
  };
37
37
  const emit = async ({ data, type }) => {
38
+ if (!(0, utils_1.isAllowedToEmit)(step, type)) {
39
+ lockedData.printer.printInvalidEmit(step, type);
40
+ return;
41
+ }
38
42
  await eventManager.emit({ data, type, traceId, flows, logger }, step.filePath);
39
43
  };
40
44
  try {
@@ -54,30 +58,44 @@ const createServer = async (options) => {
54
58
  };
55
59
  app.use(body_parser_1.default.json());
56
60
  app.use(body_parser_1.default.urlencoded({ extended: true }));
57
- const apiSteps = allSteps.filter(guards_1.isApiStep);
58
- for (const step of apiSteps) {
61
+ const router = express_1.default.Router();
62
+ const addRoute = (step) => {
59
63
  const { method, flows, path } = step.config;
60
64
  logger_1.globalLogger.debug('[API] Registering route', step.config);
61
65
  if (method === 'POST') {
62
- app.post(path, asyncHandler(step, flows));
66
+ router.post(path, asyncHandler(step, flows));
63
67
  }
64
68
  else if (method === 'GET') {
65
- app.get(path, asyncHandler(step, flows));
69
+ router.get(path, asyncHandler(step, flows));
66
70
  }
67
71
  else {
68
72
  throw new Error(`Unsupported method: ${method}`);
69
73
  }
70
- }
71
- (0, flows_endpoint_1.flowsEndpoint)(flows, app);
74
+ };
75
+ const removeRoute = (step) => {
76
+ const { path, method } = step.config;
77
+ const routerStack = router.stack;
78
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
79
+ const filteredStack = routerStack.filter((layer) => {
80
+ if (layer.route) {
81
+ const match = layer.route.path === path && layer.route.methods[method.toLowerCase()];
82
+ return !match;
83
+ }
84
+ return true;
85
+ });
86
+ router.stack = filteredStack;
87
+ };
88
+ allSteps.filter(guards_1.isApiStep).forEach(addRoute);
89
+ app.use(router);
90
+ (0, flows_endpoint_1.flowsEndpoint)(lockedData, app);
72
91
  server.on('error', (error) => {
73
92
  console.error('Server error:', error);
74
- cleanupCronJobs();
75
93
  });
76
94
  const close = async () => {
77
- cleanupCronJobs();
95
+ cronManager.close();
78
96
  await io.close();
79
97
  server.close();
80
98
  };
81
- return { app, server, socketServer: io, close };
99
+ return { app, server, socketServer: io, close, removeRoute, addRoute, cronManager };
82
100
  };
83
101
  exports.createServer = createServer;
@@ -9,6 +9,7 @@ export type RpcMessage = {
9
9
  export declare class RpcProcessor {
10
10
  private child;
11
11
  private handlers;
12
+ private isClosed;
12
13
  constructor(child: ChildProcess);
13
14
  handler<TInput, TOutput = unknown>(method: string, handler: RpcHandler<TInput, TOutput>): void;
14
15
  handle(method: string, input: unknown): Promise<any>;
@@ -6,6 +6,7 @@ class RpcProcessor {
6
6
  this.child = child;
7
7
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
8
8
  this.handlers = {};
9
+ this.isClosed = false;
9
10
  }
10
11
  handler(method, handler) {
11
12
  this.handlers[method] = handler;
@@ -18,7 +19,7 @@ class RpcProcessor {
18
19
  return handler(input);
19
20
  }
20
21
  response(id, result, error) {
21
- if (id) {
22
+ if (id && !this.isClosed && this.child.connected && !this.child.killed) {
22
23
  this.child.send?.({ type: 'rpc_response', id, result, error });
23
24
  }
24
25
  }
@@ -31,6 +32,15 @@ class RpcProcessor {
31
32
  .catch((error) => this.response(id, null, error));
32
33
  }
33
34
  });
35
+ this.child.on('exit', () => {
36
+ this.isClosed = true;
37
+ });
38
+ this.child.on('close', () => {
39
+ this.isClosed = true;
40
+ });
41
+ this.child.on('disconnect', () => {
42
+ this.isClosed = true;
43
+ });
34
44
  }
35
45
  }
36
46
  exports.RpcProcessor = RpcProcessor;
@@ -1,2 +1,8 @@
1
- import { EventManager, InternalStateManager, Step } from './types';
2
- export declare const createStepHandlers: (steps: Step[], eventManager: EventManager, state: InternalStateManager) => void;
1
+ import { EventConfig, EventManager, Step } from './types';
2
+ import { LockedData } from './locked-data';
3
+ import { StateAdapter } from './state/state-adapter';
4
+ export type MotiaEventManager = {
5
+ createHandler: (step: Step<EventConfig>) => void;
6
+ removeHandler: (step: Step<EventConfig>) => void;
7
+ };
8
+ export declare const createStepHandlers: (lockedData: LockedData, eventManager: EventManager, state: StateAdapter) => MotiaEventManager;
@@ -2,32 +2,46 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createStepHandlers = void 0;
4
4
  const logger_1 = require("./logger");
5
- const guards_1 = require("./guards");
6
5
  const call_step_file_1 = require("./call-step-file");
7
- const createStepHandlers = (steps, eventManager, state) => {
8
- logger_1.globalLogger.debug(`[step handler] creating step handlers for ${steps.length} steps`);
9
- steps.filter(guards_1.isEventStep).forEach((step) => {
6
+ const createStepHandlers = (lockedData, eventManager, state) => {
7
+ const eventSteps = lockedData.eventSteps();
8
+ logger_1.globalLogger.debug(`[step handler] creating step handlers for ${eventSteps.length} steps`);
9
+ const createHandler = (step) => {
10
10
  const { config, filePath } = step;
11
11
  const { subscribes } = config;
12
12
  logger_1.globalLogger.debug('[step handler] establishing step subscriptions', { filePath, step: step.config.name });
13
13
  subscribes.forEach((subscribe) => {
14
- eventManager.subscribe(subscribe, step.config.name, async (event) => {
15
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
16
- const { logger, ...rest } = event;
17
- logger_1.globalLogger.debug('[step handler] received event', { event: rest, step: step.config.name });
18
- try {
19
- await (0, call_step_file_1.callStepFile)(filePath, step.config.name, event, eventManager, state);
20
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
21
- }
22
- catch (error) {
23
- logger_1.globalLogger.error(`[step handler] error calling step`, {
24
- error: error.message,
25
- filePath,
26
- step: step.config.name,
27
- });
28
- }
14
+ eventManager.subscribe({
15
+ filePath,
16
+ event: subscribe,
17
+ handlerName: step.config.name,
18
+ handler: async (event) => {
19
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
20
+ const { logger, ...rest } = event;
21
+ logger_1.globalLogger.debug('[step handler] received event', { event: rest, step: step.config.name });
22
+ try {
23
+ await (0, call_step_file_1.callStepFile)(step, lockedData, event, eventManager, state);
24
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
25
+ }
26
+ catch (error) {
27
+ logger_1.globalLogger.error(`[step handler] error calling step`, {
28
+ error: error.message,
29
+ filePath,
30
+ step: step.config.name,
31
+ });
32
+ }
33
+ },
29
34
  });
30
35
  });
31
- });
36
+ };
37
+ const removeHandler = (step) => {
38
+ const { config, filePath } = step;
39
+ const { subscribes } = config;
40
+ subscribes.forEach((subscribe) => {
41
+ eventManager.unsubscribe({ filePath, event: subscribe });
42
+ });
43
+ };
44
+ eventSteps.forEach(createHandler);
45
+ return { removeHandler, createHandler };
32
46
  };
33
47
  exports.createStepHandlers = createStepHandlers;
@@ -0,0 +1,14 @@
1
+ import { Step } from './types';
2
+ export type ValidationSuccess = {
3
+ success: true;
4
+ };
5
+ export type ValidationError = {
6
+ success: false;
7
+ error: string;
8
+ errors?: Array<{
9
+ path: string;
10
+ message: string;
11
+ }>;
12
+ };
13
+ export type ValidationResult = ValidationSuccess | ValidationError;
14
+ export declare const validateStep: (step: Step) => ValidationResult;
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateStep = void 0;
4
+ const zod_1 = require("zod");
5
+ const emits = zod_1.z.array(zod_1.z.union([
6
+ zod_1.z.string(),
7
+ zod_1.z
8
+ .object({
9
+ type: zod_1.z.string(),
10
+ label: zod_1.z.string().optional(),
11
+ conditional: zod_1.z.boolean().optional(),
12
+ })
13
+ .strict(),
14
+ ]));
15
+ const noopSchema = zod_1.z
16
+ .object({
17
+ type: zod_1.z.literal('noop'),
18
+ name: zod_1.z.string(),
19
+ description: zod_1.z.string().optional(),
20
+ virtualEmits: emits,
21
+ virtualSubscribes: zod_1.z.array(zod_1.z.string()),
22
+ flows: zod_1.z.array(zod_1.z.string()).optional(),
23
+ })
24
+ .strict();
25
+ const eventSchema = zod_1.z
26
+ .object({
27
+ type: zod_1.z.literal('event'),
28
+ name: zod_1.z.string(),
29
+ description: zod_1.z.string().optional(),
30
+ subscribes: zod_1.z.array(zod_1.z.string()),
31
+ emits: emits,
32
+ virtualEmits: emits.optional(),
33
+ input: zod_1.z.any(),
34
+ flows: zod_1.z.array(zod_1.z.string()).optional(),
35
+ })
36
+ .strict();
37
+ const apiSchema = zod_1.z
38
+ .object({
39
+ type: zod_1.z.literal('api'),
40
+ name: zod_1.z.string(),
41
+ description: zod_1.z.string().optional(),
42
+ path: zod_1.z.string(),
43
+ method: zod_1.z.string(),
44
+ emits: emits,
45
+ virtualEmits: emits.optional(),
46
+ virtualSubscribes: zod_1.z.array(zod_1.z.string()).optional(),
47
+ flows: zod_1.z.array(zod_1.z.string()).optional(),
48
+ bodySchema: zod_1.z.instanceof(zod_1.z.ZodObject).optional(),
49
+ })
50
+ .strict();
51
+ const cronSchema = zod_1.z
52
+ .object({
53
+ type: zod_1.z.literal('cron'),
54
+ name: zod_1.z.string(),
55
+ description: zod_1.z.string().optional(),
56
+ cron: zod_1.z.string(),
57
+ virtualEmits: emits.optional(),
58
+ emits: emits,
59
+ flows: zod_1.z.array(zod_1.z.string()).optional(),
60
+ })
61
+ .strict();
62
+ const validateStep = (step) => {
63
+ try {
64
+ if (step.config.type === 'noop') {
65
+ noopSchema.parse(step.config);
66
+ }
67
+ else if (step.config.type === 'event') {
68
+ eventSchema.parse(step.config);
69
+ }
70
+ else if (step.config.type === 'api') {
71
+ apiSchema.parse(step.config);
72
+ }
73
+ else if (step.config.type === 'cron') {
74
+ cronSchema.parse(step.config);
75
+ }
76
+ else {
77
+ return {
78
+ success: false,
79
+ error: 'Invalid step type',
80
+ };
81
+ }
82
+ return { success: true };
83
+ }
84
+ catch (error) {
85
+ if (error instanceof zod_1.z.ZodError) {
86
+ return {
87
+ success: false,
88
+ error: 'Invalid step configuration',
89
+ errors: error.errors.map((err) => ({ path: err.path.join('.'), message: err.message })),
90
+ };
91
+ }
92
+ // Handle unexpected errors
93
+ return {
94
+ success: false,
95
+ error: 'Unexpected validation error occurred',
96
+ };
97
+ }
98
+ };
99
+ exports.validateStep = validateStep;
@@ -1,6 +1,4 @@
1
1
  import { z, ZodObject } from 'zod';
2
- import { Server } from 'http';
3
- import { Server as SocketIOServer } from 'socket.io';
4
2
  import { Logger } from './logger';
5
3
  export type InternalStateManager = {
6
4
  get<T>(traceId: string, key: string): Promise<T | null>;
@@ -25,7 +23,7 @@ export type Emit = string | {
25
23
  label?: string;
26
24
  conditional?: boolean;
27
25
  };
28
- export type EventConfig<TInput extends ZodObject<any>> = {
26
+ export type EventConfig<TInput extends ZodObject<any> = any> = {
29
27
  type: 'event';
30
28
  name: string;
31
29
  description?: string;
@@ -33,7 +31,7 @@ export type EventConfig<TInput extends ZodObject<any>> = {
33
31
  emits: Emit[];
34
32
  virtualEmits?: Emit[];
35
33
  input: TInput;
36
- flows: string[];
34
+ flows?: string[];
37
35
  };
38
36
  export type NoopConfig = {
39
37
  type: 'noop';
@@ -41,7 +39,7 @@ export type NoopConfig = {
41
39
  description?: string;
42
40
  virtualEmits: Emit[];
43
41
  virtualSubscribes: string[];
44
- flows: string[];
42
+ flows?: string[];
45
43
  };
46
44
  export type ApiRouteMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'OPTIONS' | 'HEAD';
47
45
  export type ApiRouteConfig = {
@@ -50,10 +48,10 @@ export type ApiRouteConfig = {
50
48
  description?: string;
51
49
  path: string;
52
50
  method: ApiRouteMethod;
53
- emits: string[];
51
+ emits: Emit[];
54
52
  virtualEmits?: Emit[];
55
53
  virtualSubscribes?: string[];
56
- flows: string[];
54
+ flows?: string[];
57
55
  bodySchema?: ZodObject<any>;
58
56
  };
59
57
  export type ApiRequest = {
@@ -74,23 +72,32 @@ export type CronConfig = {
74
72
  description?: string;
75
73
  cron: string;
76
74
  virtualEmits?: Emit[];
77
- emits: string[];
78
- flows: string[];
75
+ emits: Emit[];
76
+ flows?: string[];
79
77
  };
80
78
  export type StepHandler<T> = T extends EventConfig<any> ? EventHandler<T['input']> : T extends ApiRouteConfig ? ApiRouteHandler : T extends CronConfig ? never : never;
81
- export type MotiaServer = Server;
82
- export type MotiaSocketServer = SocketIOServer;
83
79
  export type Event<TData = unknown> = {
84
80
  type: string;
85
81
  data: TData;
86
82
  traceId: string;
87
- flows: string[];
83
+ flows?: string[];
88
84
  logger: Logger;
89
85
  };
90
86
  export type Handler<TData = unknown> = (event: Event<TData>) => Promise<void>;
87
+ export type SubscribeConfig<TData> = {
88
+ event: string;
89
+ handlerName: string;
90
+ filePath: string;
91
+ handler: Handler<TData>;
92
+ };
93
+ export type UnsubscribeConfig = {
94
+ filePath: string;
95
+ event: string;
96
+ };
91
97
  export type EventManager = {
92
98
  emit: <TData>(event: Event<TData>, file?: string) => Promise<void>;
93
- subscribe: <TData>(event: string, handlerName: string, handler: Handler<TData>) => void;
99
+ subscribe: <TData>(config: SubscribeConfig<TData>) => void;
100
+ unsubscribe: (config: UnsubscribeConfig) => void;
94
101
  };
95
102
  export type StepConfig = EventConfig<ZodObject<any>> | NoopConfig | ApiRouteConfig | CronConfig;
96
103
  export type Step<TConfig extends StepConfig = StepConfig> = {
@@ -103,16 +110,3 @@ export type Flow = {
103
110
  description?: string;
104
111
  steps: Step[];
105
112
  };
106
- export type LockedData = {
107
- baseDir: string;
108
- steps: {
109
- active: Step[];
110
- dev: Step[];
111
- };
112
- flows: Record<string, Flow>;
113
- state: {
114
- adapter: string;
115
- host: string;
116
- port: number;
117
- };
118
- };
@@ -0,0 +1,2 @@
1
+ import { Step } from './types';
2
+ export declare const isAllowedToEmit: (step: Step, emit: string) => boolean;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isAllowedToEmit = void 0;
4
+ const guards_1 = require("./guards");
5
+ const toType = (emit) => (typeof emit === 'string' ? emit : emit.type);
6
+ const isAllowedToEmit = (step, emit) => {
7
+ if ((0, guards_1.isApiStep)(step)) {
8
+ return step.config.emits.map(toType).includes(emit);
9
+ }
10
+ if ((0, guards_1.isEventStep)(step)) {
11
+ return step.config.emits.map(toType).includes(emit);
12
+ }
13
+ return false;
14
+ };
15
+ exports.isAllowedToEmit = isAllowedToEmit;
package/package.json CHANGED
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "@motiadev/core",
3
3
  "main": "dist/index.js",
4
- "version": "0.0.19",
4
+ "version": "0.0.21",
5
5
  "dependencies": {
6
6
  "body-parser": "^1.20.3",
7
+ "colors": "^1.4.0",
7
8
  "express": "^4.21.2",
8
9
  "ioredis": "^5.4.2",
9
10
  "node-cron": "^3.0.3",
@@ -1,62 +0,0 @@
1
- require 'net/http'
2
- require 'json'
3
- require 'uri'
4
- require 'logger'
5
-
6
- class StateManagerError < StandardError; end
7
-
8
- LOGGER = Logger.new($stdout)
9
-
10
- def get_state_manager_handler(state_manager_url)
11
- lambda do |action, payload|
12
- begin
13
- uri = URI("#{state_manager_url}/#{action}")
14
- request = Net::HTTP::Post.new(uri)
15
- request['Content-Type'] = 'application/json'
16
- request['x-trace-id'] = payload[:traceId]
17
- # TODO: Add internal auth token for security
18
- # TODO: Encrypt the payload for security
19
- request.body = payload.to_json
20
-
21
- response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == 'https') do |http|
22
- http.request(request)
23
- end
24
-
25
- unless response.is_a?(Net::HTTPSuccess)
26
- error_message = "Failed posting state change: #{response.code} - #{response.message}"
27
- LOGGER.error(error_message)
28
- raise StateManagerError, error_message
29
- end
30
-
31
- if action == 'get'
32
- result = JSON.parse(response.body, object_class: OpenStruct)
33
- return result.data
34
- end
35
-
36
- nil
37
- rescue StandardError => error
38
- LOGGER.error("[internal state manager] failed posting state change: #{error.message}")
39
- raise StateManagerError, "Failed posting state change: #{error.message}"
40
- end
41
- end
42
- end
43
-
44
- def create_internal_state_manager(state_manager_url:)
45
- handler = get_state_manager_handler(state_manager_url)
46
-
47
- {
48
- get: ->(trace_id, key) {
49
- result = handler.call('get', { traceId: trace_id, key: key })
50
- { data: result }
51
- },
52
- set: ->(trace_id, key, value) {
53
- handler.call('set', { traceId: trace_id, key: key, value: value })
54
- },
55
- delete: ->(trace_id, key) {
56
- handler.call('delete', { traceId: trace_id, key: key })
57
- },
58
- clear: ->(trace_id) {
59
- handler.call('clear', { traceId: trace_id })
60
- }
61
- }
62
- end