@motiadev/core 0.0.28 → 0.0.29

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.
@@ -0,0 +1,14 @@
1
+ import { Server } from 'socket.io';
2
+ import { Logger } from './logger';
3
+ type CreateLogger = {
4
+ traceId: string;
5
+ flows?: string[];
6
+ stepName: string;
7
+ };
8
+ export declare class LoggerFactory {
9
+ private readonly isVerbose;
10
+ private readonly socketServer;
11
+ constructor(isVerbose: boolean, socketServer: Server);
12
+ create({ stepName, traceId, flows }: CreateLogger): Logger;
13
+ }
14
+ export {};
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LoggerFactory = void 0;
4
+ const logger_1 = require("./logger");
5
+ class LoggerFactory {
6
+ constructor(isVerbose, socketServer) {
7
+ this.isVerbose = isVerbose;
8
+ this.socketServer = socketServer;
9
+ }
10
+ create({ stepName, traceId, flows }) {
11
+ return new logger_1.Logger(traceId, flows, stepName, this.isVerbose, this.socketServer);
12
+ }
13
+ }
14
+ exports.LoggerFactory = LoggerFactory;
@@ -70,12 +70,20 @@ const callStepFile = (options) => {
70
70
  child.stderr?.on('data', (data) => logger.error(Buffer.from(data).toString()));
71
71
  child.on('close', (code) => {
72
72
  if (code !== 0 && code !== null) {
73
- reject(new Error(`Process exited with code ${code}`));
73
+ reject(`Process exited with code ${code}`);
74
74
  }
75
75
  else {
76
76
  resolve(result);
77
77
  }
78
78
  });
79
+ child.on('error', (error) => {
80
+ if (error.code === 'ENOENT') {
81
+ reject(`Executable ${command} not found`);
82
+ }
83
+ else {
84
+ reject(error);
85
+ }
86
+ });
79
87
  });
80
88
  };
81
89
  exports.callStepFile = callStepFile;
@@ -1,13 +1,13 @@
1
- import { Server } from 'socket.io';
2
1
  import { LockedData } from './locked-data';
3
2
  import { StateAdapter } from './state/state-adapter';
4
3
  import { CronConfig, EventManager, Step } from './types';
4
+ import { LoggerFactory } from './LoggerFactory';
5
5
  export type CronManager = {
6
6
  createCronJob: (step: Step<CronConfig>) => void;
7
7
  removeCronJob: (step: Step<CronConfig>) => void;
8
8
  close: () => void;
9
9
  };
10
- export declare const setupCronHandlers: (lockedData: LockedData, eventManager: EventManager, state: StateAdapter, socketServer: Server) => {
10
+ export declare const setupCronHandlers: (lockedData: LockedData, eventManager: EventManager, state: StateAdapter, loggerFactory: LoggerFactory) => {
11
11
  createCronJob: (step: Step<CronConfig>) => void;
12
12
  removeCronJob: (step: Step<CronConfig>) => void;
13
13
  close: () => void;
@@ -38,12 +38,12 @@ const crypto_1 = require("crypto");
38
38
  const cron = __importStar(require("node-cron"));
39
39
  const call_step_file_1 = require("./call-step-file");
40
40
  const logger_1 = require("./logger");
41
- const setupCronHandlers = (lockedData, eventManager, state, socketServer) => {
41
+ const setupCronHandlers = (lockedData, eventManager, state, loggerFactory) => {
42
42
  const cronJobs = new Map();
43
43
  const printer = lockedData.printer;
44
44
  const createCronJob = (step) => {
45
45
  const { config, filePath } = step;
46
- const { cron: cronExpression, name: stepName } = config;
46
+ const { cron: cronExpression, name: stepName, flows } = config;
47
47
  if (!cron.validate(cronExpression)) {
48
48
  logger_1.globalLogger.error('[cron handler] invalid cron expression', {
49
49
  expression: cronExpression,
@@ -58,7 +58,7 @@ const setupCronHandlers = (lockedData, eventManager, state, socketServer) => {
58
58
  });
59
59
  const task = cron.schedule(cronExpression, async () => {
60
60
  const traceId = (0, crypto_1.randomUUID)();
61
- const logger = new logger_1.Logger(traceId, config.flows, stepName, socketServer);
61
+ const logger = loggerFactory.create({ traceId, flows, stepName });
62
62
  try {
63
63
  await (0, call_step_file_1.callStepFile)({
64
64
  contextInFirstArg: true,
@@ -47,10 +47,18 @@ const getStepConfig = (file) => {
47
47
  return; // Config was already resolved
48
48
  }
49
49
  else if (code !== 0) {
50
- reject(new Error(`Process exited with code ${code}`));
50
+ reject(`Process exited with code ${code}`);
51
51
  }
52
52
  else if (!config) {
53
- reject(new Error(`No config found for file ${file}`));
53
+ reject(`No config found for file ${file}`);
54
+ }
55
+ });
56
+ child.on('error', (error) => {
57
+ if (error.code === 'ENOENT') {
58
+ reject(`Executable ${command} not found`);
59
+ }
60
+ else {
61
+ reject(error);
54
62
  }
55
63
  });
56
64
  });
@@ -1,14 +1,15 @@
1
1
  import { Server } from 'socket.io';
2
2
  export declare class BaseLogger {
3
+ readonly isVerbose: boolean;
3
4
  private readonly meta;
4
- private logger;
5
- constructor(meta?: Record<string, unknown>);
5
+ constructor(isVerbose?: boolean, meta?: Record<string, unknown>);
6
6
  child(meta?: Record<string, unknown>): this;
7
+ private _log;
7
8
  info(message: string, args?: unknown): void;
8
9
  error(message: string, args?: unknown): void;
9
10
  debug(message: string, args?: unknown): void;
10
11
  warn(message: string, args?: unknown): void;
11
- log(args: unknown): void;
12
+ log(args: any): void;
12
13
  }
13
14
  export declare class Logger extends BaseLogger {
14
15
  private readonly traceId;
@@ -16,7 +17,7 @@ export declare class Logger extends BaseLogger {
16
17
  private readonly step;
17
18
  private readonly socketServer?;
18
19
  private emitLog;
19
- constructor(traceId: string, flows: string[] | undefined, step: string, socketServer?: Server | undefined);
20
+ constructor(traceId: string, flows: string[] | undefined, step: string, isVerbose: boolean, socketServer?: Server | undefined);
20
21
  child(meta?: Record<string, unknown>): this;
21
22
  log(message: any): void;
22
23
  info: (message: string, args?: unknown) => void;
@@ -1,44 +1,51 @@
1
1
  "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
2
  Object.defineProperty(exports, "__esModule", { value: true });
6
3
  exports.globalLogger = exports.Logger = exports.BaseLogger = void 0;
7
- const pino_1 = __importDefault(require("pino"));
8
- const isDebugEnabled = () => process.env.LOG_LEVEL === 'debug';
4
+ const pretty_print_1 = require("./pretty-print");
5
+ const logLevel = process.env.LOG_LEVEL ?? 'info';
6
+ const isDebugEnabled = logLevel === 'debug';
7
+ const isInfoEnabled = ['info', 'debug'].includes(logLevel);
8
+ const isWarnEnabled = ['warn', 'info', 'debug', 'trace'].includes(logLevel);
9
9
  class BaseLogger {
10
- constructor(meta = {}) {
10
+ constructor(isVerbose = false, meta = {}) {
11
+ this.isVerbose = isVerbose;
11
12
  this.meta = meta;
12
- this.logger = (0, pino_1.default)({
13
- level: process.env.LOG_LEVEL || 'info',
14
- formatters: { level: (level) => ({ level }) },
15
- base: null,
16
- mixin: () => meta,
17
- });
18
13
  }
19
14
  child(meta = {}) {
20
- return new BaseLogger({ ...this.meta, ...meta });
15
+ return new BaseLogger(this.isVerbose, { ...this.meta, ...meta });
16
+ }
17
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
18
+ _log(level, msg, args) {
19
+ const time = Date.now();
20
+ (0, pretty_print_1.prettyPrint)({ level, time, msg, ...this.meta, ...(args ?? {}) }, !this.isVerbose);
21
21
  }
22
22
  info(message, args) {
23
- this.logger.info(args, message);
23
+ if (isInfoEnabled) {
24
+ this._log('info', message, args);
25
+ }
24
26
  }
25
27
  error(message, args) {
26
- this.logger.error(args, message);
28
+ this._log('error', message, args);
27
29
  }
28
30
  debug(message, args) {
29
- this.logger.debug(args, message);
31
+ if (isDebugEnabled) {
32
+ this._log('debug', message, args);
33
+ }
30
34
  }
31
35
  warn(message, args) {
32
- this.logger.warn(args, message);
36
+ if (isWarnEnabled) {
37
+ this._log('warn', message, args);
38
+ }
33
39
  }
40
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
34
41
  log(args) {
35
- this.logger.info(args);
42
+ this._log('info', args.msg, args);
36
43
  }
37
44
  }
38
45
  exports.BaseLogger = BaseLogger;
39
46
  class Logger extends BaseLogger {
40
- constructor(traceId, flows, step, socketServer) {
41
- super({ traceId, flows, step });
47
+ constructor(traceId, flows, step, isVerbose, socketServer) {
48
+ super(isVerbose, { traceId, flows, step });
42
49
  this.traceId = traceId;
43
50
  this.flows = flows;
44
51
  this.step = step;
@@ -52,8 +59,8 @@ class Logger extends BaseLogger {
52
59
  this.emitLog('error', message, args);
53
60
  };
54
61
  this.debug = (message, args) => {
55
- super.debug(message, args);
56
- if (isDebugEnabled()) {
62
+ if (isDebugEnabled) {
63
+ super.debug(message, args);
57
64
  this.emitLog('debug', message, args);
58
65
  }
59
66
  };
@@ -74,11 +81,11 @@ class Logger extends BaseLogger {
74
81
  };
75
82
  }
76
83
  child(meta = {}) {
77
- return new Logger(this.traceId, this.flows, meta.step, this.socketServer);
84
+ return new Logger(this.traceId, this.flows, meta.step, this.isVerbose, this.socketServer);
78
85
  }
79
86
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
80
87
  log(message) {
81
- console.log(JSON.stringify(message));
88
+ super.log(message);
82
89
  this.emitLog(message.level, message.msg, message);
83
90
  }
84
91
  }
@@ -0,0 +1 @@
1
+ export declare const prettyPrint: (json: Record<string, any>, excludeDetails?: boolean) => void;
@@ -0,0 +1,66 @@
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.prettyPrint = void 0;
7
+ const colors_1 = __importDefault(require("colors"));
8
+ const stepTag = (step) => colors_1.default.bold(colors_1.default.cyan(step));
9
+ const timestampTag = (timestamp) => colors_1.default.gray(timestamp);
10
+ const traceIdTag = (traceId) => colors_1.default.gray(traceId);
11
+ const levelTags = {
12
+ error: colors_1.default.red('[ERROR]'),
13
+ info: colors_1.default.blue('[INFO]'),
14
+ warn: colors_1.default.yellow('[WARN]'),
15
+ debug: colors_1.default.gray('[DEBUG]'),
16
+ trace: colors_1.default.gray('[TRACE]'),
17
+ };
18
+ const numericTag = (value) => colors_1.default.green(value);
19
+ const stringTag = (value) => colors_1.default.cyan(value);
20
+ const booleanTag = (value) => colors_1.default.blue(value);
21
+ const arrayBrackets = ['[', ']'].map((s) => colors_1.default.gray(s));
22
+ const objectBrackets = ['{', '}'].map((s) => colors_1.default.gray(s));
23
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
24
+ const prettyPrintObject = (obj, depth = 0, parentIsLast = false, prefix = '') => {
25
+ const tab = prefix + (depth === 0 ? '' : parentIsLast ? '│ ' : '│ ');
26
+ if (depth > 2) {
27
+ return `${tab} └ ${colors_1.default.gray('[...]')}`;
28
+ }
29
+ const entries = Object.entries(obj);
30
+ return entries
31
+ .map(([key, value], index) => {
32
+ const isLast = index === entries.length - 1;
33
+ const isObject = typeof value === 'object' && value !== null;
34
+ const prefix = isLast && !isObject ? '└' : '├';
35
+ if (isObject) {
36
+ const subObject = prettyPrintObject(value, depth + 1, isLast, tab);
37
+ const [start, end] = Array.isArray(value) ? arrayBrackets : objectBrackets;
38
+ return `${tab}${prefix} ${key}: ${start}\n${subObject}\n${tab}${isLast ? '└' : '│'} ${end}`;
39
+ }
40
+ let printedValue = value;
41
+ if (typeof value === 'number') {
42
+ printedValue = numericTag(String(value));
43
+ }
44
+ else if (typeof value === 'boolean') {
45
+ printedValue = booleanTag(String(value));
46
+ }
47
+ else if (typeof value === 'string') {
48
+ printedValue = stringTag(value);
49
+ }
50
+ return `${tab}${prefix} ${key}: ${printedValue}`;
51
+ })
52
+ .join('\n');
53
+ };
54
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
55
+ const prettyPrint = (json, excludeDetails = false) => {
56
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
57
+ const { time, traceId, msg, flows, level, step, ...details } = json;
58
+ const levelTag = levelTags[level];
59
+ const timestamp = timestampTag(`[${new Date(time).toLocaleTimeString()}]`);
60
+ const objectHasKeys = Object.keys(details).length > 0;
61
+ console.log(`${timestamp} ${traceIdTag(traceId)} ${levelTag} ${stepTag(step)} ${msg}`);
62
+ if (objectHasKeys && !excludeDetails) {
63
+ console.log(prettyPrintObject(details));
64
+ }
65
+ };
66
+ exports.prettyPrint = prettyPrint;
@@ -14,4 +14,8 @@ export type MotiaServer = {
14
14
  addRoute: (step: Step<ApiRouteConfig>) => void;
15
15
  cronManager: CronManager;
16
16
  };
17
- export declare const createServer: (lockedData: LockedData, eventManager: EventManager, state: StateAdapter) => Promise<MotiaServer>;
17
+ type MotiaServerConfig = {
18
+ isVerbose: boolean;
19
+ };
20
+ export declare const createServer: (lockedData: LockedData, eventManager: EventManager, state: StateAdapter, config: MotiaServerConfig) => Promise<MotiaServer>;
21
+ export {};
@@ -15,18 +15,20 @@ const guards_1 = require("./guards");
15
15
  const logger_1 = require("./logger");
16
16
  const steps_1 = require("./steps");
17
17
  const call_step_file_1 = require("./call-step-file");
18
- const createServer = async (lockedData, eventManager, state) => {
18
+ const LoggerFactory_1 = require("./LoggerFactory");
19
+ const createServer = async (lockedData, eventManager, state, config) => {
19
20
  const printer = lockedData.printer;
20
21
  const app = (0, express_1.default)();
21
22
  const server = http_1.default.createServer(app);
22
23
  const io = new socket_io_1.Server(server);
24
+ const loggerFactory = new LoggerFactory_1.LoggerFactory(config.isVerbose, io);
23
25
  const allSteps = [...steps_1.systemSteps, ...lockedData.activeSteps];
24
- const cronManager = (0, cron_handler_1.setupCronHandlers)(lockedData, eventManager, state, io);
26
+ const cronManager = (0, cron_handler_1.setupCronHandlers)(lockedData, eventManager, state, loggerFactory);
25
27
  const asyncHandler = (step) => {
26
28
  return async (req, res) => {
27
29
  const traceId = (0, crypto_1.randomUUID)();
28
- const { name, flows } = step.config;
29
- const logger = new logger_1.Logger(traceId, flows, name, io);
30
+ const { name: stepName, flows } = step.config;
31
+ const logger = loggerFactory.create({ traceId, flows, stepName });
30
32
  logger.debug('[API] Received request, processing step', { path: req.path });
31
33
  const request = {
32
34
  body: req.body,
@@ -38,11 +38,8 @@ const createStepHandlers = (lockedData, eventManager, state) => {
38
38
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
39
39
  }
40
40
  catch (error) {
41
- logger_1.globalLogger.error(`[step handler] error calling step`, {
42
- error: error.message,
43
- filePath,
44
- step: name,
45
- });
41
+ const message = typeof error === 'string' ? error : error.message;
42
+ logger.error(message);
46
43
  }
47
44
  },
48
45
  });
package/package.json CHANGED
@@ -1,14 +1,13 @@
1
1
  {
2
2
  "name": "@motiadev/core",
3
3
  "main": "dist/index.js",
4
- "version": "0.0.28",
4
+ "version": "0.0.29",
5
5
  "dependencies": {
6
6
  "body-parser": "^1.20.3",
7
7
  "colors": "^1.4.0",
8
8
  "express": "^4.21.2",
9
9
  "ioredis": "^5.4.2",
10
10
  "node-cron": "^3.0.3",
11
- "pino": "^9.6.0",
12
11
  "socket.io": "^4.8.1",
13
12
  "ts-node": "^10.9.2",
14
13
  "tsconfig-paths": "^4.2.0",