@motiadev/core 0.0.1
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 +8 -0
- package/dist/index.js +24 -0
- package/dist/jest.config.d.ts +6 -0
- package/dist/src/config.types.d.ts +16 -0
- package/dist/src/config.types.js +2 -0
- package/dist/src/event-manager.d.ts +2 -0
- package/dist/src/event-manager.js +22 -0
- package/dist/src/flows-endpoint.d.ts +25 -0
- package/dist/src/flows-endpoint.js +81 -0
- package/dist/src/get-step-language.d.ts +1 -0
- package/dist/src/get-step-language.js +27 -0
- package/dist/src/guards.d.ts +4 -0
- package/dist/src/guards.js +9 -0
- package/dist/src/logger.d.ts +23 -0
- package/dist/src/logger.js +76 -0
- package/dist/src/node/logger.d.ts +8 -0
- package/dist/src/node/logger.js +23 -0
- package/dist/src/node/node-runner.d.ts +1 -0
- package/dist/src/node/node-runner.js +56 -0
- package/dist/src/node/rpc-state-manager.d.ts +10 -0
- package/dist/src/node/rpc-state-manager.js +21 -0
- package/dist/src/node/rpc.d.ts +7 -0
- package/dist/src/node/rpc.js +32 -0
- package/dist/src/python/get-config.py +42 -0
- package/dist/src/python/get-python-config.d.ts +2 -0
- package/dist/src/python/get-python-config.js +34 -0
- package/dist/src/python/logger.py +42 -0
- package/dist/src/python/python-runner.py +73 -0
- package/dist/src/python/rpc.py +80 -0
- package/dist/src/python/rpc_state_manager.py +18 -0
- package/dist/src/ruby/get-ruby-config.d.ts +2 -0
- package/dist/src/ruby/get-ruby-config.js +34 -0
- package/dist/src/ruby/get_config.rb +78 -0
- package/dist/src/ruby/logger.rb +55 -0
- package/dist/src/ruby/ruby_runner.rb +80 -0
- package/dist/src/ruby/state_adapter.rb +62 -0
- package/dist/src/server.d.ts +18 -0
- package/dist/src/server.js +73 -0
- package/dist/src/state/adapters/default-state-adapter.d.ts +17 -0
- package/dist/src/state/adapters/default-state-adapter.js +102 -0
- package/dist/src/state/adapters/redis-state-adapter.d.ts +19 -0
- package/dist/src/state/adapters/redis-state-adapter.js +44 -0
- package/dist/src/state/create-state-adapter.d.ts +8 -0
- package/dist/src/state/create-state-adapter.js +16 -0
- package/dist/src/state/state-adapter.d.ts +10 -0
- package/dist/src/state/state-adapter.js +2 -0
- package/dist/src/step-handler-rpc-processor.d.ts +12 -0
- package/dist/src/step-handler-rpc-processor.js +35 -0
- package/dist/src/step-handlers.d.ts +2 -0
- package/dist/src/step-handlers.js +98 -0
- package/dist/src/types.d.ts +109 -0
- package/dist/src/types.js +2 -0
- package/package.json +31 -0
- package/tsconfig.json +20 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
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';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
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
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__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);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { StepConfig } from './types';
|
|
2
|
+
export type StateConfig = {
|
|
3
|
+
adapter: string;
|
|
4
|
+
host: string;
|
|
5
|
+
port: number;
|
|
6
|
+
password?: string;
|
|
7
|
+
};
|
|
8
|
+
export type Config = {
|
|
9
|
+
port: number;
|
|
10
|
+
state: StateConfig;
|
|
11
|
+
};
|
|
12
|
+
export type Step<TConfig extends StepConfig = StepConfig> = {
|
|
13
|
+
config: TConfig;
|
|
14
|
+
file: string;
|
|
15
|
+
filePath: string;
|
|
16
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createEventManager = void 0;
|
|
4
|
+
const logger_1 = require("./logger");
|
|
5
|
+
const createEventManager = () => {
|
|
6
|
+
const handlers = {};
|
|
7
|
+
const emit = async (event, file) => {
|
|
8
|
+
const eventHandlers = handlers[event.type] ?? [];
|
|
9
|
+
const { logger, ...rest } = event;
|
|
10
|
+
logger.debug('[Flow Emit] Event emitted', { handlers: eventHandlers.length, data: rest, file });
|
|
11
|
+
eventHandlers.map((handler) => handler(event));
|
|
12
|
+
};
|
|
13
|
+
const subscribe = (event, handlerName, handler) => {
|
|
14
|
+
if (!handlers[event]) {
|
|
15
|
+
handlers[event] = [];
|
|
16
|
+
}
|
|
17
|
+
logger_1.globalLogger.debug('[Flow Sub] Subscribing to event', { event, handlerName });
|
|
18
|
+
handlers[event].push(handler);
|
|
19
|
+
};
|
|
20
|
+
return { emit, subscribe };
|
|
21
|
+
};
|
|
22
|
+
exports.createEventManager = createEventManager;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Express } from 'express';
|
|
2
|
+
import { Emit, LockedData } from './types';
|
|
3
|
+
type FlowListResponse = {
|
|
4
|
+
id: string;
|
|
5
|
+
name: string;
|
|
6
|
+
};
|
|
7
|
+
type FlowStepResponse = {
|
|
8
|
+
id: string;
|
|
9
|
+
name: string;
|
|
10
|
+
type: 'event' | 'api' | 'noop';
|
|
11
|
+
description?: string;
|
|
12
|
+
subscribes?: string[];
|
|
13
|
+
emits: Emit[];
|
|
14
|
+
action?: 'webhook';
|
|
15
|
+
webhookUrl?: string;
|
|
16
|
+
bodySchema?: any;
|
|
17
|
+
language?: string;
|
|
18
|
+
nodeComponentPath?: string;
|
|
19
|
+
};
|
|
20
|
+
type FlowResponse = FlowListResponse & {
|
|
21
|
+
steps: FlowStepResponse[];
|
|
22
|
+
};
|
|
23
|
+
export declare const generateFlowsList: (flows: LockedData["flows"]) => FlowResponse[];
|
|
24
|
+
export declare const flowsEndpoint: (flows: LockedData["flows"], app: Express) => void;
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,81 @@
|
|
|
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.flowsEndpoint = exports.generateFlowsList = void 0;
|
|
7
|
+
const crypto_1 = require("crypto");
|
|
8
|
+
const fs_1 = __importDefault(require("fs"));
|
|
9
|
+
const zod_to_json_schema_1 = __importDefault(require("zod-to-json-schema"));
|
|
10
|
+
const guards_1 = require("./guards");
|
|
11
|
+
const get_step_language_1 = require("./get-step-language");
|
|
12
|
+
const generateFlowsList = (flows) => {
|
|
13
|
+
const list = [];
|
|
14
|
+
Object.keys(flows).forEach((flowId) => {
|
|
15
|
+
const steps = [];
|
|
16
|
+
const flowSteps = flows[flowId].steps;
|
|
17
|
+
flowSteps.forEach((step) => {
|
|
18
|
+
const filePathWithoutExtension = step.filePath.replace(/\.[^/.]+$/, '');
|
|
19
|
+
const tsxPath = filePathWithoutExtension + '.tsx';
|
|
20
|
+
const nodeComponentPath = fs_1.default.existsSync(tsxPath) ? tsxPath : undefined;
|
|
21
|
+
if ((0, guards_1.isApiStep)(step)) {
|
|
22
|
+
steps.push({
|
|
23
|
+
id: (0, crypto_1.randomUUID)(),
|
|
24
|
+
type: 'api',
|
|
25
|
+
name: step.config.name,
|
|
26
|
+
description: step.config.description,
|
|
27
|
+
emits: [...step.config.emits, ...(step.config.virtualEmits ?? [])],
|
|
28
|
+
subscribes: step.config.virtualSubscribes ?? undefined,
|
|
29
|
+
action: 'webhook',
|
|
30
|
+
language: (0, get_step_language_1.getStepLanguage)(step.filePath),
|
|
31
|
+
webhookUrl: `${step.config.method} ${step.config.path}`,
|
|
32
|
+
bodySchema: step?.config.bodySchema ? (0, zod_to_json_schema_1.default)(step.config.bodySchema) : undefined,
|
|
33
|
+
nodeComponentPath,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
else if ((0, guards_1.isEventStep)(step)) {
|
|
37
|
+
steps.push({
|
|
38
|
+
id: (0, crypto_1.randomUUID)(),
|
|
39
|
+
type: 'event',
|
|
40
|
+
name: step.config.name,
|
|
41
|
+
description: step.config.description,
|
|
42
|
+
emits: [...step.config.emits, ...(step.config.virtualEmits ?? [])],
|
|
43
|
+
subscribes: step.config.subscribes,
|
|
44
|
+
language: (0, get_step_language_1.getStepLanguage)(step.filePath),
|
|
45
|
+
nodeComponentPath,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
else if ((0, guards_1.isNoopStep)(step)) {
|
|
49
|
+
steps.push({
|
|
50
|
+
id: (0, crypto_1.randomUUID)(),
|
|
51
|
+
type: 'noop',
|
|
52
|
+
name: step.config.name,
|
|
53
|
+
description: step.config.description,
|
|
54
|
+
emits: step.config.virtualEmits,
|
|
55
|
+
subscribes: step.config.virtualSubscribes,
|
|
56
|
+
nodeComponentPath,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
list.push({ id: flowId, name: flowId, steps });
|
|
61
|
+
});
|
|
62
|
+
return list;
|
|
63
|
+
};
|
|
64
|
+
exports.generateFlowsList = generateFlowsList;
|
|
65
|
+
const flowsEndpoint = (flows, app) => {
|
|
66
|
+
const list = (0, exports.generateFlowsList)(flows);
|
|
67
|
+
app.get('/flows', async (_, res) => {
|
|
68
|
+
res.status(200).send(list.map(({ id, name }) => ({ id, name })));
|
|
69
|
+
});
|
|
70
|
+
app.get('/flows/:id', async (req, res) => {
|
|
71
|
+
const { id } = req.params;
|
|
72
|
+
const flow = list.find((flow) => flow.id === id);
|
|
73
|
+
if (!flow) {
|
|
74
|
+
res.status(404).send({ error: 'Flow not found' });
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
res.status(200).send(flow);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
};
|
|
81
|
+
exports.flowsEndpoint = flowsEndpoint;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const getStepLanguage: (fileExtension?: string) => string | undefined;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.getStepLanguage = void 0;
|
|
4
|
+
const getStepLanguage = (fileExtension) => {
|
|
5
|
+
if (!fileExtension)
|
|
6
|
+
return;
|
|
7
|
+
if (fileExtension.endsWith('.js')) {
|
|
8
|
+
return 'javascript';
|
|
9
|
+
}
|
|
10
|
+
if (fileExtension.endsWith('.ts')) {
|
|
11
|
+
return 'typescript';
|
|
12
|
+
}
|
|
13
|
+
if (fileExtension.endsWith('.py')) {
|
|
14
|
+
return 'python';
|
|
15
|
+
}
|
|
16
|
+
if (fileExtension.endsWith('.go')) {
|
|
17
|
+
return 'go';
|
|
18
|
+
}
|
|
19
|
+
if (fileExtension.endsWith('.rb')) {
|
|
20
|
+
return 'ruby';
|
|
21
|
+
}
|
|
22
|
+
if (fileExtension.endsWith('.php')) {
|
|
23
|
+
return 'php';
|
|
24
|
+
}
|
|
25
|
+
return;
|
|
26
|
+
};
|
|
27
|
+
exports.getStepLanguage = getStepLanguage;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { ApiRouteConfig, EventConfig, NoopConfig, Step } from './types';
|
|
2
|
+
export declare const isApiStep: (step: Step) => step is Step<ApiRouteConfig>;
|
|
3
|
+
export declare const isEventStep: (step: Step) => step is Step<EventConfig<any>>;
|
|
4
|
+
export declare const isNoopStep: (step: Step) => step is Step<NoopConfig>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isNoopStep = exports.isEventStep = exports.isApiStep = void 0;
|
|
4
|
+
const isApiStep = (step) => step.config.type === 'api';
|
|
5
|
+
exports.isApiStep = isApiStep;
|
|
6
|
+
const isEventStep = (step) => step.config.type === 'event';
|
|
7
|
+
exports.isEventStep = isEventStep;
|
|
8
|
+
const isNoopStep = (step) => step.config.type === 'noop';
|
|
9
|
+
exports.isNoopStep = isNoopStep;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Server } from 'socket.io';
|
|
2
|
+
declare class BaseLogger {
|
|
3
|
+
private logger;
|
|
4
|
+
constructor(meta?: any);
|
|
5
|
+
info(message: string, args?: any): void;
|
|
6
|
+
error(message: string, args?: any): void;
|
|
7
|
+
debug(message: string, args?: any): void;
|
|
8
|
+
warn(message: string, args?: any): void;
|
|
9
|
+
}
|
|
10
|
+
export declare class Logger extends BaseLogger {
|
|
11
|
+
private readonly traceId;
|
|
12
|
+
private readonly flows;
|
|
13
|
+
private readonly file;
|
|
14
|
+
private emitLog;
|
|
15
|
+
constructor(traceId: string, flows: string[], file: string, socketServer?: Server);
|
|
16
|
+
log(message: any): void;
|
|
17
|
+
info: (message: string, args?: any) => void;
|
|
18
|
+
error: (message: string, args?: any) => void;
|
|
19
|
+
debug: (message: string, args?: any) => void;
|
|
20
|
+
warn: (message: string, args?: any) => void;
|
|
21
|
+
}
|
|
22
|
+
export declare const globalLogger: BaseLogger;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
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.globalLogger = exports.Logger = void 0;
|
|
7
|
+
const pino_1 = __importDefault(require("pino"));
|
|
8
|
+
const isDebugEnabled = () => process.env.LOG_LEVEL === 'debug';
|
|
9
|
+
class BaseLogger {
|
|
10
|
+
constructor(meta = {}) {
|
|
11
|
+
this.logger = (0, pino_1.default)({
|
|
12
|
+
level: process.env.LOG_LEVEL || 'info',
|
|
13
|
+
formatters: { level: (level) => ({ level }) },
|
|
14
|
+
base: null,
|
|
15
|
+
mixin: () => meta,
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
info(message, args) {
|
|
19
|
+
this.logger.info(args, message);
|
|
20
|
+
}
|
|
21
|
+
error(message, args) {
|
|
22
|
+
this.logger.error(args, message);
|
|
23
|
+
}
|
|
24
|
+
debug(message, args) {
|
|
25
|
+
this.logger.debug(args, message);
|
|
26
|
+
}
|
|
27
|
+
warn(message, args) {
|
|
28
|
+
this.logger.warn(args, message);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
class Logger extends BaseLogger {
|
|
32
|
+
constructor(traceId, flows, file, socketServer) {
|
|
33
|
+
super({ traceId, flows, file });
|
|
34
|
+
this.traceId = traceId;
|
|
35
|
+
this.flows = flows;
|
|
36
|
+
this.file = file;
|
|
37
|
+
this.info = (message, args) => {
|
|
38
|
+
super.info(message, args);
|
|
39
|
+
this.emitLog('info', message, args);
|
|
40
|
+
};
|
|
41
|
+
this.error = (message, args) => {
|
|
42
|
+
super.error(message, args);
|
|
43
|
+
this.emitLog('error', message, args);
|
|
44
|
+
};
|
|
45
|
+
this.debug = (message, args) => {
|
|
46
|
+
super.debug(message, args);
|
|
47
|
+
if (isDebugEnabled()) {
|
|
48
|
+
this.emitLog('debug', message, args);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
this.warn = (message, args) => {
|
|
52
|
+
super.warn(message, args);
|
|
53
|
+
this.emitLog('warn', message, args);
|
|
54
|
+
};
|
|
55
|
+
this.emitLog = (level, msg, args) => {
|
|
56
|
+
if (!socketServer) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
socketServer.emit('log', {
|
|
60
|
+
file: this.file,
|
|
61
|
+
...(args ?? {}),
|
|
62
|
+
level,
|
|
63
|
+
time: Date.now(),
|
|
64
|
+
msg,
|
|
65
|
+
traceId: this.traceId,
|
|
66
|
+
flows: this.flows,
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
log(message) {
|
|
71
|
+
console.log(JSON.stringify(message));
|
|
72
|
+
this.emitLog(message.level, message.msg, message);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
exports.Logger = Logger;
|
|
76
|
+
exports.globalLogger = new BaseLogger();
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare class Logger {
|
|
2
|
+
private logger;
|
|
3
|
+
constructor(traceId: string, flows: string[], file?: string);
|
|
4
|
+
info: (message: string, args: Record<string, unknown>) => void;
|
|
5
|
+
error: (message: string, args: Record<string, unknown>) => void;
|
|
6
|
+
debug: (message: string, args: Record<string, unknown>) => void;
|
|
7
|
+
warn: (message: string, args: Record<string, unknown>) => void;
|
|
8
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
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.Logger = void 0;
|
|
7
|
+
const pino_1 = __importDefault(require("pino"));
|
|
8
|
+
class Logger {
|
|
9
|
+
constructor(traceId, flows, file) {
|
|
10
|
+
this.info = (message, args) => this.logger.info(args, message);
|
|
11
|
+
this.error = (message, args) => this.logger.error(args, message);
|
|
12
|
+
this.debug = (message, args) => this.logger.debug(args, message);
|
|
13
|
+
this.warn = (message, args) => this.logger.warn(args, message);
|
|
14
|
+
this.logger = (0, pino_1.default)({
|
|
15
|
+
level: 'info',
|
|
16
|
+
formatters: { level: (level) => ({ level }) },
|
|
17
|
+
errorKey: 'error',
|
|
18
|
+
base: null,
|
|
19
|
+
mixin: () => ({ traceId, flows, file }),
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.Logger = Logger;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,56 @@
|
|
|
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
|
+
const path_1 = __importDefault(require("path"));
|
|
7
|
+
const logger_1 = require("./logger");
|
|
8
|
+
const rpc_state_manager_1 = require("./rpc-state-manager");
|
|
9
|
+
const rpc_1 = require("./rpc");
|
|
10
|
+
// Add ts-node registration before dynamic imports
|
|
11
|
+
require('ts-node').register({
|
|
12
|
+
transpileOnly: true,
|
|
13
|
+
compilerOptions: { module: 'commonjs' },
|
|
14
|
+
});
|
|
15
|
+
function parseArgs(arg) {
|
|
16
|
+
try {
|
|
17
|
+
return JSON.parse(arg);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return arg;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
async function runTypescriptModule(filePath, args) {
|
|
24
|
+
try {
|
|
25
|
+
// Remove pathToFileURL since we'll use require
|
|
26
|
+
const module = require(path_1.default.resolve(filePath));
|
|
27
|
+
// Check if the specified function exists in the module
|
|
28
|
+
if (typeof module.handler !== 'function') {
|
|
29
|
+
throw new Error(`Function handler not found in module ${filePath}`);
|
|
30
|
+
}
|
|
31
|
+
const { stateConfig, ...event } = args;
|
|
32
|
+
const { traceId, flows } = event;
|
|
33
|
+
const logger = new logger_1.Logger(traceId, flows, filePath.split('/').pop());
|
|
34
|
+
const sender = new rpc_1.RpcSender(process);
|
|
35
|
+
const state = new rpc_state_manager_1.RpcStateManager(sender);
|
|
36
|
+
const emit = async (data) => sender.send('emit', data);
|
|
37
|
+
const context = { traceId, flows, logger, state, emit };
|
|
38
|
+
sender.init();
|
|
39
|
+
// Call the function with provided arguments
|
|
40
|
+
await module.handler(event.data, context);
|
|
41
|
+
process.exit(0);
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
console.error('Error running TypeScript module:', error);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const [, , filePath, arg] = process.argv;
|
|
49
|
+
if (!filePath) {
|
|
50
|
+
console.error('Usage: node nodeRunner.js <file-path> <arg>');
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
runTypescriptModule(filePath, parseArgs(arg)).catch((err) => {
|
|
54
|
+
console.error('Error:', err);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { InternalStateManager } from '../types';
|
|
2
|
+
import { RpcSender } from './rpc';
|
|
3
|
+
export declare class RpcStateManager implements InternalStateManager {
|
|
4
|
+
private readonly sender;
|
|
5
|
+
constructor(sender: RpcSender);
|
|
6
|
+
get<T>(traceId: string, key: string): Promise<T>;
|
|
7
|
+
set<T>(traceId: string, key: string, value: T): Promise<void>;
|
|
8
|
+
delete(traceId: string, key: string): Promise<void>;
|
|
9
|
+
clear(traceId: string): Promise<void>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RpcStateManager = void 0;
|
|
4
|
+
class RpcStateManager {
|
|
5
|
+
constructor(sender) {
|
|
6
|
+
this.sender = sender;
|
|
7
|
+
}
|
|
8
|
+
async get(traceId, key) {
|
|
9
|
+
return this.sender.send('state.get', { traceId, key });
|
|
10
|
+
}
|
|
11
|
+
async set(traceId, key, value) {
|
|
12
|
+
await this.sender.send('state.set', { traceId, key, value });
|
|
13
|
+
}
|
|
14
|
+
async delete(traceId, key) {
|
|
15
|
+
await this.sender.send('state.delete', { traceId, key });
|
|
16
|
+
}
|
|
17
|
+
async clear(traceId) {
|
|
18
|
+
await this.sender.send('state.clear', { traceId });
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.RpcStateManager = RpcStateManager;
|
|
@@ -0,0 +1,32 @@
|
|
|
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.RpcSender = void 0;
|
|
7
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
8
|
+
class RpcSender {
|
|
9
|
+
constructor(process) {
|
|
10
|
+
this.process = process;
|
|
11
|
+
this.pendingRequests = {};
|
|
12
|
+
}
|
|
13
|
+
send(method, args) {
|
|
14
|
+
return new Promise((resolve, reject) => {
|
|
15
|
+
const id = crypto_1.default.randomUUID();
|
|
16
|
+
this.pendingRequests[id] = { resolve, reject, method, args };
|
|
17
|
+
process.send?.({ type: 'rpc_request', id, method, args });
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
init() {
|
|
21
|
+
this.process.on('message', (msg) => {
|
|
22
|
+
if (msg.type === 'rpc_response') {
|
|
23
|
+
const { id, result, error } = msg;
|
|
24
|
+
const callbacks = this.pendingRequests[id];
|
|
25
|
+
const callback = error ? callbacks.reject : callbacks.resolve;
|
|
26
|
+
callback(result);
|
|
27
|
+
delete this.pendingRequests[id];
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
exports.RpcSender = RpcSender;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import json
|
|
3
|
+
import importlib.util
|
|
4
|
+
import os
|
|
5
|
+
from typing import Any, Callable
|
|
6
|
+
|
|
7
|
+
# get the FD from ENV
|
|
8
|
+
NODEIPCFD = int(os.environ["NODE_CHANNEL_FD"])
|
|
9
|
+
|
|
10
|
+
def sendMessage(text):
|
|
11
|
+
'sends a Node IPC message to parent proccess'
|
|
12
|
+
# encode message as json string + newline in bytes
|
|
13
|
+
bytesMessage = (json.dumps(text) + "\n").encode('utf-8')
|
|
14
|
+
# send message
|
|
15
|
+
os.write(NODEIPCFD, bytesMessage)
|
|
16
|
+
|
|
17
|
+
async def run_python_module(file_path: str) -> None:
|
|
18
|
+
try:
|
|
19
|
+
# Load the module dynamically
|
|
20
|
+
spec = importlib.util.spec_from_file_location("dynamic_module", file_path)
|
|
21
|
+
if spec is None or spec.loader is None:
|
|
22
|
+
raise ImportError(f"Could not load module from {file_path}")
|
|
23
|
+
|
|
24
|
+
module = importlib.util.module_from_spec(spec)
|
|
25
|
+
spec.loader.exec_module(module)
|
|
26
|
+
|
|
27
|
+
# Print the config if it exists
|
|
28
|
+
if hasattr(module, 'config'):
|
|
29
|
+
sendMessage(module.config)
|
|
30
|
+
|
|
31
|
+
except Exception as error:
|
|
32
|
+
print('Error running Python module:', str(error), file=sys.stderr)
|
|
33
|
+
sys.exit(1)
|
|
34
|
+
|
|
35
|
+
if __name__ == "__main__":
|
|
36
|
+
if len(sys.argv) < 2:
|
|
37
|
+
sys.exit(1)
|
|
38
|
+
|
|
39
|
+
file_path = sys.argv[1]
|
|
40
|
+
|
|
41
|
+
import asyncio
|
|
42
|
+
asyncio.run(run_python_module(file_path))
|
|
@@ -0,0 +1,34 @@
|
|
|
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.getPythonConfig = void 0;
|
|
7
|
+
const child_process_1 = require("child_process");
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const logger_1 = require("../logger");
|
|
10
|
+
const getPythonConfig = (file) => {
|
|
11
|
+
const getConfig = path_1.default.join(__dirname, 'get-config.py');
|
|
12
|
+
return new Promise((resolve, reject) => {
|
|
13
|
+
let config = null;
|
|
14
|
+
const child = (0, child_process_1.spawn)('python', [getConfig, file], {
|
|
15
|
+
stdio: ['inherit', 'inherit', 'inherit', 'ipc'],
|
|
16
|
+
});
|
|
17
|
+
child.on('message', (message) => {
|
|
18
|
+
logger_1.globalLogger.debug('[Python Config] Read config', { config: message });
|
|
19
|
+
config = message;
|
|
20
|
+
});
|
|
21
|
+
child.on('close', (code) => {
|
|
22
|
+
if (code !== 0) {
|
|
23
|
+
reject(new Error(`Process exited with code ${code}`));
|
|
24
|
+
}
|
|
25
|
+
else if (!config) {
|
|
26
|
+
reject(new Error(`No config found for file ${file}`));
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
resolve(config);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
exports.getPythonConfig = getPythonConfig;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import time
|
|
2
|
+
from typing import Any, Dict, Optional
|
|
3
|
+
from rpc import RpcSender
|
|
4
|
+
|
|
5
|
+
class Logger:
|
|
6
|
+
def __init__(self, trace_id: str, flows: list[str], file_path: str, sender: RpcSender):
|
|
7
|
+
self.trace_id = trace_id
|
|
8
|
+
self.flows = flows
|
|
9
|
+
self.file_name = file_path.split('/')[-1]
|
|
10
|
+
self.sender = sender
|
|
11
|
+
|
|
12
|
+
def _log(self, level: str, message: str, args: Optional[Dict[str, Any]] = None) -> None:
|
|
13
|
+
log_entry = {
|
|
14
|
+
"level": level,
|
|
15
|
+
"time": int(time.time() * 1000),
|
|
16
|
+
"traceId": self.trace_id,
|
|
17
|
+
"flows": self.flows,
|
|
18
|
+
"file": self.file_name,
|
|
19
|
+
"msg": message
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if args:
|
|
23
|
+
# Convert SimpleNamespace to dict if needed
|
|
24
|
+
if hasattr(args, '__dict__'):
|
|
25
|
+
args = vars(args)
|
|
26
|
+
elif not isinstance(args, dict):
|
|
27
|
+
args = {"data": args}
|
|
28
|
+
log_entry.update(args)
|
|
29
|
+
|
|
30
|
+
self.sender.send_no_wait('log', log_entry)
|
|
31
|
+
|
|
32
|
+
def info(self, message: str, args: Optional[Any] = None) -> None:
|
|
33
|
+
self._log("info", message, args)
|
|
34
|
+
|
|
35
|
+
def error(self, message: str, args: Optional[Any] = None) -> None:
|
|
36
|
+
self._log("error", message, args)
|
|
37
|
+
|
|
38
|
+
def debug(self, message: str, args: Optional[Any] = None) -> None:
|
|
39
|
+
self._log("debug", message, args)
|
|
40
|
+
|
|
41
|
+
def warn(self, message: str, args: Optional[Any] = None) -> None:
|
|
42
|
+
self._log("warn", message, args)
|