@hahnpro/flow-sdk 4.23.7 → 4.24.0

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.
@@ -1,30 +1,42 @@
1
1
  import 'reflect-metadata';
2
+ import { API } from '@hahnpro/hpc-api';
3
+ import { AmqpConnectionManager } from 'amqp-connection-manager';
2
4
  import { CloudEvent } from 'cloudevents';
3
5
  import { PartialObserver } from 'rxjs';
4
- import { API } from '@hahnpro/hpc-api';
5
- import { AmqpConnection, Nack } from './amqp';
6
+ import { AmqpConnection, AmqpConnectionConfig, Nack } from './amqp';
6
7
  import { ClassType, Flow, FlowElementContext } from './flow.interface';
7
8
  import { FlowEvent } from './FlowEvent';
8
9
  import { Logger } from './FlowLogger';
9
10
  import { RpcClient } from './RpcClient';
11
+ interface FlowAppConfig {
12
+ logger?: Logger;
13
+ amqpConfig?: AmqpConnectionConfig;
14
+ amqpConnection?: AmqpConnectionManager;
15
+ skipApi?: boolean;
16
+ explicitInit?: boolean;
17
+ }
10
18
  export declare class FlowApplication {
11
19
  private modules;
12
20
  private flow;
13
- private readonly baseLogger?;
14
- private amqpConnection?;
15
- private skipApi;
16
- api: API;
21
+ private _api;
22
+ private _rpcClient;
23
+ private amqpChannel;
24
+ private readonly amqpConnection;
25
+ private readonly baseLogger;
17
26
  private context;
18
27
  private declarations;
19
28
  private elements;
29
+ private initialized;
20
30
  private readonly logger;
21
31
  private outputStreamMap;
22
32
  private outputQueueMetrics;
23
33
  private performanceMap;
24
34
  private properties;
25
- private _rpcClient;
26
- private initialized;
35
+ private readonly skipApi;
36
+ constructor(modules: ClassType<any>[], flow: Flow, config?: FlowAppConfig);
27
37
  constructor(modules: ClassType<any>[], flow: Flow, baseLogger?: Logger, amqpConnection?: AmqpConnection, skipApi?: boolean, explicitInit?: boolean);
38
+ get rpcClient(): RpcClient;
39
+ get api(): API;
28
40
  init(): Promise<void>;
29
41
  private publishLifecycleEvent;
30
42
  private setQueueMetrics;
@@ -38,8 +50,7 @@ export declare class FlowApplication {
38
50
  * Publish a flow event to the amqp flowlogs exchange.
39
51
  * If the event size exceeds the limit it will be truncated
40
52
  */
41
- publishEvent: (event: FlowEvent) => void;
42
- rpcClient(): Promise<RpcClient>;
53
+ publishEvent: (event: FlowEvent) => Promise<boolean>;
43
54
  /**
44
55
  * Calls onDestroy lifecycle method on all flow elements,
45
56
  * closes amqp connection after allowing logs to be processed and published
@@ -56,3 +67,4 @@ export interface Context extends FlowElementContext {
56
67
  app?: FlowApplication;
57
68
  logger?: Logger;
58
69
  }
70
+ export {};
@@ -3,36 +3,33 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FlowApplication = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  require("reflect-metadata");
6
+ const hpc_api_1 = require("@hahnpro/hpc-api");
6
7
  const cloudevents_1 = require("cloudevents");
8
+ const crypto_1 = require("crypto");
7
9
  const object_sizeof_1 = tslib_1.__importDefault(require("object-sizeof"));
8
10
  const perf_hooks_1 = require("perf_hooks");
9
11
  const rxjs_1 = require("rxjs");
10
12
  const operators_1 = require("rxjs/operators");
11
- const crypto_1 = require("crypto");
12
- const hpc_api_1 = require("@hahnpro/hpc-api");
13
13
  const amqp_1 = require("./amqp");
14
- const utils_1 = require("./utils");
15
14
  const flow_interface_1 = require("./flow.interface");
16
15
  const FlowLogger_1 = require("./FlowLogger");
17
16
  const RpcClient_1 = require("./RpcClient");
17
+ const utils_1 = require("./utils");
18
18
  const MAX_EVENT_SIZE_BYTES = +process.env.MAX_EVENT_SIZE_BYTES || 512 * 1024;
19
19
  const WARN_EVENT_PROCESSING_SEC = +process.env.WARN_EVENT_PROCESSING_SEC || 60;
20
20
  const WARN_EVENT_QUEUE_SIZE = +process.env.WARN_EVENT_QUEUE_SIZE || 100;
21
21
  class FlowApplication {
22
- constructor(modules, flow, baseLogger, amqpConnection, skipApi = false, explicitInit = false) {
22
+ constructor(modules, flow, baseLoggerOrConfig, amqpConnection, skipApi, explicitInit) {
23
23
  this.modules = modules;
24
24
  this.flow = flow;
25
- this.baseLogger = baseLogger;
26
- this.amqpConnection = amqpConnection;
27
- this.skipApi = skipApi;
28
25
  this.declarations = {};
29
26
  this.elements = {};
27
+ this.initialized = false;
30
28
  this.outputStreamMap = new Map();
31
29
  this.outputQueueMetrics = new Map();
32
30
  this.performanceMap = new Map();
33
- this.initialized = false;
34
31
  this.publishLifecycleEvent = (element, flowEventId, eventType, data = {}) => {
35
- if (!this.amqpConnection) {
32
+ if (!this.amqpChannel) {
36
33
  return;
37
34
  }
38
35
  try {
@@ -46,7 +43,7 @@ class FlowApplication {
46
43
  time: new Date().toISOString(),
47
44
  });
48
45
  const message = event.toJSON();
49
- return this.amqpConnection.publish('flow', 'lifecycle', message);
46
+ return this.amqpChannel.publish('flow', 'lifecycle', message);
50
47
  }
51
48
  catch (err) {
52
49
  this.logger.error(err);
@@ -138,7 +135,7 @@ class FlowApplication {
138
135
  data: { deploymentId: this.context.deploymentId, status: 'updated' },
139
136
  };
140
137
  try {
141
- (_f = this.amqpConnection) === null || _f === void 0 ? void 0 : _f.publish('deployment', 'health', statusEvent);
138
+ (_f = this.amqpChannel) === null || _f === void 0 ? void 0 : _f.publish('deployment', 'health', statusEvent);
142
139
  }
143
140
  catch (err) {
144
141
  this.logger.error(err);
@@ -154,7 +151,7 @@ class FlowApplication {
154
151
  data: { deploymentId: this.context.deploymentId, status: 'updating failed' },
155
152
  };
156
153
  try {
157
- (_g = this.amqpConnection) === null || _g === void 0 ? void 0 : _g.publish('deployment', 'health', statusEvent);
154
+ (_g = this.amqpChannel) === null || _g === void 0 ? void 0 : _g.publish('deployment', 'health', statusEvent);
158
155
  }
159
156
  catch (e) {
160
157
  this.logger.error(e);
@@ -182,7 +179,7 @@ class FlowApplication {
182
179
  return undefined;
183
180
  };
184
181
  this.publishEvent = (event) => {
185
- if (!this.amqpConnection) {
182
+ if (!this.amqpChannel) {
186
183
  return;
187
184
  }
188
185
  try {
@@ -190,13 +187,26 @@ class FlowApplication {
190
187
  if ((0, object_sizeof_1.default)(message) > MAX_EVENT_SIZE_BYTES) {
191
188
  message.data = (0, utils_1.truncate)(message.data);
192
189
  }
193
- return this.amqpConnection.publish('flowlogs', '', message);
190
+ return this.amqpChannel.publish('flowlogs', '', message);
194
191
  }
195
192
  catch (err) {
196
193
  this.logger.error(err);
197
194
  }
198
195
  };
199
- this.logger = new FlowLogger_1.FlowLogger(Object.assign({ id: 'none', functionFqn: 'FlowApplication' }, flow === null || flow === void 0 ? void 0 : flow.context), baseLogger || undefined, this.publishEvent);
196
+ if (baseLoggerOrConfig && !baseLoggerOrConfig.log) {
197
+ const config = baseLoggerOrConfig;
198
+ this.baseLogger = config.logger;
199
+ this.amqpConnection = config.amqpConnection || (0, amqp_1.createAmqpConnection)(config.amqpConfig);
200
+ this.skipApi = config.skipApi || false;
201
+ explicitInit = config.explicitInit || false;
202
+ }
203
+ else {
204
+ this.baseLogger = baseLoggerOrConfig;
205
+ this.amqpConnection = amqpConnection === null || amqpConnection === void 0 ? void 0 : amqpConnection.managedConnection;
206
+ this.skipApi = skipApi || false;
207
+ explicitInit = explicitInit || false;
208
+ }
209
+ this.logger = new FlowLogger_1.FlowLogger(Object.assign({ id: 'none', functionFqn: 'FlowApplication' }, flow === null || flow === void 0 ? void 0 : flow.context), this.baseLogger || undefined, this.publishEvent);
200
210
  process.once('uncaughtException', (err) => {
201
211
  this.logger.error('Uncaught exception!');
202
212
  this.logger.error(err);
@@ -214,14 +224,24 @@ class FlowApplication {
214
224
  this.init();
215
225
  }
216
226
  }
227
+ get rpcClient() {
228
+ if (!this._rpcClient && this.amqpConnection) {
229
+ this._rpcClient = new RpcClient_1.RpcClient(this.amqpConnection, this.logger);
230
+ }
231
+ return this._rpcClient;
232
+ }
233
+ get api() {
234
+ return this._api;
235
+ }
217
236
  async init() {
237
+ var _a;
218
238
  if (this.initialized)
219
239
  return;
220
240
  this.context = Object.assign({}, this.flow.context);
221
241
  this.properties = this.flow.properties || {};
222
242
  try {
223
243
  if (!this.skipApi) {
224
- this.api = new hpc_api_1.API();
244
+ this._api = new hpc_api_1.API();
225
245
  }
226
246
  }
227
247
  catch (err) {
@@ -231,28 +251,28 @@ class FlowApplication {
231
251
  this.logger.error(new Error(err));
232
252
  await this.destroy(1);
233
253
  };
234
- if (this.amqpConnection) {
235
- try {
236
- await this.amqpConnection.managedChannel.assertExchange('deployment', 'direct', { durable: true });
237
- await this.amqpConnection.managedChannel.assertExchange('flowlogs', 'fanout', { durable: true });
238
- await this.amqpConnection.managedChannel.assertExchange('flow', 'direct', { durable: true });
239
- }
240
- catch (e) {
241
- await logErrorAndExit(`Could not assert exchanges: ${e}`);
242
- return;
243
- }
244
- try {
245
- await this.amqpConnection.createSubscriber((msg) => this.onMessage(msg), {
246
- exchange: 'deployment',
247
- routingKey: this.context.deploymentId,
248
- queueOptions: { durable: false, exclusive: true },
249
- }, 'FlowApplication.onMessage');
250
- }
251
- catch (err) {
252
- await logErrorAndExit(`Could not subscribe to deployment exchange: ${err}`);
253
- return;
254
- }
255
- }
254
+ this.amqpChannel = (_a = this.amqpConnection) === null || _a === void 0 ? void 0 : _a.createChannel({
255
+ json: true,
256
+ setup: async (channel) => {
257
+ try {
258
+ await channel.assertExchange('deployment', 'direct', { durable: true });
259
+ await channel.assertExchange('flowlogs', 'fanout', { durable: true });
260
+ await channel.assertExchange('flow', 'direct', { durable: true });
261
+ }
262
+ catch (e) {
263
+ await logErrorAndExit(`Could not assert exchanges: ${e}`);
264
+ }
265
+ try {
266
+ const queue = await channel.assertQueue(null, { durable: false, exclusive: true });
267
+ await channel.bindQueue(queue.queue, 'deployment', this.context.deploymentId);
268
+ await channel.consume(queue.queue, (msg) => this.onMessage(msg));
269
+ }
270
+ catch (err) {
271
+ await logErrorAndExit(`Could not subscribe to deployment exchange: ${err}`);
272
+ }
273
+ },
274
+ });
275
+ this.amqpChannel && (await this.amqpChannel.waitForConnect());
256
276
  for (const module of this.modules) {
257
277
  const moduleName = Reflect.getMetadata('module:name', module);
258
278
  const moduleDeclarations = Reflect.getMetadata('module:declarations', module);
@@ -341,36 +361,26 @@ class FlowApplication {
341
361
  getProperties() {
342
362
  return this.properties;
343
363
  }
344
- async rpcClient() {
345
- if (!this.amqpConnection) {
346
- throw new Error('No AMQP connection available');
347
- }
348
- if (!this._rpcClient) {
349
- this._rpcClient = new RpcClient_1.RpcClient(this.amqpConnection);
350
- await this._rpcClient.init();
351
- }
352
- return this._rpcClient;
353
- }
354
364
  async destroy(exitCode = 0) {
355
- var _a, _b, _c, _d;
365
+ var _a;
356
366
  try {
357
367
  try {
358
368
  for (const element of Object.values(this.elements)) {
359
369
  (_a = element === null || element === void 0 ? void 0 : element.onDestroy) === null || _a === void 0 ? void 0 : _a.call(element);
360
370
  }
361
- await ((_b = this._rpcClient) === null || _b === void 0 ? void 0 : _b.close());
371
+ this._rpcClient && (await this._rpcClient.close());
362
372
  }
363
373
  catch (err) {
364
374
  this.logger.error(err);
365
375
  }
366
376
  await (0, utils_1.delay)(250);
367
- await ((_d = (_c = this.amqpConnection) === null || _c === void 0 ? void 0 : _c.managedConnection) === null || _d === void 0 ? void 0 : _d.close());
377
+ this.amqpConnection && (await this.amqpConnection.close());
368
378
  }
369
379
  catch (err) {
370
380
  console.error(err);
371
381
  }
372
382
  finally {
373
- if (process.env.JEST_WORKER_ID == undefined || process.env.NODE_ENV !== 'test') {
383
+ if (process.env.NODE_ENV !== 'test') {
374
384
  process.exit(exitCode);
375
385
  }
376
386
  }
@@ -92,13 +92,8 @@ class FlowElement {
92
92
  return this.validateProperties(classType, event.getData(), whitelist);
93
93
  }
94
94
  async callRpcFunction(functionName, ...args) {
95
- var _a, _b;
96
- try {
97
- return (_b = (await ((_a = this.app) === null || _a === void 0 ? void 0 : _a.rpcClient()))) === null || _b === void 0 ? void 0 : _b.callFunction(this.rpcRoutingKey, functionName, ...args);
98
- }
99
- catch (err) {
100
- this.logger.error(err);
101
- }
95
+ var _a;
96
+ return (_a = this.app) === null || _a === void 0 ? void 0 : _a.rpcClient.callFunction(this.rpcRoutingKey, functionName, ...args);
102
97
  }
103
98
  runPyRpcScript(scriptPath, ...args) {
104
99
  const options = {
@@ -1,10 +1,10 @@
1
- import { AmqpConnection } from './amqp';
1
+ import type { AmqpConnectionManager } from 'amqp-connection-manager';
2
+ import { FlowLogger } from './FlowLogger';
2
3
  export declare class RpcClient {
3
- private amqpConnection;
4
- private channel;
4
+ private readonly logger?;
5
+ private readonly channel;
5
6
  private openRequests;
6
- constructor(amqpConnection: AmqpConnection);
7
- init(): Promise<void>;
7
+ constructor(amqpConnection: AmqpConnectionManager, logger?: FlowLogger);
8
8
  private onMessage;
9
9
  callFunction: (routingKey: string, functionName: string, ...args: any[]) => Promise<unknown>;
10
10
  declareFunction: (routingKey: string, name: string) => (...args: any[]) => Promise<unknown>;
package/dist/RpcClient.js CHANGED
@@ -1,10 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RpcClient = void 0;
4
+ const tslib_1 = require("tslib");
4
5
  const crypto_1 = require("crypto");
6
+ const object_sizeof_1 = tslib_1.__importDefault(require("object-sizeof"));
7
+ const MAX_MSG_SIZE = +process.env.MAX_RPC_MSG_SIZE_BYTES;
8
+ const WARN_MSG_SIZE = +process.env.WARN_RPC_MSG_SIZE_BYTES;
5
9
  class RpcClient {
6
- constructor(amqpConnection) {
7
- this.amqpConnection = amqpConnection;
10
+ constructor(amqpConnection, logger) {
11
+ this.logger = logger;
8
12
  this.openRequests = new Map();
9
13
  this.onMessage = (msg) => {
10
14
  if (this.openRequests.has(msg.properties.correlationId)) {
@@ -39,6 +43,16 @@ class RpcClient {
39
43
  this.callFunction = (routingKey, functionName, ...args) => {
40
44
  const stack = new Error('test').stack;
41
45
  return new Promise((resolve, reject) => {
46
+ var _a;
47
+ if (MAX_MSG_SIZE || WARN_MSG_SIZE) {
48
+ const messageSize = (0, object_sizeof_1.default)(args);
49
+ if (messageSize > MAX_MSG_SIZE) {
50
+ throw new Error(`Max RPC message size exceeded: ${messageSize} bytes / ${MAX_MSG_SIZE} bytes`);
51
+ }
52
+ if (messageSize > WARN_MSG_SIZE) {
53
+ (_a = this.logger) === null || _a === void 0 ? void 0 : _a.warn(`Large RPC message size detected: ${messageSize} bytes`);
54
+ }
55
+ }
42
56
  const call = { functionName, arguments: args };
43
57
  const correlationId = (0, crypto_1.randomUUID)();
44
58
  this.openRequests.set(correlationId, { resolve, reject, trace: RpcClient.formatTrace(stack) });
@@ -53,12 +67,13 @@ class RpcClient {
53
67
  if (!amqpConnection) {
54
68
  throw new Error('currently no amqp connection available');
55
69
  }
56
- }
57
- async init() {
58
- this.channel = this.amqpConnection.managedConnection.createChannel({ json: true });
59
- await this.channel.waitForConnect();
60
- await this.channel.assertExchange('rpc_direct_exchange', 'direct', { durable: false });
61
- await this.channel.consume('amq.rabbitmq.reply-to', this.onMessage, { noAck: true });
70
+ this.channel = amqpConnection.createChannel({
71
+ json: true,
72
+ setup: async (channel) => {
73
+ await channel.assertExchange('rpc_direct_exchange', 'direct', { durable: false });
74
+ await channel.consume('amq.rabbitmq.reply-to', this.onMessage, { noAck: true });
75
+ },
76
+ });
62
77
  }
63
78
  close() {
64
79
  return this.channel.close();
package/dist/amqp.d.ts CHANGED
@@ -1,37 +1,19 @@
1
- import type { AmqpConnectionManager, ChannelWrapper } from 'amqp-connection-manager';
2
- import type { Channel, Connection, ConsumeMessage, Options } from 'amqplib';
3
- interface SubscriptionResult {
4
- consumerTag: string;
5
- }
1
+ import { AmqpConnectionManager, ChannelWrapper } from 'amqp-connection-manager';
6
2
  export interface AmqpConnection {
7
- channel: Channel;
8
- connection: Connection;
9
3
  managedChannel: ChannelWrapper;
10
4
  managedConnection: AmqpConnectionManager;
11
- createSubscriber<T>(handler: (msg: T | undefined, rawMessage?: ConsumeMessage) => Promise<any | undefined | void>, msgOptions: MessageHandlerOptions, originalHandlerName: string): Promise<SubscriptionResult>;
12
- publish(exchange: string, routingKey: string, message: any, options?: Options.Publish): void;
13
5
  }
6
+ export interface AmqpConnectionConfig {
7
+ protocol?: string;
8
+ hostname?: string;
9
+ vhost?: string;
10
+ user?: string;
11
+ password?: string;
12
+ port?: number;
13
+ }
14
+ export declare function createAmqpConnection(config: AmqpConnectionConfig): AmqpConnectionManager;
14
15
  export declare class Nack {
15
16
  private readonly _requeue;
16
17
  constructor(_requeue?: boolean);
17
18
  get requeue(): boolean;
18
19
  }
19
- export interface MessageHandlerOptions {
20
- exchange: string;
21
- routingKey: string | string[];
22
- queue?: string;
23
- queueOptions?: QueueOptions;
24
- }
25
- export interface QueueOptions {
26
- durable?: boolean;
27
- exclusive?: boolean;
28
- autoDelete?: boolean;
29
- arguments?: any;
30
- messageTtl?: number;
31
- expires?: number;
32
- deadLetterExchange?: string;
33
- deadLetterRoutingKey?: string;
34
- maxLength?: number;
35
- maxPriority?: number;
36
- }
37
- export {};
package/dist/amqp.js CHANGED
@@ -1,6 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Nack = void 0;
3
+ exports.Nack = exports.createAmqpConnection = void 0;
4
+ const amqp_connection_manager_1 = require("amqp-connection-manager");
5
+ function createAmqpConnection(config) {
6
+ if (!config) {
7
+ return;
8
+ }
9
+ const { protocol = process.env.RABBIT_PROTOCOL || 'amqp', hostname = process.env.RABBIT_HOST || 'localhost', port = +process.env.RABBIT_PORT || 5672, user = process.env.RABBIT_USER || 'guest', password = process.env.RABBIT_PASSWORD || 'guest', vhost = process.env.RABBIT_VHOST || '', } = config;
10
+ const uri = `${protocol}://${user}:${password}@${hostname}:${port}${vhost ? '/' + vhost : ''}`;
11
+ return (0, amqp_connection_manager_1.connect)(uri);
12
+ }
13
+ exports.createAmqpConnection = createAmqpConnection;
4
14
  class Nack {
5
15
  constructor(_requeue = false) {
6
16
  this._requeue = _requeue;
@@ -12,6 +12,7 @@ host = os.getenv("RABBIT_HOST", "localhost")
12
12
  port = os.getenv("RABBIT_PORT", "5672")
13
13
  vhost = os.getenv("RABBIT_VHOST", "")
14
14
  routingKey = os.getenv("RPC_ROUTING_KEY", "rpc")
15
+ max_msg_size = int(os.getenv("MAX_RPC_MSG_SIZE_BYTES", "0"))
15
16
 
16
17
  remote_procedures = {}
17
18
  flow_logs_exchange: AbstractRobustExchange
@@ -64,11 +65,18 @@ async def on_message(exchange: Exchange, message: IncomingMessage):
64
65
 
65
66
 
66
67
  async def send_reply(exchange: Exchange, reply, original_message: Message):
68
+ body = json.dumps(reply).encode("utf-8")
69
+
70
+ if max_msg_size > 0 and len(body) > max_msg_size:
71
+ body = json.dumps(
72
+ {
73
+ "type": "error",
74
+ "message": "Max RPC message size exceeded: " + str(len(body)) + " bytes / " + max_msg_size + " bytes",
75
+ }
76
+ ).encode("utf-8")
77
+
67
78
  await exchange.publish(
68
- Message(
69
- body=json.dumps(reply).encode("utf-8"),
70
- correlation_id=original_message.correlation_id
71
- ),
79
+ Message(body=body, correlation_id=original_message.correlation_id),
72
80
  routing_key=original_message.reply_to,
73
81
  )
74
82
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hahnpro/flow-sdk",
3
- "version": "4.23.7",
3
+ "version": "4.24.0",
4
4
  "description": "SDK for building Flow Modules",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -24,7 +24,7 @@
24
24
  "access": "public"
25
25
  },
26
26
  "dependencies": {
27
- "amqp-connection-manager": "^3.9.0",
27
+ "amqp-connection-manager": "^4.1.11",
28
28
  "amqplib": "^0.10.3",
29
29
  "class-transformer": "0.5.1",
30
30
  "class-validator": "~0.14.0",
@@ -38,15 +38,12 @@
38
38
  "@hahnpro/hpc-api": "3.7.1"
39
39
  },
40
40
  "devDependencies": {
41
- "@golevelup/nestjs-rabbitmq": "^3.5.0",
42
- "@nestjs/common": "^9.3.9",
43
- "@types/amqp-connection-manager": "^2.0.12",
44
41
  "@types/amqplib": "^0.10.1",
45
- "@types/jest": "^29.4.0",
42
+ "@types/jest": "^29.4.2",
46
43
  "@types/lodash": "^4.14.191",
47
- "@types/node": "^18.14.4",
48
- "class-validator-jsonschema": "^4.0.0",
49
- "jest": "^29.4.3",
44
+ "@types/node": "^18.15.3",
45
+ "class-validator-jsonschema": "^5.0.0",
46
+ "jest": "^29.5.0",
50
47
  "typescript": "^4.9.5"
51
48
  },
52
49
  "engines": {