@forklaunch/core 0.8.0 → 0.8.3

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,9 +1,7 @@
1
1
  "use strict";
2
- var __create = Object.create;
3
2
  var __defProp = Object.defineProperty;
4
3
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
- var __getProtoOf = Object.getPrototypeOf;
7
5
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
6
  var __export = (target, all) => {
9
7
  for (var name in all)
@@ -17,20 +15,11 @@ var __copyProps = (to, from, except, desc) => {
17
15
  }
18
16
  return to;
19
17
  };
20
- var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
- // If the importer is in node compatibility mode or this is not an ESM
22
- // file that has been converted to a CommonJS file using a Babel-
23
- // compatible transform (i.e. "__esModule" has not been set), then set
24
- // "default" to the CommonJS "module.exports" for node compatibility.
25
- isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
- mod
27
- ));
28
18
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
19
 
30
20
  // src/cache/index.ts
31
21
  var cache_exports = {};
32
22
  __export(cache_exports, {
33
- RedisTtlCache: () => RedisTtlCache,
34
23
  createCacheKey: () => createCacheKey
35
24
  });
36
25
  module.exports = __toCommonJS(cache_exports);
@@ -39,445 +28,8 @@ module.exports = __toCommonJS(cache_exports);
39
28
  var createCacheKey = (cacheKeyPrefix) => (id) => {
40
29
  return `${cacheKeyPrefix}:${id}`;
41
30
  };
42
-
43
- // src/cache/redisTtlCache.ts
44
- var import_common5 = require("@forklaunch/common");
45
- var import_redis = require("redis");
46
-
47
- // src/http/middleware/request/cors.middleware.ts
48
- var import_cors = __toESM(require("cors"));
49
-
50
- // src/http/middleware/request/createContext.middleware.ts
51
- var import_api = require("@opentelemetry/api");
52
- var import_uuid = require("uuid");
53
-
54
- // src/http/telemetry/constants.ts
55
- var import_semantic_conventions = require("@opentelemetry/semantic-conventions");
56
-
57
- // src/http/guards/isExpressLikeSchemaHandler.ts
58
- var import_common = require("@forklaunch/common");
59
-
60
- // src/http/middleware/request/auth.middleware.ts
61
- var import_jose = require("jose");
62
-
63
- // src/services/getEnvVar.ts
64
- function getEnvVar(name) {
65
- const value = process.env[name];
66
- return value;
67
- }
68
-
69
- // src/http/telemetry/openTelemetryCollector.ts
70
- var import_opentelemetry_instrumentation_hyper_express = require("@forklaunch/opentelemetry-instrumentation-hyper-express");
71
- var import_api3 = require("@opentelemetry/api");
72
- var import_exporter_logs_otlp_http = require("@opentelemetry/exporter-logs-otlp-http");
73
- var import_exporter_metrics_otlp_http = require("@opentelemetry/exporter-metrics-otlp-http");
74
- var import_exporter_trace_otlp_http = require("@opentelemetry/exporter-trace-otlp-http");
75
- var import_instrumentation_express = require("@opentelemetry/instrumentation-express");
76
- var import_instrumentation_http = require("@opentelemetry/instrumentation-http");
77
- var import_resources = require("@opentelemetry/resources");
78
- var import_sdk_logs = require("@opentelemetry/sdk-logs");
79
- var import_sdk_metrics = require("@opentelemetry/sdk-metrics");
80
- var import_sdk_node = require("@opentelemetry/sdk-node");
81
- var import_semantic_conventions2 = require("@opentelemetry/semantic-conventions");
82
- var import_dotenv = __toESM(require("dotenv"));
83
- var import_uuid2 = require("uuid");
84
-
85
- // src/http/guards/isForklaunchRequest.ts
86
- function isForklaunchRequest(request) {
87
- return request != null && typeof request === "object" && "contractDetails" in request;
88
- }
89
-
90
- // src/http/telemetry/pinoLogger.ts
91
- var import_common2 = require("@forklaunch/common");
92
- var import_api2 = require("@opentelemetry/api");
93
- var import_api_logs = require("@opentelemetry/api-logs");
94
- var import_pino = __toESM(require("pino"));
95
- var import_pino_pretty = __toESM(require("pino-pretty"));
96
-
97
- // src/http/telemetry/openTelemetryCollector.ts
98
- import_dotenv.default.config({ path: getEnvVar("ENV_FILE_PATH") });
99
- new import_sdk_node.NodeSDK({
100
- resource: (0, import_resources.resourceFromAttributes)({
101
- [import_semantic_conventions2.ATTR_SERVICE_NAME]: getEnvVar("OTEL_SERVICE_NAME")
102
- }),
103
- traceExporter: new import_exporter_trace_otlp_http.OTLPTraceExporter({
104
- url: `${getEnvVar("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/traces`
105
- }),
106
- metricReader: new import_sdk_metrics.PeriodicExportingMetricReader({
107
- exporter: new import_exporter_metrics_otlp_http.OTLPMetricExporter({
108
- url: `${getEnvVar("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/metrics`
109
- }),
110
- exportIntervalMillis: 5e3
111
- }),
112
- logRecordProcessors: [
113
- new import_sdk_logs.BatchLogRecordProcessor(
114
- new import_exporter_logs_otlp_http.OTLPLogExporter({
115
- url: `${getEnvVar("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/logs`
116
- })
117
- )
118
- ],
119
- instrumentations: [
120
- new import_instrumentation_http.HttpInstrumentation({
121
- applyCustomAttributesOnSpan: (span, request) => {
122
- span.setAttribute(
123
- "service.name",
124
- getEnvVar("OTEL_SERVICE_NAME") ?? "unknown"
125
- );
126
- if (isForklaunchRequest(request)) {
127
- span.setAttribute("api.name", request.contractDetails?.name);
128
- }
129
- }
130
- }),
131
- new import_instrumentation_express.ExpressInstrumentation(),
132
- new import_opentelemetry_instrumentation_hyper_express.HyperExpressInstrumentation()
133
- ]
134
- }).start();
135
- var httpRequestsTotalCounter = import_api3.metrics.getMeter(getEnvVar("OTEL_SERVICE_NAME") || "unknown").createCounter("http_requests_total", {
136
- description: "Number of HTTP requests"
137
- });
138
- var httpServerDurationHistogram = import_api3.metrics.getMeter(getEnvVar("OTEL_SERVICE_NAME") || "unknown").createHistogram("http_server_duration", {
139
- description: "Duration of HTTP server requests",
140
- unit: "s"
141
- });
142
-
143
- // src/http/middleware/request/parse.middleware.ts
144
- var import_validator = require("@forklaunch/validator");
145
-
146
- // src/http/middleware/response/parse.middleware.ts
147
- var import_validator2 = require("@forklaunch/validator");
148
-
149
- // src/http/middleware/response/enrichExpressLikeSend.middleware.ts
150
- var import_common4 = require("@forklaunch/common");
151
-
152
- // src/http/telemetry/recordMetric.ts
153
- var import_semantic_conventions3 = require("@opentelemetry/semantic-conventions");
154
-
155
- // src/services/configInjector.ts
156
- var import_common3 = require("@forklaunch/common");
157
- var import_validator3 = require("@forklaunch/validator");
158
-
159
- // src/http/telemetry/evaluateTelemetryOptions.ts
160
- function evaluateTelemetryOptions(telemetryOptions) {
161
- return {
162
- enabled: typeof telemetryOptions.enabled === "boolean" ? {
163
- metrics: telemetryOptions.enabled,
164
- tracing: telemetryOptions.enabled,
165
- logging: telemetryOptions.enabled
166
- } : {
167
- metrics: telemetryOptions.enabled.metrics,
168
- tracing: telemetryOptions.enabled.tracing,
169
- logging: telemetryOptions.enabled.logging
170
- },
171
- level: telemetryOptions.level
172
- };
173
- }
174
-
175
- // src/cache/redisTtlCache.ts
176
- var RedisTtlCache = class {
177
- /**
178
- * Creates an instance of RedisTtlCache.
179
- *
180
- * @param {number} ttlMilliseconds - The default Time-To-Live in milliseconds for cache entries
181
- * @param {OpenTelemetryCollector<MetricsDefinition>} openTelemetryCollector - Collector for OpenTelemetry metrics
182
- * @param {RedisClientOptions} hostingOptions - Configuration options for the Redis client
183
- * @param {TelemetryOptions} telemetryOptions - Configuration options for telemetry
184
- */
185
- constructor(ttlMilliseconds, openTelemetryCollector, hostingOptions, telemetryOptions) {
186
- this.ttlMilliseconds = ttlMilliseconds;
187
- this.openTelemetryCollector = openTelemetryCollector;
188
- this.telemetryOptions = evaluateTelemetryOptions(telemetryOptions);
189
- this.client = (0, import_redis.createClient)(hostingOptions);
190
- if (this.telemetryOptions.enabled.logging) {
191
- this.client.on("error", (err) => this.openTelemetryCollector.error(err));
192
- this.client.connect().catch(this.openTelemetryCollector.error);
193
- }
194
- }
195
- client;
196
- telemetryOptions;
197
- /**
198
- * Parses a raw Redis reply into the expected type.
199
- * Handles null values, arrays, buffers, and JSON strings.
200
- *
201
- * @template T - The expected type of the parsed value
202
- * @param {RedisCommandRawReply} value - The raw value from Redis to parse
203
- * @returns {T} The parsed value cast to type T
204
- */
205
- parseValue(value) {
206
- if (value == null) {
207
- return null;
208
- }
209
- if (Array.isArray(value)) {
210
- return value.map((v) => this.parseValue(v));
211
- }
212
- if (Buffer.isBuffer(value)) {
213
- return value.toJSON();
214
- }
215
- switch (typeof value) {
216
- case "object":
217
- case "string":
218
- return (0, import_common5.safeParse)(value);
219
- case "number":
220
- return value;
221
- }
222
- }
223
- /**
224
- * Puts a record into the Redis cache.
225
- *
226
- * @template T - The type of value being cached
227
- * @param {TtlCacheRecord<T>} param0 - The cache record containing key, value and optional TTL
228
- * @param {string} param0.key - The key to store the value under
229
- * @param {T} param0.value - The value to cache
230
- * @param {number} [param0.ttlMilliseconds] - Optional TTL in milliseconds, defaults to constructor value
231
- * @returns {Promise<void>} A promise that resolves when the value is cached
232
- */
233
- async putRecord({
234
- key,
235
- value,
236
- ttlMilliseconds = this.ttlMilliseconds
237
- }) {
238
- if (this.telemetryOptions.enabled.logging) {
239
- this.openTelemetryCollector.info(`Putting record into cache: ${key}`);
240
- }
241
- await this.client.set(key, (0, import_common5.safeStringify)(value), {
242
- PX: ttlMilliseconds
243
- });
244
- }
245
- /**
246
- * Puts multiple records into the Redis cache in a single transaction.
247
- *
248
- * @template T - The type of values being cached
249
- * @param {TtlCacheRecord<T>[]} cacheRecords - Array of cache records to store
250
- * @returns {Promise<void>} A promise that resolves when all values are cached
251
- */
252
- async putBatchRecords(cacheRecords) {
253
- const multiCommand = this.client.multi();
254
- for (const { key, value, ttlMilliseconds } of cacheRecords) {
255
- multiCommand.set(key, (0, import_common5.safeStringify)(value), {
256
- PX: ttlMilliseconds || this.ttlMilliseconds
257
- });
258
- }
259
- await multiCommand.exec();
260
- }
261
- /**
262
- * Adds a value to the left end of a Redis list.
263
- *
264
- * @template T - The type of value being enqueued
265
- * @param {string} queueName - The name of the Redis list
266
- * @param {T} value - The value to add to the list
267
- * @returns {Promise<void>} A promise that resolves when the value is enqueued
268
- */
269
- async enqueueRecord(queueName, value) {
270
- await this.client.lPush(queueName, (0, import_common5.safeStringify)(value));
271
- }
272
- /**
273
- * Adds multiple values to the left end of a Redis list in a single transaction.
274
- *
275
- * @template T - The type of values being enqueued
276
- * @param {string} queueName - The name of the Redis list
277
- * @param {T[]} values - Array of values to add to the list
278
- * @returns {Promise<void>} A promise that resolves when all values are enqueued
279
- */
280
- async enqueueBatchRecords(queueName, values) {
281
- const multiCommand = this.client.multi();
282
- for (const value of values) {
283
- multiCommand.lPush(queueName, (0, import_common5.safeStringify)(value));
284
- }
285
- await multiCommand.exec();
286
- }
287
- /**
288
- * Deletes a record from the Redis cache.
289
- *
290
- * @param {string} cacheRecordKey - The key of the record to delete
291
- * @returns {Promise<void>} A promise that resolves when the record is deleted
292
- */
293
- async deleteRecord(cacheRecordKey) {
294
- await this.client.del(cacheRecordKey);
295
- }
296
- /**
297
- * Deletes multiple records from the Redis cache in a single transaction.
298
- *
299
- * @param {string[]} cacheRecordKeys - Array of keys to delete
300
- * @returns {Promise<void>} A promise that resolves when all records are deleted
301
- */
302
- async deleteBatchRecords(cacheRecordKeys) {
303
- const multiCommand = this.client.multi();
304
- for (const key of cacheRecordKeys) {
305
- multiCommand.del(key);
306
- }
307
- await multiCommand.exec();
308
- }
309
- /**
310
- * Removes and returns the rightmost element from a Redis list.
311
- *
312
- * @template T - The type of value being dequeued
313
- * @param {string} queueName - The name of the Redis list
314
- * @returns {Promise<T>} A promise that resolves with the dequeued value
315
- * @throws {Error} If the queue is empty
316
- */
317
- async dequeueRecord(queueName) {
318
- const value = await this.client.rPop(queueName);
319
- if (value === null) {
320
- throw new Error(`Queue is empty: ${queueName}`);
321
- }
322
- return (0, import_common5.safeParse)(value);
323
- }
324
- /**
325
- * Removes and returns multiple elements from the right end of a Redis list.
326
- *
327
- * @template T - The type of values being dequeued
328
- * @param {string} queueName - The name of the Redis list
329
- * @param {number} pageSize - Maximum number of elements to dequeue
330
- * @returns {Promise<T[]>} A promise that resolves with an array of dequeued values
331
- */
332
- async dequeueBatchRecords(queueName, pageSize) {
333
- const multiCommand = this.client.multi();
334
- for (let i = 0; i < pageSize; i++) {
335
- multiCommand.rPop(queueName);
336
- }
337
- const values = await multiCommand.exec();
338
- return values.map(
339
- (value) => this.parseValue(value)
340
- ).filter(Boolean);
341
- }
342
- /**
343
- * Reads a record from the Redis cache.
344
- *
345
- * @template T - The type of value being read
346
- * @param {string} cacheRecordKey - The key of the record to read
347
- * @returns {Promise<TtlCacheRecord<T>>} A promise that resolves with the cache record
348
- * @throws {Error} If the record is not found
349
- */
350
- async readRecord(cacheRecordKey) {
351
- const [value, ttl] = await this.client.multi().get(cacheRecordKey).ttl(cacheRecordKey).exec();
352
- if (value === null) {
353
- throw new Error(`Record not found for key: ${cacheRecordKey}`);
354
- }
355
- return {
356
- key: cacheRecordKey,
357
- value: this.parseValue(value),
358
- ttlMilliseconds: this.parseValue(ttl) * 1e3
359
- };
360
- }
361
- /**
362
- * Reads multiple records from the Redis cache.
363
- *
364
- * @template T - The type of values being read
365
- * @param {string[] | string} cacheRecordKeysOrPrefix - Array of keys to read, or a prefix pattern
366
- * @returns {Promise<TtlCacheRecord<T>[]>} A promise that resolves with an array of cache records
367
- */
368
- async readBatchRecords(cacheRecordKeysOrPrefix) {
369
- const keys = Array.isArray(cacheRecordKeysOrPrefix) ? cacheRecordKeysOrPrefix : await this.client.keys(cacheRecordKeysOrPrefix + "*");
370
- const multiCommand = this.client.multi();
371
- for (const key of keys) {
372
- multiCommand.get(key);
373
- multiCommand.ttl(key);
374
- }
375
- const values = await multiCommand.exec();
376
- return values.reduce((acc, value, index) => {
377
- if (index % 2 === 0) {
378
- const maybeValue = this.parseValue(
379
- value
380
- );
381
- const ttl = this.parseValue(
382
- values[index + 1]
383
- );
384
- if (maybeValue && ttl) {
385
- acc.push({
386
- key: keys[index / 2],
387
- value: maybeValue,
388
- ttlMilliseconds: ttl * 1e3
389
- });
390
- }
391
- }
392
- return acc;
393
- }, []);
394
- }
395
- /**
396
- * Lists all keys in the Redis cache that match a pattern prefix.
397
- *
398
- * @param {string} pattern_prefix - The prefix pattern to match keys against
399
- * @returns {Promise<string[]>} A promise that resolves with an array of matching keys
400
- */
401
- async listKeys(pattern_prefix) {
402
- const keys = await this.client.keys(pattern_prefix + "*");
403
- return keys;
404
- }
405
- /**
406
- * Checks if a record exists in the Redis cache.
407
- *
408
- * @param {string} cacheRecordKey - The key to check
409
- * @returns {Promise<boolean>} A promise that resolves with true if the record exists, false otherwise
410
- */
411
- async peekRecord(cacheRecordKey) {
412
- const result = await this.client.exists(cacheRecordKey);
413
- return result === 1;
414
- }
415
- /**
416
- * Checks if multiple records exist in the Redis cache.
417
- *
418
- * @param {string[] | string} cacheRecordKeysOrPrefix - Array of keys to check, or a prefix pattern
419
- * @returns {Promise<boolean[]>} A promise that resolves with an array of existence booleans
420
- */
421
- async peekBatchRecords(cacheRecordKeysOrPrefix) {
422
- const keys = Array.isArray(cacheRecordKeysOrPrefix) ? cacheRecordKeysOrPrefix : await this.client.keys(cacheRecordKeysOrPrefix + "*");
423
- const multiCommand = this.client.multi();
424
- for (const key of keys) {
425
- multiCommand.exists(key);
426
- }
427
- const results = await multiCommand.exec();
428
- return results.map((result) => result === 1);
429
- }
430
- /**
431
- * Peeks at a record in the Redis cache.
432
- *
433
- * @template T - The type of value being peeked at
434
- * @param {string} queueName - The name of the Redis queue
435
- * @returns {Promise<T>} A promise that resolves with the peeked value
436
- */
437
- async peekQueueRecord(queueName) {
438
- const value = await this.client.lRange(queueName, 0, 0);
439
- return this.parseValue(value[0]);
440
- }
441
- /**
442
- * Peeks at multiple records in the Redis cache.
443
- *
444
- * @template T - The type of values being peeked at
445
- * @param {string} queueName - The name of the Redis queue
446
- * @param {number} pageSize - The number of records to peek at
447
- * @returns {Promise<T[]>} A promise that resolves with an array of peeked values
448
- */
449
- async peekQueueRecords(queueName, pageSize) {
450
- const values = await this.client.lRange(queueName, 0, pageSize - 1);
451
- return values.map((value) => this.parseValue(value)).filter(Boolean);
452
- }
453
- /**
454
- * Gracefully disconnects from the Redis server.
455
- *
456
- * @returns {Promise<void>} A promise that resolves when the connection is closed
457
- */
458
- async disconnect() {
459
- await this.client.quit();
460
- }
461
- /**
462
- * Gets the default Time-To-Live value in milliseconds.
463
- *
464
- * @returns {number} The default TTL in milliseconds
465
- */
466
- getTtlMilliseconds() {
467
- return this.ttlMilliseconds;
468
- }
469
- /**
470
- * Gets the underlying Redis client instance.
471
- *
472
- * @returns {typeof this.client} The Redis client instance
473
- */
474
- getClient() {
475
- return this.client;
476
- }
477
- };
478
31
  // Annotate the CommonJS export names for ESM import in node:
479
32
  0 && (module.exports = {
480
- RedisTtlCache,
481
33
  createCacheKey
482
34
  });
483
35
  //# sourceMappingURL=index.js.map