@polygonlabs/servercore 1.5.1-dev.0 → 1.5.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.
Files changed (109) hide show
  1. package/dist/index.d.ts +443 -31
  2. package/dist/index.js +951 -8
  3. package/dist/index.js.map +1 -1
  4. package/package.json +5 -3
  5. package/dist/api/health-check.d.ts +0 -8
  6. package/dist/api/health-check.js +0 -89
  7. package/dist/api/health-check.js.map +0 -1
  8. package/dist/api/index.d.ts +0 -9
  9. package/dist/api/index.js +0 -4
  10. package/dist/api/index.js.map +0 -1
  11. package/dist/api/response_handler.d.ts +0 -15
  12. package/dist/api/response_handler.js +0 -23
  13. package/dist/api/response_handler.js.map +0 -1
  14. package/dist/api/zod_utils.d.ts +0 -7
  15. package/dist/api/zod_utils.js +0 -42
  16. package/dist/api/zod_utils.js.map +0 -1
  17. package/dist/constants/error_codes.d.ts +0 -29
  18. package/dist/constants/error_codes.js +0 -34
  19. package/dist/constants/error_codes.js.map +0 -1
  20. package/dist/constants/http_success_codes.d.ts +0 -5
  21. package/dist/constants/http_success_codes.js +0 -7
  22. package/dist/constants/http_success_codes.js.map +0 -1
  23. package/dist/constants/index.d.ts +0 -2
  24. package/dist/constants/index.js +0 -3
  25. package/dist/constants/index.js.map +0 -1
  26. package/dist/consumers/abstract_cron_event_consumer.d.ts +0 -23
  27. package/dist/consumers/abstract_cron_event_consumer.js +0 -30
  28. package/dist/consumers/abstract_cron_event_consumer.js.map +0 -1
  29. package/dist/consumers/abstract_event_consumer.d.ts +0 -34
  30. package/dist/consumers/abstract_event_consumer.js +0 -41
  31. package/dist/consumers/abstract_event_consumer.js.map +0 -1
  32. package/dist/consumers/event_consumer.d.ts +0 -26
  33. package/dist/consumers/event_consumer.js +0 -141
  34. package/dist/consumers/event_consumer.js.map +0 -1
  35. package/dist/consumers/index.d.ts +0 -11
  36. package/dist/consumers/index.js +0 -4
  37. package/dist/consumers/index.js.map +0 -1
  38. package/dist/consumers/rest_api_consumer.d.ts +0 -30
  39. package/dist/consumers/rest_api_consumer.js +0 -131
  40. package/dist/consumers/rest_api_consumer.js.map +0 -1
  41. package/dist/errors/api_errors.d.ts +0 -32
  42. package/dist/errors/api_errors.js +0 -96
  43. package/dist/errors/api_errors.js.map +0 -1
  44. package/dist/errors/base_error.d.ts +0 -22
  45. package/dist/errors/base_error.js +0 -30
  46. package/dist/errors/base_error.js.map +0 -1
  47. package/dist/errors/consumer_errors.d.ts +0 -13
  48. package/dist/errors/consumer_errors.js +0 -18
  49. package/dist/errors/consumer_errors.js.map +0 -1
  50. package/dist/errors/database_errors.d.ts +0 -13
  51. package/dist/errors/database_errors.js +0 -18
  52. package/dist/errors/database_errors.js.map +0 -1
  53. package/dist/errors/external_dependency_error.d.ts +0 -15
  54. package/dist/errors/external_dependency_error.js +0 -29
  55. package/dist/errors/external_dependency_error.js.map +0 -1
  56. package/dist/errors/index.d.ts +0 -5
  57. package/dist/errors/index.js +0 -6
  58. package/dist/errors/index.js.map +0 -1
  59. package/dist/logger/index.d.ts +0 -3
  60. package/dist/logger/index.js +0 -2
  61. package/dist/logger/index.js.map +0 -1
  62. package/dist/logger/logger.d.ts +0 -54
  63. package/dist/logger/logger.js +0 -124
  64. package/dist/logger/logger.js.map +0 -1
  65. package/dist/storage/cache_interface.d.ts +0 -13
  66. package/dist/storage/cache_interface.js +0 -1
  67. package/dist/storage/cache_interface.js.map +0 -1
  68. package/dist/storage/db_interface.d.ts +0 -18
  69. package/dist/storage/db_interface.js +0 -1
  70. package/dist/storage/db_interface.js.map +0 -1
  71. package/dist/storage/index.d.ts +0 -5
  72. package/dist/storage/index.js +0 -4
  73. package/dist/storage/index.js.map +0 -1
  74. package/dist/storage/queue_interface.d.ts +0 -11
  75. package/dist/storage/queue_interface.js +0 -1
  76. package/dist/storage/queue_interface.js.map +0 -1
  77. package/dist/types/database.d.ts +0 -62
  78. package/dist/types/database.js +0 -1
  79. package/dist/types/database.js.map +0 -1
  80. package/dist/types/event_consumer_config.d.ts +0 -19
  81. package/dist/types/event_consumer_config.js +0 -1
  82. package/dist/types/event_consumer_config.js.map +0 -1
  83. package/dist/types/index.d.ts +0 -9
  84. package/dist/types/index.js +0 -8
  85. package/dist/types/index.js.map +0 -1
  86. package/dist/types/logger_config.d.ts +0 -18
  87. package/dist/types/logger_config.js +0 -2
  88. package/dist/types/logger_config.js.map +0 -1
  89. package/dist/types/observer.d.ts +0 -8
  90. package/dist/types/observer.js +0 -1
  91. package/dist/types/observer.js.map +0 -1
  92. package/dist/types/queue_job_opts.d.ts +0 -5
  93. package/dist/types/queue_job_opts.js +0 -1
  94. package/dist/types/queue_job_opts.js.map +0 -1
  95. package/dist/types/response_context.d.ts +0 -6
  96. package/dist/types/response_context.js +0 -1
  97. package/dist/types/response_context.js.map +0 -1
  98. package/dist/types/rest_api_consumer_config.d.ts +0 -17
  99. package/dist/types/rest_api_consumer_config.js +0 -1
  100. package/dist/types/rest_api_consumer_config.js.map +0 -1
  101. package/dist/utils/decoder.d.ts +0 -10
  102. package/dist/utils/decoder.js +0 -12
  103. package/dist/utils/decoder.js.map +0 -1
  104. package/dist/utils/index.d.ts +0 -2
  105. package/dist/utils/index.js +0 -3
  106. package/dist/utils/index.js.map +0 -1
  107. package/dist/utils/ulid.d.ts +0 -3
  108. package/dist/utils/ulid.js +0 -24
  109. package/dist/utils/ulid.js.map +0 -1
package/dist/index.js CHANGED
@@ -1,9 +1,952 @@
1
- export * from "./api";
2
- export * from "./logger";
3
- export * from "./constants";
4
- export * from "./types";
5
- export * from "./errors";
6
- export * from "./storage";
7
- export * from "./consumers";
8
- export * from "./utils";
1
+ // src/constants/error_codes.ts
2
+ var errorCodes = {
3
+ // Base error identifier
4
+ base: { BASE_ERROR: 100 },
5
+ // Consumer related error codes
6
+ consumer: {
7
+ UNKNOWN_CONSUMER_ERR: 1e3
8
+ },
9
+ // Datastore related error codes
10
+ datastore: {
11
+ UNKNOWN_DATASTORE_ERR: 2e3,
12
+ DATASTORE_AUTH_ERR: 2001,
13
+ DATASTORE_READ_ERROR: 2002,
14
+ DATASTORE_WRITE_ERROR: 2003
15
+ },
16
+ // External dependencies errors codes
17
+ external: {
18
+ UNKNOWN_EXTERNAL_DEPENDENCY_ERROR: 3e3
19
+ },
20
+ // API related error codes
21
+ api: {
22
+ BAD_REQUEST: 400,
23
+ UNAUTHORIZED: 401,
24
+ FORBIDDEN: 403,
25
+ NOT_FOUND: 404,
26
+ TOO_MANY_REQUESTS: 429,
27
+ INTERNAL_SERVER_ERROR: 500,
28
+ GATEWAY_ERROR: 502,
29
+ TIMEOUT_ERROR: 504
30
+ }
31
+ };
32
+
33
+ // src/constants/http_success_codes.ts
34
+ var httpResponseCodes = {
35
+ OK_RESPONSE: 200
36
+ };
37
+
38
+ // src/errors/base_error.ts
39
+ var BaseError = class extends Error {
40
+ name;
41
+ code;
42
+ isFatal;
43
+ origin;
44
+ context;
45
+ /**
46
+ * @param name {string} - The error name
47
+ * @param code {number} - The error code
48
+ * @param isFatal {boolean} - Flag to know if it is a fatal error
49
+ * @param message {string} - The actual error message
50
+ * @param origin {string} - The point this error originated
51
+ * @param context Record<string, any> - The stack trace
52
+ */
53
+ constructor(name, code, message = "Unknown error", isFatal, origin, context) {
54
+ super(message);
55
+ this.name = name;
56
+ this.code = code;
57
+ this.isFatal = isFatal ?? true;
58
+ this.origin = origin ?? "base_error";
59
+ this.context = context ?? {};
60
+ Object.setPrototypeOf(this, new.target.prototype);
61
+ }
62
+ identifier = errorCodes.base.BASE_ERROR;
63
+ };
64
+
65
+ // src/errors/api_errors.ts
66
+ var ApiError = class extends BaseError {
67
+ constructor(message, {
68
+ name = "INTERNAL_SERVER_ERROR",
69
+ code = errorCodes.api.INTERNAL_SERVER_ERROR,
70
+ isFatal = false,
71
+ origin = "api_errors",
72
+ context = {}
73
+ } = {}) {
74
+ super(name, code, message, isFatal, origin, context);
75
+ Error.captureStackTrace(this, this.constructor);
76
+ }
77
+ };
78
+ var UnauthorizedError = class extends ApiError {
79
+ constructor(message = "Invalid auth credentials", context = {}, origin = "api_errors") {
80
+ super(message, {
81
+ name: "UNAUTHORIZED",
82
+ code: errorCodes.api.UNAUTHORIZED,
83
+ isFatal: false,
84
+ origin,
85
+ context
86
+ });
87
+ }
88
+ };
89
+ var ForbiddenError = class extends ApiError {
90
+ constructor(message = "You do not have permission to perform this action", context = {}, origin = "api_errors") {
91
+ super(message, {
92
+ name: "FORBIDDEN",
93
+ code: errorCodes.api.FORBIDDEN,
94
+ isFatal: false,
95
+ origin,
96
+ context
97
+ });
98
+ }
99
+ };
100
+ var BadRequestError = class extends ApiError {
101
+ validationErrors;
102
+ constructor(message = "Malformed or invalid request", validationErrors = {}, context = {}, origin = "api_errors") {
103
+ super(message, {
104
+ name: "BAD_REQUEST",
105
+ code: errorCodes.api.BAD_REQUEST,
106
+ isFatal: false,
107
+ origin,
108
+ context
109
+ });
110
+ this.validationErrors = validationErrors;
111
+ }
112
+ };
113
+ var NotFoundError = class extends ApiError {
114
+ constructor(message, entity = "Path", identifier, context = {}, origin = "api_errors") {
115
+ if (!message) {
116
+ message = identifier ? `${entity} with identifier ${identifier} not found` : `${entity} not found`;
117
+ }
118
+ super(message, {
119
+ name: "NOT_FOUND",
120
+ code: errorCodes.api.NOT_FOUND,
121
+ isFatal: false,
122
+ origin,
123
+ context: { entity, identifier, ...context }
124
+ });
125
+ }
126
+ };
127
+ var RateLimitError = class extends ApiError {
128
+ constructor(message = "Rate limit exceeded", context = {}, origin = "api_errors") {
129
+ super(message, {
130
+ name: "RATE_LIMIT",
131
+ code: errorCodes.api.TOO_MANY_REQUESTS,
132
+ isFatal: false,
133
+ origin,
134
+ context
135
+ });
136
+ }
137
+ };
138
+ var TimeoutError = class extends ApiError {
139
+ constructor(operation, timeoutMs, context = {}, origin = "api_errors") {
140
+ super(`Operation '${operation}' timed out after ${timeoutMs}ms`, {
141
+ name: "TIMEOUT",
142
+ code: errorCodes.api.TIMEOUT_ERROR,
143
+ // Gateway Timeout
144
+ isFatal: false,
145
+ origin,
146
+ context: { operation, timeoutMs, ...context }
147
+ });
148
+ }
149
+ };
150
+
151
+ // src/errors/database_errors.ts
152
+ var DatabaseError = class extends BaseError {
153
+ constructor(message, originalError, {
154
+ name = "DATABASE_ERROR",
155
+ code = errorCodes.consumer.UNKNOWN_CONSUMER_ERR,
156
+ isFatal = true,
157
+ origin = "databse_errors",
158
+ context = {}
159
+ } = {}) {
160
+ super(name, code, message, isFatal, origin, context);
161
+ Error.captureStackTrace(this, this.constructor);
162
+ }
163
+ };
164
+
165
+ // src/errors/external_dependency_error.ts
166
+ var ExternalDependencyError = class extends BaseError {
167
+ apiName;
168
+ externalCode;
169
+ rawError;
170
+ constructor(apiName, message, {
171
+ externalCode,
172
+ rawError,
173
+ context = {},
174
+ origin = "external_dependency_error"
175
+ } = {}) {
176
+ super(
177
+ "EXTERNAL_DEPENDENCY_ERROR",
178
+ errorCodes.external.UNKNOWN_EXTERNAL_DEPENDENCY_ERROR,
179
+ `${apiName} API error: ${message}`,
180
+ false,
181
+ origin,
182
+ { apiName, externalCode, ...context }
183
+ );
184
+ this.apiName = apiName;
185
+ this.externalCode = externalCode;
186
+ this.rawError = rawError;
187
+ }
188
+ };
189
+
190
+ // src/errors/consumer_errors.ts
191
+ var ConsumerError = class extends BaseError {
192
+ constructor(message, {
193
+ name = "CONSUMER_ERROR",
194
+ code = errorCodes.consumer.UNKNOWN_CONSUMER_ERR,
195
+ isFatal = true,
196
+ origin = "consumer_errors",
197
+ context = {}
198
+ } = {}) {
199
+ super(name, code, message, isFatal, origin, context);
200
+ Error.captureStackTrace(this, this.constructor);
201
+ }
202
+ };
203
+
204
+ // src/api/response_handler.ts
205
+ var handleResponse = (c, data, pagination) => {
206
+ return c.status(httpResponseCodes.OK_RESPONSE).json({
207
+ status: "success",
208
+ data,
209
+ pagination
210
+ });
211
+ };
212
+ var handleError = (c, error) => {
213
+ return c.status(error.code ?? errorCodes.api.INTERNAL_SERVER_ERROR).json({
214
+ status: "error",
215
+ message: error.message,
216
+ name: error.name,
217
+ code: error.code,
218
+ details: error.context
219
+ });
220
+ };
221
+
222
+ // src/api/zod_utils.ts
223
+ import "zod";
224
+ var validateBody = (schema, data) => {
225
+ const result = schema.safeParse(data);
226
+ if (!result.success) {
227
+ throw new Error(
228
+ `Invalid body: ${JSON.stringify(result.error.format(), null, 2)}`
229
+ );
230
+ }
231
+ return result.data;
232
+ };
233
+ var validateQuery = (schema, query) => {
234
+ const result = schema.safeParse(query);
235
+ if (!result.success) {
236
+ throw new Error(
237
+ `Invalid query params: ${JSON.stringify(
238
+ result.error.format(),
239
+ null,
240
+ 2
241
+ )}`
242
+ );
243
+ }
244
+ return result.data;
245
+ };
246
+ var validateParams = (schema, params) => {
247
+ const result = schema.safeParse(params);
248
+ if (!result.success) {
249
+ throw new Error(
250
+ `Invalid path params: ${JSON.stringify(
251
+ result.error.format(),
252
+ null,
253
+ 2
254
+ )}`
255
+ );
256
+ }
257
+ return result.data;
258
+ };
259
+
260
+ // src/api/health-check.ts
261
+ var HealthCheck = class {
262
+ constructor(urls) {
263
+ this.urls = urls;
264
+ }
265
+ async checkHealth() {
266
+ try {
267
+ const results = await Promise.all(
268
+ this.urls.map(async (url) => {
269
+ const response = await fetch(`${url}`);
270
+ if (!response.ok) {
271
+ throw new Error(`Health check failed for ${url}`);
272
+ }
273
+ return response;
274
+ })
275
+ );
276
+ if (results.every((res) => res.ok)) {
277
+ return "ok";
278
+ }
279
+ throw new Error("One or more URLs failed the health check.");
280
+ } catch (error) {
281
+ const failedUrlChecks = await Promise.all(
282
+ this.urls.map(async (url) => {
283
+ try {
284
+ const response = await fetch(`${url}`);
285
+ return { url, failed: !response.ok };
286
+ } catch {
287
+ return { url, failed: true };
288
+ }
289
+ })
290
+ );
291
+ const failedUrls = failedUrlChecks.filter((check) => check.failed).map((check) => check.url);
292
+ throw new ApiError(
293
+ `Health check failed for the following URLs: ${failedUrls.join(
294
+ ", "
295
+ )}. Original error: ${error.message}`
296
+ );
297
+ }
298
+ }
299
+ };
300
+ function createResponseContext(c) {
301
+ c.status = c.status || 200;
302
+ return {
303
+ status: (statusCode) => {
304
+ c.status = statusCode;
305
+ return createResponseContext(c);
306
+ },
307
+ json: (body) => {
308
+ return new Response(JSON.stringify(body), {
309
+ status: c.status,
310
+ headers: { "Content-Type": "application/json" }
311
+ });
312
+ }
313
+ };
314
+ }
315
+ function setupHealthCheckServer(urls, serverPort, func) {
316
+ const healthCheck = new HealthCheck(urls);
317
+ Bun.serve({
318
+ port: serverPort,
319
+ routes: {
320
+ "/health-check": {
321
+ GET: async (c) => {
322
+ try {
323
+ if (func) {
324
+ await func();
325
+ } else {
326
+ await healthCheck.checkHealth();
327
+ }
328
+ return handleResponse(createResponseContext(c), "ok");
329
+ } catch (error) {
330
+ return handleError(
331
+ createResponseContext(c),
332
+ error
333
+ );
334
+ }
335
+ }
336
+ }
337
+ }
338
+ });
339
+ console.log(
340
+ `Health check server running on http://localhost:${serverPort}/health-check`
341
+ );
342
+ }
343
+
344
+ // src/logger/logger.ts
345
+ import winston from "winston";
346
+
347
+ // src/logger/sentry_transport.ts
348
+ import * as Sentry from "@sentry/node";
349
+ import Transport from "winston-transport";
350
+ import { LEVEL } from "triple-beam";
351
+ var DEFAULT_LEVELS_MAP = {
352
+ silly: "debug" /* Debug */,
353
+ verbose: "debug" /* Debug */,
354
+ info: "info" /* Info */,
355
+ debug: "debug" /* Debug */,
356
+ warn: "warning" /* Warning */,
357
+ error: "error" /* Error */
358
+ };
359
+ var ExtendedError = class extends Error {
360
+ constructor(info) {
361
+ super(info.message);
362
+ this.name = info.name || "Error";
363
+ if (info.stack && typeof info.stack === "string") {
364
+ this.stack = info.stack;
365
+ }
366
+ }
367
+ };
368
+ var SentryTransport = class _SentryTransport extends Transport {
369
+ silent = false;
370
+ levelsMap = {};
371
+ constructor(opts) {
372
+ super(opts);
373
+ this.levelsMap = this.setLevelsMap(opts && opts.levelsMap);
374
+ this.silent = opts && opts.silent || false;
375
+ if (!opts || !opts.skipSentryInit) {
376
+ Sentry.init(_SentryTransport.withDefaults(opts && opts.sentry || {}));
377
+ }
378
+ }
379
+ log(info, callback) {
380
+ setImmediate(() => {
381
+ this.emit("logged", info);
382
+ });
383
+ if (this.silent) return callback();
384
+ const { message, tags, user, ...meta } = info;
385
+ const winstonLevel = info[LEVEL];
386
+ const sentryLevel = this.levelsMap[winstonLevel];
387
+ const scope = Sentry.getCurrentScope();
388
+ scope.clear();
389
+ if (tags !== void 0 && _SentryTransport.isObject(tags)) {
390
+ scope.setTags(tags);
391
+ }
392
+ scope.setExtras(meta);
393
+ if (user !== void 0 && _SentryTransport.isObject(user)) {
394
+ scope.setUser(user);
395
+ }
396
+ if (_SentryTransport.shouldLogException(sentryLevel)) {
397
+ const error = Object.values(info).find((value) => value instanceof Error) ?? new ExtendedError(info);
398
+ Sentry.captureException(error, { tags, level: sentryLevel });
399
+ return callback();
400
+ }
401
+ Sentry.captureMessage(message, sentryLevel);
402
+ return callback();
403
+ }
404
+ end(...args) {
405
+ Sentry.flush().then(() => {
406
+ super.end(...args);
407
+ });
408
+ return this;
409
+ }
410
+ get sentry() {
411
+ return Sentry;
412
+ }
413
+ setLevelsMap = (options) => {
414
+ if (!options) return DEFAULT_LEVELS_MAP;
415
+ const customLevelsMap = Object.keys(options).reduce(
416
+ (acc, winstonSeverity) => {
417
+ acc[winstonSeverity] = options[winstonSeverity];
418
+ return acc;
419
+ },
420
+ {}
421
+ );
422
+ return { ...DEFAULT_LEVELS_MAP, ...customLevelsMap };
423
+ };
424
+ static withDefaults(options) {
425
+ return {
426
+ ...options,
427
+ dsn: options && options.dsn || process.env.SENTRY_DSN || "",
428
+ serverName: options && options.serverName || "winston-transport-sentry-node",
429
+ environment: options && options.environment || process.env.SENTRY_ENVIRONMENT || process.env.NODE_ENV || "production",
430
+ debug: options && options.debug || !!process.env.SENTRY_DEBUG || false,
431
+ sampleRate: options && options.sampleRate || 1,
432
+ maxBreadcrumbs: options && options.maxBreadcrumbs || 100
433
+ };
434
+ }
435
+ static isObject(obj) {
436
+ const type = typeof obj;
437
+ return type === "function" || type === "object" && !!obj;
438
+ }
439
+ static shouldLogException(level) {
440
+ return level === "fatal" /* Fatal */ || level === "error" /* Error */;
441
+ }
442
+ };
443
+
444
+ // src/logger/logger.ts
445
+ var Logger = class {
446
+ static logger;
447
+ /**
448
+ * @static
449
+ * Create method must first be called before using the logger. It creates a singleton, which will then
450
+ * be referred to throughout the application.
451
+ */
452
+ static create(config) {
453
+ if (!this.logger) {
454
+ this.logger = winston.createLogger({
455
+ format: winston.format.json(),
456
+ transports: [
457
+ new winston.transports.Console({
458
+ level: config.console?.level || "info"
459
+ }),
460
+ new SentryTransport({
461
+ sentry: {
462
+ dsn: config.sentry?.dsn
463
+ },
464
+ level: config.sentry?.level || "error"
465
+ })
466
+ ],
467
+ ...config.winston
468
+ });
469
+ }
470
+ }
471
+ static ensureInitialized() {
472
+ if (!this.logger) {
473
+ throw new BaseError(
474
+ "LOGGER_ERROR",
475
+ errorCodes.base.BASE_ERROR,
476
+ "Logger not initialized. Please call Logger.create() first.",
477
+ true,
478
+ "Logger",
479
+ void 0
480
+ );
481
+ }
482
+ }
483
+ /**
484
+ * @static
485
+ * Method to log for level - "info", this should not be called if it has been custom levels are
486
+ * set which does not include "info"
487
+ *
488
+ * @param {string|object} message - String or object to log.
489
+ */
490
+ static info(message) {
491
+ this.ensureInitialized();
492
+ if (typeof message === "string") {
493
+ this.logger.info(message);
494
+ } else {
495
+ this.logger.info(JSON.stringify(message));
496
+ }
497
+ }
498
+ /**
499
+ * @static
500
+ * Method to log for level - "debug", this should not be called if it has been custom levels are
501
+ * set which does not include "debug"
502
+ *
503
+ * @param {string|object} message - String or object to log.
504
+ */
505
+ static debug(message) {
506
+ this.ensureInitialized();
507
+ if (typeof message === "string") {
508
+ this.logger?.debug(message);
509
+ } else {
510
+ this.logger?.debug(JSON.stringify(message));
511
+ }
512
+ }
513
+ /**
514
+ * @static
515
+ * Method to log for level - "error", this should not be called if it has been custom levels are
516
+ * set which does not include "error"
517
+ *
518
+ * @param {string|object} error - String or object to log.
519
+ */
520
+ static error(error) {
521
+ this.ensureInitialized();
522
+ if (typeof error === "string") {
523
+ this.logger?.error(error);
524
+ } else {
525
+ this.logger?.error(
526
+ `${error.message ? `${error.message} : ` : ""}${JSON.stringify(error)}`
527
+ );
528
+ }
529
+ }
530
+ /**
531
+ * @static
532
+ * Method to log for level - "warn", this should not be called if it has been custom levels are
533
+ * set which does not include "warn"
534
+ *
535
+ * @param {string|object} message - String or object to log.
536
+ */
537
+ static warn(message) {
538
+ this.ensureInitialized();
539
+ if (typeof message === "string") {
540
+ this.logger?.warn(message);
541
+ } else {
542
+ this.logger?.warn(JSON.stringify(message));
543
+ }
544
+ }
545
+ /**
546
+ * @static
547
+ * Method to log for any level, which should be used to log all custom levels that may be added.
548
+ *
549
+ * @param {string|object} message - String or object to log.
550
+ */
551
+ static log(level, message) {
552
+ this.ensureInitialized();
553
+ if (typeof message === "string") {
554
+ this.logger?.log(level, message);
555
+ } else {
556
+ this.logger?.log(level, JSON.stringify(message));
557
+ }
558
+ }
559
+ };
560
+
561
+ // src/types/logger_config.ts
562
+ import "winston";
563
+
564
+ // src/consumers/event_consumer.ts
565
+ import { createPublicClient, http } from "viem";
566
+
567
+ // src/consumers/abstract_event_consumer.ts
568
+ import { EventEmitter } from "events";
569
+ var AbstractEventConsumer = class extends EventEmitter {
570
+ constructor() {
571
+ super();
572
+ }
573
+ /**
574
+ * @public
575
+ *
576
+ * Method to register listener for events. The Abstract Event Consumer emits fatalError
577
+ * event.
578
+ *
579
+ * @param {"fatalError"} eventName - Event name to register listener for.
580
+ * @param listener - Listener to be called when emitting the event.
581
+ *
582
+ * @returns {this} - Returns an instance of the class.
583
+ */
584
+ on(eventName, listener) {
585
+ return super.on(eventName, listener);
586
+ }
587
+ /**
588
+ * @public
589
+ *
590
+ * Method to register listener for events that will be called only once. The Abstract Event Consumer emits fatalError
591
+ * event.
592
+ *
593
+ * @param {"fatalError"} eventName - Event name to register listener for.
594
+ * @param listener - Listener to be called when emitting the event.
595
+ *
596
+ * @returns {this} - Returns an instance of the class.
597
+ */
598
+ once(eventName, listener) {
599
+ return super.on(eventName, listener);
600
+ }
601
+ onFatalError(error) {
602
+ this.emit("fatalError", error);
603
+ }
604
+ };
605
+
606
+ // src/consumers/event_consumer.ts
607
+ var EventConsumer = class extends AbstractEventConsumer {
608
+ constructor(config) {
609
+ super();
610
+ this.config = config;
611
+ this.client = createPublicClient({
612
+ chain: {
613
+ id: config.chainId,
614
+ name: "custom",
615
+ rpcUrls: { default: { http: [config.rpcUrl] } },
616
+ nativeCurrency: config.nativeCurrency
617
+ },
618
+ transport: http(config.rpcUrl)
619
+ });
620
+ }
621
+ client = null;
622
+ observer = null;
623
+ isBackfillingInProcess = false;
624
+ unWatchFunction;
625
+ async _backfillEvents(fromBlock, toBlock, step) {
626
+ Logger.info({
627
+ location: "event_consumer",
628
+ functon: "_backfillEvents",
629
+ status: `[Backfill] Starting backfill from block ${fromBlock.toString()} to ${toBlock.toString()}`
630
+ });
631
+ for (let start = fromBlock; start <= toBlock; start += step) {
632
+ const end = start + step - 1n > toBlock ? toBlock : start + step - 1n;
633
+ Logger.debug({
634
+ location: "event_consumer",
635
+ functon: "_backfillEvents",
636
+ status: `[Backfill] Fetching logs from block ${start.toString()} to ${end.toString()}`
637
+ });
638
+ const getLogsConfig = {
639
+ address: this.config.contractAddress,
640
+ fromBlock: start,
641
+ toBlock: end
642
+ };
643
+ if (this.config.events.length > 1) {
644
+ getLogsConfig.events = this.config.events;
645
+ } else {
646
+ getLogsConfig.event = this.config.events[0];
647
+ }
648
+ const logs = await this.client?.getLogs(getLogsConfig);
649
+ Logger.debug({
650
+ location: "event_consumer",
651
+ functon: "_backfillEvents",
652
+ status: `[Backfill] ${logs?.length} logs from block ${start.toString()} to ${end.toString()}`
653
+ });
654
+ await this.observer?.next(logs);
655
+ }
656
+ }
657
+ async start(observer) {
658
+ try {
659
+ this.isBackfillingInProcess = true;
660
+ this.observer = observer;
661
+ const latestBlock = await this.client?.getBlockNumber() ?? BigInt(0);
662
+ Logger.debug({
663
+ location: "event_consumer",
664
+ functon: "start",
665
+ data: {
666
+ latestBlock: latestBlock.toString(),
667
+ startBlock: this.config.startBlock.toString(),
668
+ pollBatchSize: this.config.pollBatchSize.toString()
669
+ }
670
+ });
671
+ if (latestBlock - this.config.startBlock > BigInt(0)) {
672
+ await this._backfillEvents(
673
+ this.config.startBlock,
674
+ latestBlock,
675
+ this.config.pollBatchSize
676
+ );
677
+ }
678
+ this.isBackfillingInProcess = false;
679
+ Logger.debug({
680
+ location: "event_consumer",
681
+ functon: "start",
682
+ status: `Starting event subscription`
683
+ });
684
+ this.unWatchFunction = this.client?.watchEvent({
685
+ address: this.config.contractAddress,
686
+ events: this.config.events,
687
+ fromBlock: latestBlock,
688
+ onLogs: (logs) => {
689
+ const transformedLogs = logs;
690
+ this.observer?.next(transformedLogs);
691
+ },
692
+ onError: (error) => {
693
+ this.onDisconnect();
694
+ this.onFatalError(
695
+ new ConsumerError(error.name, {
696
+ name: "CONSUMER_ERROR",
697
+ code: 1001,
698
+ isFatal: true,
699
+ origin: "EventConsumer",
700
+ context: { eventName: this.config.events }
701
+ })
702
+ );
703
+ }
704
+ });
705
+ Logger.debug({
706
+ location: "event_consumer",
707
+ functon: "start",
708
+ status: `Subscribed to event`
709
+ });
710
+ } catch (error) {
711
+ this.onDisconnect();
712
+ this.onFatalError(
713
+ new ExternalDependencyError(
714
+ this.config.rpcUrl,
715
+ "Failed to start the event consumer",
716
+ {
717
+ externalCode: errorCodes.external.UNKNOWN_EXTERNAL_DEPENDENCY_ERROR,
718
+ rawError: error,
719
+ origin: "EventConsumer",
720
+ context: { eventName: this.config.events }
721
+ }
722
+ )
723
+ );
724
+ }
725
+ }
726
+ /**
727
+ * Private method which updates the connection status of consumer to disconnected, and removes all listeners.
728
+ *
729
+ * @returns {void}
730
+ */
731
+ onDisconnect() {
732
+ this.removeAllListeners();
733
+ if (this.unWatchFunction) {
734
+ this.unWatchFunction();
735
+ }
736
+ this.observer?.closed();
737
+ }
738
+ };
739
+
740
+ // src/consumers/abstract_cron_event_consumer.ts
741
+ import { Cron } from "croner";
742
+ var AbstractCronEventConsumer = class extends AbstractEventConsumer {
743
+ cronJob = null;
744
+ /**
745
+ * Start the cron job with the given cron expression.
746
+ * @param cronExpr Cron expression string
747
+ */
748
+ startCron(cronExpr) {
749
+ if (this.cronJob) {
750
+ this.cronJob.stop();
751
+ }
752
+ this.cronJob = new Cron(cronExpr, { protect: true }, async () => {
753
+ await this.onTick();
754
+ });
755
+ }
756
+ /**
757
+ * Stop the cron job.
758
+ */
759
+ stopCron() {
760
+ if (this.cronJob) {
761
+ this.cronJob.stop();
762
+ this.cronJob = null;
763
+ }
764
+ }
765
+ };
766
+
767
+ // src/consumers/rest_api_consumer.ts
768
+ var RestAPIConsumer = class extends AbstractCronEventConsumer {
769
+ constructor(config) {
770
+ super();
771
+ this.config = config;
772
+ this.highestValueSeen = this.config.startCount.value;
773
+ }
774
+ observer = null;
775
+ currentPage = 0;
776
+ highestValueSeen = 0;
777
+ async start(observer) {
778
+ this.observer = observer;
779
+ this.currentPage = 1;
780
+ if (this.config.cronExpr) {
781
+ this.startCron(this.config.cronExpr);
782
+ } else {
783
+ await this.onTick();
784
+ }
785
+ }
786
+ stop() {
787
+ this.stopCron();
788
+ }
789
+ async onTick() {
790
+ this.currentPage = 1;
791
+ let shouldFetchNextPage = true;
792
+ while (shouldFetchNextPage) {
793
+ shouldFetchNextPage = await this.fetchPage();
794
+ }
795
+ this.config.startCount.value = this.highestValueSeen;
796
+ this.observer?.summary({
797
+ [`${this.config.startCount.key}`]: this.config.startCount.value
798
+ });
799
+ }
800
+ async fetchPage() {
801
+ try {
802
+ const paginationParam = this.config.paginationParam || "page";
803
+ const params = {
804
+ ...this.config.params || {},
805
+ [paginationParam]: this.currentPage.toString()
806
+ };
807
+ let url = this.config.apiUrl.toString();
808
+ if (Object.keys(params).length > 0) {
809
+ const queryString = new URLSearchParams(params).toString();
810
+ url += (url.includes("?") ? "&" : "?") + queryString;
811
+ }
812
+ const response = await fetch(url, {
813
+ method: this.config.method || "GET",
814
+ headers: this.config.headers,
815
+ body: this.config.body ? JSON.stringify(this.config.body) : void 0
816
+ });
817
+ if (!response.ok) {
818
+ throw new ConsumerError(
819
+ `Failed to fetch from API: ${response.statusText}`,
820
+ {
821
+ code: errorCodes.external.UNKNOWN_EXTERNAL_DEPENDENCY_ERROR,
822
+ isFatal: true,
823
+ origin: "rest_api_consumer"
824
+ }
825
+ );
826
+ }
827
+ let data = await response.json();
828
+ if (this.config.resultPath) {
829
+ data = this.getValueByPath(data, this.config.resultPath);
830
+ }
831
+ const countKey = this.config.startCount.key;
832
+ let currentValue = void 0;
833
+ if (Array.isArray(data)) {
834
+ if (data.length === 0) {
835
+ return false;
836
+ }
837
+ for (const elem of data) {
838
+ currentValue = this.processDataForCurrentCountValue(
839
+ elem,
840
+ countKey
841
+ );
842
+ }
843
+ } else {
844
+ currentValue = this.processDataForCurrentCountValue(
845
+ data,
846
+ countKey
847
+ );
848
+ }
849
+ this.observer?.next(data);
850
+ this.currentPage++;
851
+ if (currentValue === void 0) {
852
+ return false;
853
+ }
854
+ return currentValue >= this.config.startCount.value;
855
+ } catch (error) {
856
+ this.onFatalError(error);
857
+ return false;
858
+ }
859
+ }
860
+ // Utility to get nested values using dot notation (e.g., "response.data.count")
861
+ getValueByPath(obj, path) {
862
+ return path.split(".").reduce((prev, curr) => {
863
+ return prev && prev[curr] !== void 0 ? prev[curr] : void 0;
864
+ }, obj);
865
+ }
866
+ processDataForCurrentCountValue(item, countKey) {
867
+ const value = this.getValueByPath(item, countKey);
868
+ if (value === void 0) {
869
+ throw new ConsumerError(
870
+ `Count key '${countKey}' not found in API response`,
871
+ {
872
+ code: errorCodes.external.UNKNOWN_EXTERNAL_DEPENDENCY_ERROR,
873
+ isFatal: false,
874
+ origin: "rest_api_consumer"
875
+ }
876
+ );
877
+ }
878
+ this.highestValueSeen = Math.max(this.highestValueSeen, value);
879
+ return value;
880
+ }
881
+ /**
882
+ * Updates the start count value to a specific value.
883
+ * This allows reprocessing data from a specified point.
884
+ *
885
+ * @param value The value to set as the new start count
886
+ */
887
+ setStartValue(value) {
888
+ this.config.startCount.value = value;
889
+ this.highestValueSeen = value;
890
+ }
891
+ };
892
+
893
+ // src/utils/decoder.ts
894
+ import { decodeEventLog, parseAbi, decodeAbiParameters } from "viem";
895
+ var parseEventLog = (eventSignature, data, topics) => {
896
+ return decodeEventLog({ abi: parseAbi(eventSignature), data, topics });
897
+ };
898
+ var decodeAbiParams = (params, data) => {
899
+ return decodeAbiParameters(params, data);
900
+ };
901
+
902
+ // src/utils/ulid.ts
903
+ import { monotonicFactory, ulid } from "ulid";
904
+ function deterministicPRNG(seed) {
905
+ let t = seed;
906
+ return () => {
907
+ t += 1831565813;
908
+ t = Math.imul(t ^ t >>> 15, t | 1);
909
+ t ^= t + Math.imul(t ^ t >>> 7, t | 61);
910
+ return ((t ^ t >>> 14) >>> 0) / 4294967296;
911
+ };
912
+ }
913
+ function generateDeterministicULID(dataToEncode, timeSeed, separator) {
914
+ if (!Array.isArray(dataToEncode) || !dataToEncode.every((item) => typeof item === "string")) {
915
+ throw new Error("dataToEncode must be an array of strings");
916
+ }
917
+ const monotonicULID = monotonicFactory();
918
+ const timestampPart = timeSeed ? ulid(timeSeed, deterministicPRNG(timeSeed)) : monotonicULID();
919
+ return [timestampPart, ...dataToEncode.filter(Boolean)].join(
920
+ separator || "-"
921
+ );
922
+ }
923
+ export {
924
+ AbstractCronEventConsumer,
925
+ ApiError,
926
+ BadRequestError,
927
+ BaseError,
928
+ ConsumerError,
929
+ DatabaseError,
930
+ EventConsumer,
931
+ ExternalDependencyError,
932
+ ForbiddenError,
933
+ HealthCheck,
934
+ Logger,
935
+ NotFoundError,
936
+ RateLimitError,
937
+ RestAPIConsumer,
938
+ TimeoutError,
939
+ UnauthorizedError,
940
+ decodeAbiParams,
941
+ errorCodes,
942
+ generateDeterministicULID,
943
+ handleError,
944
+ handleResponse,
945
+ httpResponseCodes,
946
+ parseEventLog,
947
+ setupHealthCheckServer,
948
+ validateBody,
949
+ validateParams,
950
+ validateQuery
951
+ };
9
952
  //# sourceMappingURL=index.js.map