@forklaunch/core 0.7.4 → 0.8.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.
- package/lib/__test__/index.js +3 -5
- package/lib/__test__/index.js.map +1 -1
- package/lib/__test__/index.mjs +4 -6
- package/lib/__test__/index.mjs.map +1 -1
- package/lib/src/cache/index.js +30 -20
- package/lib/src/cache/index.js.map +1 -1
- package/lib/src/cache/index.mjs +37 -20
- package/lib/src/cache/index.mjs.map +1 -1
- package/lib/src/http/index.d.mts +201 -43
- package/lib/src/http/index.d.ts +201 -43
- package/lib/src/http/index.js +321 -37
- package/lib/src/http/index.js.map +1 -1
- package/lib/src/http/index.mjs +326 -37
- package/lib/src/http/index.mjs.map +1 -1
- package/lib/src/services/index.js +7 -1
- package/lib/src/services/index.js.map +1 -1
- package/lib/src/services/index.mjs +7 -1
- package/lib/src/services/index.mjs.map +1 -1
- package/package.json +31 -31
package/lib/__test__/index.js
CHANGED
@@ -32,11 +32,9 @@ var import_zod = require("@forklaunch/validator/zod");
|
|
32
32
|
function testSchemaEquality(zodSchema, typeBoxSchema, testData) {
|
33
33
|
const zodParseResult = (0, import_zod.parse)(zodSchema, testData);
|
34
34
|
const typeboxParseResult = (0, import_typebox.parse)(typeBoxSchema, testData);
|
35
|
-
const isEqual =
|
36
|
-
zodParseResult.ok ? (0, import_common.sortObjectKeys)(
|
37
|
-
|
38
|
-
) : "-1"
|
39
|
-
) === JSON.stringify(
|
35
|
+
const isEqual = (0, import_common.safeStringify)(
|
36
|
+
zodParseResult.ok ? (0, import_common.sortObjectKeys)(zodParseResult.value) : "-1"
|
37
|
+
) === (0, import_common.safeStringify)(
|
40
38
|
typeboxParseResult.ok ? (0, import_common.sortObjectKeys)(typeboxParseResult.value) : "1"
|
41
39
|
);
|
42
40
|
return isEqual;
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../../__test__/index.ts","../../__test__/utils/schemaEquality.ts"],"sourcesContent":["export * from './utils/schemaEquality';\n","import { sortObjectKeys } from '@forklaunch/common';\nimport { IdiomaticSchema } from '@forklaunch/validator';\n\nimport { Schema } from '@forklaunch/validator';\nimport {\n TypeboxSchemaValidator,\n parse as typeboxParse\n} from '@forklaunch/validator/typebox';\nimport {\n ZodSchemaValidator,\n parse as zodParse\n} from '@forklaunch/validator/zod';\n\nexport function testSchemaEquality<\n Z extends IdiomaticSchema<ZodSchemaValidator>,\n T extends IdiomaticSchema<TypeboxSchemaValidator>\n>(zodSchema: Z, typeBoxSchema: T, testData: Schema<Z, ZodSchemaValidator>) {\n const zodParseResult = zodParse(zodSchema, testData);\n const typeboxParseResult = typeboxParse(typeBoxSchema, testData);\n\n const isEqual =\n
|
1
|
+
{"version":3,"sources":["../../__test__/index.ts","../../__test__/utils/schemaEquality.ts"],"sourcesContent":["export * from './utils/schemaEquality';\n","import { safeStringify, sortObjectKeys } from '@forklaunch/common';\nimport { IdiomaticSchema } from '@forklaunch/validator';\n\nimport { Schema } from '@forklaunch/validator';\nimport {\n TypeboxSchemaValidator,\n parse as typeboxParse\n} from '@forklaunch/validator/typebox';\nimport {\n ZodSchemaValidator,\n parse as zodParse\n} from '@forklaunch/validator/zod';\n\nexport function testSchemaEquality<\n Z extends IdiomaticSchema<ZodSchemaValidator>,\n T extends IdiomaticSchema<TypeboxSchemaValidator>\n>(zodSchema: Z, typeBoxSchema: T, testData: Schema<Z, ZodSchemaValidator>) {\n const zodParseResult = zodParse(zodSchema, testData);\n const typeboxParseResult = typeboxParse(typeBoxSchema, testData);\n\n const isEqual =\n safeStringify(\n zodParseResult.ok ? sortObjectKeys(zodParseResult.value) : '-1'\n ) ===\n safeStringify(\n typeboxParseResult.ok\n ? sortObjectKeys(typeboxParseResult.value as Record<string, unknown>)\n : '1'\n );\n\n return isEqual as EqualityWithoutFunction<T, Z>;\n}\n\ntype InjectiveWithoutFunction<O, T> = {\n [K in keyof O]: K extends keyof T\n ? O[K] extends object\n ? T[K] extends object\n ? InjectiveWithoutFunction<O[K], T[K]>\n : false\n : O[K] extends (...args: never[]) => unknown\n ? T[K] extends (...args: never[]) => unknown\n ? true\n : false\n : O[K] extends T[K]\n ? T[K] extends O[K]\n ? true\n : false\n : false\n : false;\n} extends infer R\n ? R extends {\n [K in keyof R]: true;\n }\n ? true\n : false\n : false;\n\ntype EqualityWithoutFunction<\n T extends IdiomaticSchema<TypeboxSchemaValidator>,\n Z extends IdiomaticSchema<ZodSchemaValidator>\n> =\n Schema<T, TypeboxSchemaValidator> extends infer TypeboxSchema\n ? Schema<Z, ZodSchemaValidator> extends infer ZodSchema\n ? InjectiveWithoutFunction<\n TypeboxSchema,\n ZodSchema\n > extends InjectiveWithoutFunction<ZodSchema, TypeboxSchema>\n ? true\n : false\n : false\n : false;\n\nexport enum DummyEnum {\n A = 'A',\n B = 'B'\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,oBAA8C;AAI9C,qBAGO;AACP,iBAGO;AAEA,SAAS,mBAGd,WAAc,eAAkB,UAAyC;AACzE,QAAM,qBAAiB,WAAAA,OAAS,WAAW,QAAQ;AACnD,QAAM,yBAAqB,eAAAC,OAAa,eAAe,QAAQ;AAE/D,QAAM,cACJ;AAAA,IACE,eAAe,SAAK,8BAAe,eAAe,KAAK,IAAI;AAAA,EAC7D,UACA;AAAA,IACE,mBAAmB,SACf,8BAAe,mBAAmB,KAAgC,IAClE;AAAA,EACN;AAEF,SAAO;AACT;AAyCO,IAAK,YAAL,kBAAKC,eAAL;AACL,EAAAA,WAAA,OAAI;AACJ,EAAAA,WAAA,OAAI;AAFM,SAAAA;AAAA,GAAA;","names":["zodParse","typeboxParse","DummyEnum"]}
|
package/lib/__test__/index.mjs
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
// __test__/utils/schemaEquality.ts
|
2
|
-
import { sortObjectKeys } from "@forklaunch/common";
|
2
|
+
import { safeStringify, sortObjectKeys } from "@forklaunch/common";
|
3
3
|
import {
|
4
4
|
parse as typeboxParse
|
5
5
|
} from "@forklaunch/validator/typebox";
|
@@ -9,11 +9,9 @@ import {
|
|
9
9
|
function testSchemaEquality(zodSchema, typeBoxSchema, testData) {
|
10
10
|
const zodParseResult = zodParse(zodSchema, testData);
|
11
11
|
const typeboxParseResult = typeboxParse(typeBoxSchema, testData);
|
12
|
-
const isEqual =
|
13
|
-
zodParseResult.ok ? sortObjectKeys(
|
14
|
-
|
15
|
-
) : "-1"
|
16
|
-
) === JSON.stringify(
|
12
|
+
const isEqual = safeStringify(
|
13
|
+
zodParseResult.ok ? sortObjectKeys(zodParseResult.value) : "-1"
|
14
|
+
) === safeStringify(
|
17
15
|
typeboxParseResult.ok ? sortObjectKeys(typeboxParseResult.value) : "1"
|
18
16
|
);
|
19
17
|
return isEqual;
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../../__test__/utils/schemaEquality.ts"],"sourcesContent":["import { sortObjectKeys } from '@forklaunch/common';\nimport { IdiomaticSchema } from '@forklaunch/validator';\n\nimport { Schema } from '@forklaunch/validator';\nimport {\n TypeboxSchemaValidator,\n parse as typeboxParse\n} from '@forklaunch/validator/typebox';\nimport {\n ZodSchemaValidator,\n parse as zodParse\n} from '@forklaunch/validator/zod';\n\nexport function testSchemaEquality<\n Z extends IdiomaticSchema<ZodSchemaValidator>,\n T extends IdiomaticSchema<TypeboxSchemaValidator>\n>(zodSchema: Z, typeBoxSchema: T, testData: Schema<Z, ZodSchemaValidator>) {\n const zodParseResult = zodParse(zodSchema, testData);\n const typeboxParseResult = typeboxParse(typeBoxSchema, testData);\n\n const isEqual =\n
|
1
|
+
{"version":3,"sources":["../../__test__/utils/schemaEquality.ts"],"sourcesContent":["import { safeStringify, sortObjectKeys } from '@forklaunch/common';\nimport { IdiomaticSchema } from '@forklaunch/validator';\n\nimport { Schema } from '@forklaunch/validator';\nimport {\n TypeboxSchemaValidator,\n parse as typeboxParse\n} from '@forklaunch/validator/typebox';\nimport {\n ZodSchemaValidator,\n parse as zodParse\n} from '@forklaunch/validator/zod';\n\nexport function testSchemaEquality<\n Z extends IdiomaticSchema<ZodSchemaValidator>,\n T extends IdiomaticSchema<TypeboxSchemaValidator>\n>(zodSchema: Z, typeBoxSchema: T, testData: Schema<Z, ZodSchemaValidator>) {\n const zodParseResult = zodParse(zodSchema, testData);\n const typeboxParseResult = typeboxParse(typeBoxSchema, testData);\n\n const isEqual =\n safeStringify(\n zodParseResult.ok ? sortObjectKeys(zodParseResult.value) : '-1'\n ) ===\n safeStringify(\n typeboxParseResult.ok\n ? sortObjectKeys(typeboxParseResult.value as Record<string, unknown>)\n : '1'\n );\n\n return isEqual as EqualityWithoutFunction<T, Z>;\n}\n\ntype InjectiveWithoutFunction<O, T> = {\n [K in keyof O]: K extends keyof T\n ? O[K] extends object\n ? T[K] extends object\n ? InjectiveWithoutFunction<O[K], T[K]>\n : false\n : O[K] extends (...args: never[]) => unknown\n ? T[K] extends (...args: never[]) => unknown\n ? true\n : false\n : O[K] extends T[K]\n ? T[K] extends O[K]\n ? true\n : false\n : false\n : false;\n} extends infer R\n ? R extends {\n [K in keyof R]: true;\n }\n ? true\n : false\n : false;\n\ntype EqualityWithoutFunction<\n T extends IdiomaticSchema<TypeboxSchemaValidator>,\n Z extends IdiomaticSchema<ZodSchemaValidator>\n> =\n Schema<T, TypeboxSchemaValidator> extends infer TypeboxSchema\n ? Schema<Z, ZodSchemaValidator> extends infer ZodSchema\n ? InjectiveWithoutFunction<\n TypeboxSchema,\n ZodSchema\n > extends InjectiveWithoutFunction<ZodSchema, TypeboxSchema>\n ? true\n : false\n : false\n : false;\n\nexport enum DummyEnum {\n A = 'A',\n B = 'B'\n}\n"],"mappings":";AAAA,SAAS,eAAe,sBAAsB;AAI9C;AAAA,EAEE,SAAS;AAAA,OACJ;AACP;AAAA,EAEE,SAAS;AAAA,OACJ;AAEA,SAAS,mBAGd,WAAc,eAAkB,UAAyC;AACzE,QAAM,iBAAiB,SAAS,WAAW,QAAQ;AACnD,QAAM,qBAAqB,aAAa,eAAe,QAAQ;AAE/D,QAAM,UACJ;AAAA,IACE,eAAe,KAAK,eAAe,eAAe,KAAK,IAAI;AAAA,EAC7D,MACA;AAAA,IACE,mBAAmB,KACf,eAAe,mBAAmB,KAAgC,IAClE;AAAA,EACN;AAEF,SAAO;AACT;AAyCO,IAAK,YAAL,kBAAKA,eAAL;AACL,EAAAA,WAAA,OAAI;AACJ,EAAAA,WAAA,OAAI;AAFM,SAAAA;AAAA,GAAA;","names":["DummyEnum"]}
|
package/lib/src/cache/index.js
CHANGED
@@ -41,6 +41,7 @@ var createCacheKey = (cacheKeyPrefix) => (id) => {
|
|
41
41
|
};
|
42
42
|
|
43
43
|
// src/cache/redisTtlCache.ts
|
44
|
+
var import_common5 = require("@forklaunch/common");
|
44
45
|
var import_redis = require("redis");
|
45
46
|
|
46
47
|
// src/http/middleware/request/cors.middleware.ts
|
@@ -145,6 +146,9 @@ var import_validator = require("@forklaunch/validator");
|
|
145
146
|
// src/http/middleware/response/parse.middleware.ts
|
146
147
|
var import_validator2 = require("@forklaunch/validator");
|
147
148
|
|
149
|
+
// src/http/middleware/response/enrichExpressLikeSend.middleware.ts
|
150
|
+
var import_common4 = require("@forklaunch/common");
|
151
|
+
|
148
152
|
// src/http/telemetry/recordMetric.ts
|
149
153
|
var import_semantic_conventions3 = require("@opentelemetry/semantic-conventions");
|
150
154
|
|
@@ -211,7 +215,7 @@ var RedisTtlCache = class {
|
|
211
215
|
switch (typeof value) {
|
212
216
|
case "object":
|
213
217
|
case "string":
|
214
|
-
return
|
218
|
+
return (0, import_common5.safeParse)(value);
|
215
219
|
case "number":
|
216
220
|
return value;
|
217
221
|
}
|
@@ -234,7 +238,7 @@ var RedisTtlCache = class {
|
|
234
238
|
if (this.telemetryOptions.enabled.logging) {
|
235
239
|
this.openTelemetryCollector.info(`Putting record into cache: ${key}`);
|
236
240
|
}
|
237
|
-
await this.client.set(key,
|
241
|
+
await this.client.set(key, (0, import_common5.safeStringify)(value), {
|
238
242
|
PX: ttlMilliseconds
|
239
243
|
});
|
240
244
|
}
|
@@ -246,9 +250,9 @@ var RedisTtlCache = class {
|
|
246
250
|
* @returns {Promise<void>} A promise that resolves when all values are cached
|
247
251
|
*/
|
248
252
|
async putBatchRecords(cacheRecords) {
|
249
|
-
|
253
|
+
const multiCommand = this.client.multi();
|
250
254
|
for (const { key, value, ttlMilliseconds } of cacheRecords) {
|
251
|
-
multiCommand
|
255
|
+
multiCommand.set(key, (0, import_common5.safeStringify)(value), {
|
252
256
|
PX: ttlMilliseconds || this.ttlMilliseconds
|
253
257
|
});
|
254
258
|
}
|
@@ -263,7 +267,7 @@ var RedisTtlCache = class {
|
|
263
267
|
* @returns {Promise<void>} A promise that resolves when the value is enqueued
|
264
268
|
*/
|
265
269
|
async enqueueRecord(queueName, value) {
|
266
|
-
await this.client.lPush(queueName,
|
270
|
+
await this.client.lPush(queueName, (0, import_common5.safeStringify)(value));
|
267
271
|
}
|
268
272
|
/**
|
269
273
|
* Adds multiple values to the left end of a Redis list in a single transaction.
|
@@ -274,9 +278,9 @@ var RedisTtlCache = class {
|
|
274
278
|
* @returns {Promise<void>} A promise that resolves when all values are enqueued
|
275
279
|
*/
|
276
280
|
async enqueueBatchRecords(queueName, values) {
|
277
|
-
|
281
|
+
const multiCommand = this.client.multi();
|
278
282
|
for (const value of values) {
|
279
|
-
multiCommand
|
283
|
+
multiCommand.lPush(queueName, (0, import_common5.safeStringify)(value));
|
280
284
|
}
|
281
285
|
await multiCommand.exec();
|
282
286
|
}
|
@@ -296,9 +300,9 @@ var RedisTtlCache = class {
|
|
296
300
|
* @returns {Promise<void>} A promise that resolves when all records are deleted
|
297
301
|
*/
|
298
302
|
async deleteBatchRecords(cacheRecordKeys) {
|
299
|
-
|
303
|
+
const multiCommand = this.client.multi();
|
300
304
|
for (const key of cacheRecordKeys) {
|
301
|
-
multiCommand
|
305
|
+
multiCommand.del(key);
|
302
306
|
}
|
303
307
|
await multiCommand.exec();
|
304
308
|
}
|
@@ -315,7 +319,7 @@ var RedisTtlCache = class {
|
|
315
319
|
if (value === null) {
|
316
320
|
throw new Error(`Queue is empty: ${queueName}`);
|
317
321
|
}
|
318
|
-
return
|
322
|
+
return (0, import_common5.safeParse)(value);
|
319
323
|
}
|
320
324
|
/**
|
321
325
|
* Removes and returns multiple elements from the right end of a Redis list.
|
@@ -326,12 +330,14 @@ var RedisTtlCache = class {
|
|
326
330
|
* @returns {Promise<T[]>} A promise that resolves with an array of dequeued values
|
327
331
|
*/
|
328
332
|
async dequeueBatchRecords(queueName, pageSize) {
|
329
|
-
|
333
|
+
const multiCommand = this.client.multi();
|
330
334
|
for (let i = 0; i < pageSize; i++) {
|
331
|
-
multiCommand
|
335
|
+
multiCommand.rPop(queueName);
|
332
336
|
}
|
333
337
|
const values = await multiCommand.exec();
|
334
|
-
return values.map(
|
338
|
+
return values.map(
|
339
|
+
(value) => this.parseValue(value)
|
340
|
+
).filter(Boolean);
|
335
341
|
}
|
336
342
|
/**
|
337
343
|
* Reads a record from the Redis cache.
|
@@ -361,16 +367,20 @@ var RedisTtlCache = class {
|
|
361
367
|
*/
|
362
368
|
async readBatchRecords(cacheRecordKeysOrPrefix) {
|
363
369
|
const keys = Array.isArray(cacheRecordKeysOrPrefix) ? cacheRecordKeysOrPrefix : await this.client.keys(cacheRecordKeysOrPrefix + "*");
|
364
|
-
|
370
|
+
const multiCommand = this.client.multi();
|
365
371
|
for (const key of keys) {
|
366
|
-
multiCommand
|
367
|
-
multiCommand
|
372
|
+
multiCommand.get(key);
|
373
|
+
multiCommand.ttl(key);
|
368
374
|
}
|
369
375
|
const values = await multiCommand.exec();
|
370
376
|
return values.reduce((acc, value, index) => {
|
371
377
|
if (index % 2 === 0) {
|
372
|
-
const maybeValue = this.parseValue(
|
373
|
-
|
378
|
+
const maybeValue = this.parseValue(
|
379
|
+
value
|
380
|
+
);
|
381
|
+
const ttl = this.parseValue(
|
382
|
+
values[index + 1]
|
383
|
+
);
|
374
384
|
if (maybeValue && ttl) {
|
375
385
|
acc.push({
|
376
386
|
key: keys[index / 2],
|
@@ -410,9 +420,9 @@ var RedisTtlCache = class {
|
|
410
420
|
*/
|
411
421
|
async peekBatchRecords(cacheRecordKeysOrPrefix) {
|
412
422
|
const keys = Array.isArray(cacheRecordKeysOrPrefix) ? cacheRecordKeysOrPrefix : await this.client.keys(cacheRecordKeysOrPrefix + "*");
|
413
|
-
|
423
|
+
const multiCommand = this.client.multi();
|
414
424
|
for (const key of keys) {
|
415
|
-
multiCommand
|
425
|
+
multiCommand.exists(key);
|
416
426
|
}
|
417
427
|
const results = await multiCommand.exec();
|
418
428
|
return results.map((result) => result === 1);
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"sources":["../../../src/cache/index.ts","../../../src/cache/cacheKey.ts","../../../src/cache/redisTtlCache.ts","../../../src/http/middleware/request/cors.middleware.ts","../../../src/http/middleware/request/createContext.middleware.ts","../../../src/http/telemetry/constants.ts","../../../src/http/guards/isExpressLikeSchemaHandler.ts","../../../src/http/middleware/request/auth.middleware.ts","../../../src/services/getEnvVar.ts","../../../src/http/telemetry/openTelemetryCollector.ts","../../../src/http/guards/isForklaunchRequest.ts","../../../src/http/telemetry/pinoLogger.ts","../../../src/http/middleware/request/parse.middleware.ts","../../../src/http/middleware/response/parse.middleware.ts","../../../src/http/telemetry/recordMetric.ts","../../../src/services/configInjector.ts","../../../src/http/telemetry/evaluateTelemetryOptions.ts"],"sourcesContent":["export * from './cacheKey';\nexport * from './interfaces/ttlCache.interface';\nexport * from './redisTtlCache';\nexport * from './types/ttlCacheRecord.types';\n","/**\n * Creates a function that generates cache keys with a given prefix.\n *\n * @param {string} cacheKeyPrefix - The prefix to use for all cache keys\n * @returns {Function} A function that takes an ID and returns a formatted cache key\n * @example\n * const createUserCacheKey = createCacheKey('user');\n * const key = createUserCacheKey('123'); // Returns 'user:123'\n */\nexport const createCacheKey = (cacheKeyPrefix: string) => (id: string) => {\n return `${cacheKeyPrefix}:${id}`;\n};\n","import { createClient, RedisClientOptions } from 'redis';\nimport {\n evaluateTelemetryOptions,\n MetricsDefinition,\n TelemetryOptions\n} from '../http';\nimport { OpenTelemetryCollector } from '../http/telemetry/openTelemetryCollector';\nimport { TtlCache } from './interfaces/ttlCache.interface';\nimport { TtlCacheRecord } from './types/ttlCacheRecord.types';\n\n/**\n * Type representing a raw reply from Redis commands.\n * Can be a string, number, Buffer, null, undefined, or array of raw replies.\n */\ntype RedisCommandRawReply =\n | string\n | number\n | Buffer\n | null\n | undefined\n | Array<RedisCommandRawReply>;\n\n/**\n * Class representing a Redis-based TTL (Time-To-Live) cache.\n * Implements the TtlCache interface to provide caching functionality with automatic expiration.\n */\nexport class RedisTtlCache implements TtlCache {\n private client;\n private telemetryOptions;\n\n /**\n * Creates an instance of RedisTtlCache.\n *\n * @param {number} ttlMilliseconds - The default Time-To-Live in milliseconds for cache entries\n * @param {OpenTelemetryCollector<MetricsDefinition>} openTelemetryCollector - Collector for OpenTelemetry metrics\n * @param {RedisClientOptions} hostingOptions - Configuration options for the Redis client\n * @param {TelemetryOptions} telemetryOptions - Configuration options for telemetry\n */\n constructor(\n private ttlMilliseconds: number,\n private openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>,\n hostingOptions: RedisClientOptions,\n telemetryOptions: TelemetryOptions\n ) {\n this.telemetryOptions = evaluateTelemetryOptions(telemetryOptions);\n this.client = createClient(hostingOptions);\n if (this.telemetryOptions.enabled.logging) {\n this.client.on('error', (err) => this.openTelemetryCollector.error(err));\n this.client.connect().catch(this.openTelemetryCollector.error);\n }\n }\n\n /**\n * Parses a raw Redis reply into the expected type.\n * Handles null values, arrays, buffers, and JSON strings.\n *\n * @template T - The expected type of the parsed value\n * @param {RedisCommandRawReply} value - The raw value from Redis to parse\n * @returns {T} The parsed value cast to type T\n */\n private parseValue<T>(value: RedisCommandRawReply): T {\n if (value == null) {\n return null as T;\n }\n\n if (Array.isArray(value)) {\n return value.map((v) => this.parseValue<T>(v)) as T;\n }\n\n if (Buffer.isBuffer(value)) {\n return value.toJSON() as T;\n }\n\n switch (typeof value) {\n case 'object':\n case 'string':\n return JSON.parse(value);\n case 'number':\n return value as T;\n }\n }\n\n /**\n * Puts a record into the Redis cache.\n *\n * @template T - The type of value being cached\n * @param {TtlCacheRecord<T>} param0 - The cache record containing key, value and optional TTL\n * @param {string} param0.key - The key to store the value under\n * @param {T} param0.value - The value to cache\n * @param {number} [param0.ttlMilliseconds] - Optional TTL in milliseconds, defaults to constructor value\n * @returns {Promise<void>} A promise that resolves when the value is cached\n */\n async putRecord<T>({\n key,\n value,\n ttlMilliseconds = this.ttlMilliseconds\n }: TtlCacheRecord<T>): Promise<void> {\n if (this.telemetryOptions.enabled.logging) {\n this.openTelemetryCollector.info(`Putting record into cache: ${key}`);\n }\n await this.client.set(key, JSON.stringify(value), {\n PX: ttlMilliseconds\n });\n }\n\n /**\n * Puts multiple records into the Redis cache in a single transaction.\n *\n * @template T - The type of values being cached\n * @param {TtlCacheRecord<T>[]} cacheRecords - Array of cache records to store\n * @returns {Promise<void>} A promise that resolves when all values are cached\n */\n async putBatchRecords<T>(cacheRecords: TtlCacheRecord<T>[]): Promise<void> {\n let multiCommand = this.client.multi();\n for (const { key, value, ttlMilliseconds } of cacheRecords) {\n multiCommand = multiCommand.set(key, JSON.stringify(value), {\n PX: ttlMilliseconds || this.ttlMilliseconds\n });\n }\n await multiCommand.exec();\n }\n\n /**\n * Adds a value to the left end of a Redis list.\n *\n * @template T - The type of value being enqueued\n * @param {string} queueName - The name of the Redis list\n * @param {T} value - The value to add to the list\n * @returns {Promise<void>} A promise that resolves when the value is enqueued\n */\n async enqueueRecord<T>(queueName: string, value: T): Promise<void> {\n await this.client.lPush(queueName, JSON.stringify(value));\n }\n\n /**\n * Adds multiple values to the left end of a Redis list in a single transaction.\n *\n * @template T - The type of values being enqueued\n * @param {string} queueName - The name of the Redis list\n * @param {T[]} values - Array of values to add to the list\n * @returns {Promise<void>} A promise that resolves when all values are enqueued\n */\n async enqueueBatchRecords<T>(queueName: string, values: T[]): Promise<void> {\n let multiCommand = this.client.multi();\n for (const value of values) {\n multiCommand = multiCommand.lPush(queueName, JSON.stringify(value));\n }\n await multiCommand.exec();\n }\n\n /**\n * Deletes a record from the Redis cache.\n *\n * @param {string} cacheRecordKey - The key of the record to delete\n * @returns {Promise<void>} A promise that resolves when the record is deleted\n */\n async deleteRecord(cacheRecordKey: string): Promise<void> {\n await this.client.del(cacheRecordKey);\n }\n\n /**\n * Deletes multiple records from the Redis cache in a single transaction.\n *\n * @param {string[]} cacheRecordKeys - Array of keys to delete\n * @returns {Promise<void>} A promise that resolves when all records are deleted\n */\n async deleteBatchRecords(cacheRecordKeys: string[]): Promise<void> {\n let multiCommand = this.client.multi();\n for (const key of cacheRecordKeys) {\n multiCommand = multiCommand.del(key);\n }\n await multiCommand.exec();\n }\n\n /**\n * Removes and returns the rightmost element from a Redis list.\n *\n * @template T - The type of value being dequeued\n * @param {string} queueName - The name of the Redis list\n * @returns {Promise<T>} A promise that resolves with the dequeued value\n * @throws {Error} If the queue is empty\n */\n async dequeueRecord<T>(queueName: string): Promise<T> {\n const value = await this.client.rPop(queueName);\n if (value === null) {\n throw new Error(`Queue is empty: ${queueName}`);\n }\n return JSON.parse(value);\n }\n\n /**\n * Removes and returns multiple elements from the right end of a Redis list.\n *\n * @template T - The type of values being dequeued\n * @param {string} queueName - The name of the Redis list\n * @param {number} pageSize - Maximum number of elements to dequeue\n * @returns {Promise<T[]>} A promise that resolves with an array of dequeued values\n */\n async dequeueBatchRecords<T>(\n queueName: string,\n pageSize: number\n ): Promise<T[]> {\n let multiCommand = this.client.multi();\n for (let i = 0; i < pageSize; i++) {\n multiCommand = multiCommand.rPop(queueName);\n }\n const values = await multiCommand.exec();\n return values.map((value) => this.parseValue<T>(value)).filter(Boolean);\n }\n\n /**\n * Reads a record from the Redis cache.\n *\n * @template T - The type of value being read\n * @param {string} cacheRecordKey - The key of the record to read\n * @returns {Promise<TtlCacheRecord<T>>} A promise that resolves with the cache record\n * @throws {Error} If the record is not found\n */\n async readRecord<T>(cacheRecordKey: string): Promise<TtlCacheRecord<T>> {\n const [value, ttl] = await this.client\n .multi()\n .get(cacheRecordKey)\n .ttl(cacheRecordKey)\n .exec();\n if (value === null) {\n throw new Error(`Record not found for key: ${cacheRecordKey}`);\n }\n\n return {\n key: cacheRecordKey,\n value: this.parseValue<T>(value),\n ttlMilliseconds: this.parseValue<number>(ttl) * 1000\n };\n }\n\n /**\n * Reads multiple records from the Redis cache.\n *\n * @template T - The type of values being read\n * @param {string[] | string} cacheRecordKeysOrPrefix - Array of keys to read, or a prefix pattern\n * @returns {Promise<TtlCacheRecord<T>[]>} A promise that resolves with an array of cache records\n */\n async readBatchRecords<T>(\n cacheRecordKeysOrPrefix: string[] | string\n ): Promise<TtlCacheRecord<T>[]> {\n const keys = Array.isArray(cacheRecordKeysOrPrefix)\n ? cacheRecordKeysOrPrefix\n : await this.client.keys(cacheRecordKeysOrPrefix + '*');\n let multiCommand = this.client.multi();\n for (const key of keys) {\n multiCommand = multiCommand.get(key);\n multiCommand = multiCommand.ttl(key);\n }\n const values = await multiCommand.exec();\n return values.reduce<TtlCacheRecord<T>[]>((acc, value, index) => {\n if (index % 2 === 0) {\n const maybeValue = this.parseValue<T>(value);\n const ttl = this.parseValue<number>(values[index + 1]);\n if (maybeValue && ttl) {\n acc.push({\n key: keys[index / 2],\n value: maybeValue,\n ttlMilliseconds: ttl * 1000\n });\n }\n }\n return acc;\n }, []);\n }\n\n /**\n * Lists all keys in the Redis cache that match a pattern prefix.\n *\n * @param {string} pattern_prefix - The prefix pattern to match keys against\n * @returns {Promise<string[]>} A promise that resolves with an array of matching keys\n */\n async listKeys(pattern_prefix: string): Promise<string[]> {\n const keys = await this.client.keys(pattern_prefix + '*');\n return keys;\n }\n\n /**\n * Checks if a record exists in the Redis cache.\n *\n * @param {string} cacheRecordKey - The key to check\n * @returns {Promise<boolean>} A promise that resolves with true if the record exists, false otherwise\n */\n async peekRecord(cacheRecordKey: string): Promise<boolean> {\n const result = await this.client.exists(cacheRecordKey);\n return result === 1;\n }\n\n /**\n * Checks if multiple records exist in the Redis cache.\n *\n * @param {string[] | string} cacheRecordKeysOrPrefix - Array of keys to check, or a prefix pattern\n * @returns {Promise<boolean[]>} A promise that resolves with an array of existence booleans\n */\n async peekBatchRecords(\n cacheRecordKeysOrPrefix: string[] | string\n ): Promise<boolean[]> {\n const keys = Array.isArray(cacheRecordKeysOrPrefix)\n ? cacheRecordKeysOrPrefix\n : await this.client.keys(cacheRecordKeysOrPrefix + '*');\n let multiCommand = this.client.multi();\n for (const key of keys) {\n multiCommand = multiCommand.exists(key);\n }\n const results = await multiCommand.exec();\n return results.map((result) => result === 1);\n }\n\n /**\n * Peeks at a record in the Redis cache.\n *\n * @template T - The type of value being peeked at\n * @param {string} queueName - The name of the Redis queue\n * @returns {Promise<T>} A promise that resolves with the peeked value\n */\n async peekQueueRecord<T>(queueName: string): Promise<T> {\n const value = await this.client.lRange(queueName, 0, 0);\n return this.parseValue<T>(value[0]);\n }\n\n /**\n * Peeks at multiple records in the Redis cache.\n *\n * @template T - The type of values being peeked at\n * @param {string} queueName - The name of the Redis queue\n * @param {number} pageSize - The number of records to peek at\n * @returns {Promise<T[]>} A promise that resolves with an array of peeked values\n */\n async peekQueueRecords<T>(queueName: string, pageSize: number): Promise<T[]> {\n const values = await this.client.lRange(queueName, 0, pageSize - 1);\n return values.map((value) => this.parseValue<T>(value)).filter(Boolean);\n }\n\n /**\n * Gracefully disconnects from the Redis server.\n *\n * @returns {Promise<void>} A promise that resolves when the connection is closed\n */\n async disconnect(): Promise<void> {\n await this.client.quit();\n }\n\n /**\n * Gets the default Time-To-Live value in milliseconds.\n *\n * @returns {number} The default TTL in milliseconds\n */\n getTtlMilliseconds(): number {\n return this.ttlMilliseconds;\n }\n\n /**\n * Gets the underlying Redis client instance.\n *\n * @returns {typeof this.client} The Redis client instance\n */\n getClient(): typeof this.client {\n return this.client;\n }\n}\n","import { AnySchemaValidator } from '@forklaunch/validator';\nimport corsMiddleware from 'cors';\nimport { ParsedQs } from 'qs';\nimport {\n ForklaunchNextFunction,\n ForklaunchRequest,\n ForklaunchResHeaders,\n ForklaunchResponse\n} from '../../types/apiDefinition.types';\nimport { ParamsDictionary } from '../../types/contractDetails.types';\n\n/**\n * Cors middleware handler\n *\n * @param req - Express-like request object\n * @param res - Express-like response object\n * @param next - Express-like next function\n */\nexport function cors<\n SV extends AnySchemaValidator,\n P extends ParamsDictionary,\n ResBodyMap extends Record<number, unknown>,\n ReqBody extends Record<string, unknown>,\n ReqQuery extends ParsedQs,\n ReqHeaders extends Record<string, string>,\n ResHeaders extends Record<string, string>,\n LocalsObj extends Record<string, unknown>\n>(\n req: ForklaunchRequest<SV, P, ReqBody, ReqQuery, ReqHeaders>,\n res: ForklaunchResponse<\n ResBodyMap,\n ForklaunchResHeaders & ResHeaders,\n LocalsObj\n >,\n next?: ForklaunchNextFunction\n) {\n if (req.method === 'OPTIONS') {\n res.cors = true;\n }\n corsMiddleware()(req, res, next ?? (() => {}));\n}\n","import { AnySchemaValidator } from '@forklaunch/validator';\nimport { trace } from '@opentelemetry/api';\nimport { v4 } from 'uuid';\nimport { ATTR_CORRELATION_ID } from '../../telemetry/constants';\nimport {\n ExpressLikeSchemaHandler,\n ForklaunchNextFunction\n} from '../../types/apiDefinition.types';\nimport {\n Body,\n HeadersObject,\n ParamsObject,\n QueryObject,\n ResponsesObject\n} from '../../types/contractDetails.types';\n\n/**\n * Middleware to create and add a request context.\n *\n * @template SV - A type that extends AnySchemaValidator.\n * @template Request - A type that extends ForklaunchRequest.\n * @template Response - A type that extends ForklaunchResponse.\n * @template NextFunction - A type that extends ForklaunchNextFunction.\n * @param {SV} schemaValidator - The schema validator.\n * @returns {Function} - Middleware function to create request context.\n */\nexport function createContext<\n SV extends AnySchemaValidator,\n P extends ParamsObject<SV>,\n ResBodyMap extends ResponsesObject<SV>,\n ReqBody extends Body<SV>,\n ReqQuery extends QueryObject<SV>,\n ReqHeaders extends HeadersObject<SV>,\n ResHeaders extends HeadersObject<SV>,\n LocalsObj extends Record<string, unknown>\n>(\n schemaValidator: SV\n): ExpressLikeSchemaHandler<\n SV,\n P,\n ResBodyMap,\n ReqBody,\n ReqQuery,\n ReqHeaders,\n ResHeaders,\n LocalsObj,\n unknown,\n unknown,\n ForklaunchNextFunction\n> {\n return function setContext(req, res, next?) {\n req.schemaValidator = schemaValidator;\n\n let correlationId = v4();\n\n if (req.headers['x-correlation-id']) {\n correlationId = req.headers['x-correlation-id'];\n }\n\n res.setHeader('x-correlation-id', correlationId);\n\n req.context = {\n correlationId: correlationId\n };\n\n const span = trace.getActiveSpan();\n if (span != null) {\n req.context.span = span;\n req.context.span?.setAttribute(ATTR_CORRELATION_ID, correlationId);\n }\n\n next?.();\n };\n}\n","import {\n ATTR_HTTP_REQUEST_METHOD,\n ATTR_HTTP_RESPONSE_STATUS_CODE,\n ATTR_HTTP_ROUTE,\n ATTR_SERVICE_NAME\n} from '@opentelemetry/semantic-conventions';\n\nexport const ATTR_API_NAME = 'api.name';\nexport const ATTR_CORRELATION_ID = 'correlation.id';\n\nexport {\n ATTR_HTTP_REQUEST_METHOD,\n ATTR_HTTP_RESPONSE_STATUS_CODE,\n ATTR_HTTP_ROUTE,\n ATTR_SERVICE_NAME\n};\n","import { extractArgumentNames } from '@forklaunch/common';\nimport { AnySchemaValidator } from '@forklaunch/validator';\nimport { ExpressLikeSchemaHandler } from '../types/apiDefinition.types';\nimport {\n Body,\n HeadersObject,\n ParamsObject,\n QueryObject,\n ResponsesObject\n} from '../types/contractDetails.types';\n\n/**\n * Type guard that checks if an object is an Express-like schema handler.\n * A schema handler is a function that takes up to 3 arguments (request, response, next)\n * and is used for middleware or route handlers in an Express-like application.\n *\n * @template SV - A type that extends AnySchemaValidator\n * @template P - The type of route parameters\n * @template ResBodyMap - The type of response body mapping\n * @template ReqBody - The type of request body\n * @template ReqQuery - The type of request query parameters\n * @template ReqHeaders - The type of request headers\n * @template ResHeaders - The type of response headers\n * @template LocalsObj - The type of locals object\n * @template BaseRequest - The base request type\n * @template BaseResponse - The base response type\n * @template NextFunction - The type of next function\n * @param {unknown} middleware - The object to check\n * @returns {boolean} A type predicate indicating whether the object is an ExpressLikeSchemaHandler\n *\n * @example\n * ```ts\n * if (isExpressLikeSchemaHandler(middleware)) {\n * // middleware is now typed as ExpressLikeSchemaHandler\n * middleware(req, res, next);\n * }\n * ```\n */\nexport function isExpressLikeSchemaHandler<\n SV extends AnySchemaValidator,\n P extends ParamsObject<SV>,\n ResBodyMap extends ResponsesObject<SV>,\n ReqBody extends Body<SV>,\n ReqQuery extends QueryObject<SV>,\n ReqHeaders extends HeadersObject<SV>,\n ResHeaders extends HeadersObject<SV>,\n LocalsObj extends Record<string, unknown>,\n BaseRequest,\n BaseResponse,\n NextFunction\n>(\n middleware: unknown\n): middleware is ExpressLikeSchemaHandler<\n SV,\n P,\n ResBodyMap,\n ReqBody,\n ReqQuery,\n ReqHeaders,\n ResHeaders,\n LocalsObj,\n BaseRequest,\n BaseResponse,\n NextFunction\n> {\n const extractedArgumentNames =\n typeof middleware === 'function' &&\n new Set(\n extractArgumentNames(middleware).map((argumentName) =>\n argumentName.toLowerCase()\n )\n );\n return extractedArgumentNames && extractedArgumentNames.size <= 3;\n}\n","import { AnySchemaValidator } from '@forklaunch/validator';\nimport { jwtVerify } from 'jose';\nimport { ParsedQs } from 'qs';\nimport {\n ForklaunchNextFunction,\n ForklaunchRequest,\n ForklaunchResponse,\n MapParamsSchema,\n MapReqBodySchema,\n MapReqHeadersSchema,\n MapReqQuerySchema,\n MapResBodyMapSchema,\n MapResHeadersSchema,\n ResolvedForklaunchRequest\n} from '../../types/apiDefinition.types';\nimport {\n AuthMethods,\n Body,\n HeadersObject,\n ParamsDictionary,\n ParamsObject,\n QueryObject,\n ResponsesObject\n} from '../../types/contractDetails.types';\n\nconst invalidAuthorizationTokenFormat = [\n 401,\n 'Invalid Authorization token format.'\n] as const;\nconst invalidAuthorizationSubject = [\n 403,\n 'Invalid Authorization subject.'\n] as const;\nconst invalidAuthorizationTokenPermissions = [\n 403,\n 'Invalid Authorization permissions.'\n] as const;\nconst invalidAuthorizationTokenRoles = [\n 403,\n 'Invalid Authorization roles.'\n] as const;\nconst invalidAuthorizationToken = [\n 403,\n 'Invalid Authorization token.'\n] as const;\nconst invalidAuthorizationLogin = [\n 403,\n 'Invalid Authorization login.'\n] as const;\n\n/**\n * Checks the authorization token for validity.\n *\n * @param {AuthMethod} [authorizationMethod] - The method of authorization.\n * @param {string} [authorizationToken] - The authorization string.\n * @returns {Promise<[401 | 403, string] | string | undefined>} - The result of the authorization check.\n */\nasync function checkAuthorizationToken<\n SV extends AnySchemaValidator,\n P extends ParamsDictionary,\n ReqBody extends Record<string, unknown>,\n ReqQuery extends ParsedQs,\n ReqHeaders extends Record<string, string>,\n BaseRequest\n>(\n authorizationMethod: AuthMethods<\n SV,\n P,\n ReqBody,\n ReqQuery,\n ReqHeaders,\n BaseRequest\n >,\n authorizationToken?: string,\n req?: ResolvedForklaunchRequest<\n SV,\n P,\n ReqBody,\n ReqQuery,\n ReqHeaders,\n BaseRequest\n >\n): Promise<readonly [401 | 403 | 500, string] | undefined> {\n if (authorizationToken == null) {\n return [401, 'No Authorization token provided.'];\n }\n\n const [tokenPrefix, token] = authorizationToken.split(' ');\n\n let resourceId;\n\n switch (authorizationMethod.method) {\n case 'jwt': {\n if (tokenPrefix !== 'Bearer') {\n return invalidAuthorizationTokenFormat;\n }\n\n try {\n const decodedJwt = await jwtVerify(\n token,\n new TextEncoder().encode(\n // TODO: Check this at application startup if there is any route with jwt checking\n process.env.JWT_SECRET\n )\n );\n\n if (!decodedJwt.payload.sub) {\n return invalidAuthorizationSubject;\n }\n\n resourceId = decodedJwt.payload.sub;\n } catch (error) {\n (\n req as ForklaunchRequest<SV, P, ReqBody, ReqQuery, ReqHeaders>\n ).openTelemetryCollector.error(error);\n return invalidAuthorizationToken;\n }\n\n break;\n }\n case 'basic': {\n if (authorizationToken !== 'Basic') {\n return invalidAuthorizationTokenFormat;\n }\n\n const [username, password] = Buffer.from(token, 'base64')\n .toString('utf-8')\n .split(':');\n\n if (!username || !password) {\n return invalidAuthorizationTokenFormat;\n }\n\n if (!authorizationMethod.login(username, password)) {\n return invalidAuthorizationLogin;\n }\n\n resourceId = username;\n break;\n }\n case 'other':\n if (tokenPrefix !== authorizationMethod.tokenPrefix) {\n return invalidAuthorizationTokenFormat;\n }\n\n resourceId = authorizationMethod.decodeResource(token);\n\n break;\n }\n\n if (\n authorizationMethod.allowedPermissions ||\n authorizationMethod.forbiddenPermissions\n ) {\n if (!authorizationMethod.mapPermissions) {\n return [500, 'No permission mapping function provided.'];\n }\n\n const resourcePermissions = await authorizationMethod.mapPermissions(\n resourceId,\n req\n );\n\n if (authorizationMethod.allowedPermissions) {\n if (\n resourcePermissions.intersection(authorizationMethod.allowedPermissions)\n .size === 0\n ) {\n return invalidAuthorizationTokenPermissions;\n }\n }\n\n if (authorizationMethod.forbiddenPermissions) {\n if (\n resourcePermissions.intersection(\n authorizationMethod.forbiddenPermissions\n ).size !== 0\n ) {\n return invalidAuthorizationTokenPermissions;\n }\n }\n }\n\n if (authorizationMethod.allowedRoles || authorizationMethod.forbiddenRoles) {\n if (!authorizationMethod.mapRoles) {\n return [500, 'No role mapping function provided.'];\n }\n\n const resourceRoles = await authorizationMethod.mapRoles(resourceId, req);\n\n if (authorizationMethod.allowedRoles) {\n if (\n resourceRoles.intersection(authorizationMethod.allowedRoles).size === 0\n ) {\n return invalidAuthorizationTokenRoles;\n }\n }\n\n if (authorizationMethod.forbiddenRoles) {\n if (\n resourceRoles.intersection(authorizationMethod.forbiddenRoles).size !==\n 0\n ) {\n return invalidAuthorizationTokenRoles;\n }\n }\n }\n\n return [401, 'Invalid Authorization method.'];\n}\n\n/**\n * Middleware to parse request authorization.\n *\n * @template SV - A type that extends AnySchemaValidator.\n * @template Request - A type that extends ForklaunchRequest.\n * @template Response - A type that extends ForklaunchResponse.\n * @template NextFunction - A type that extends ForklaunchNextFunction.\n * @param {Request} req - The request object.\n * @param {Response} res - The response object.\n * @param {NextFunction} [next] - The next middleware function.\n */\nexport async function parseRequestAuth<\n SV extends AnySchemaValidator,\n P extends ParamsObject<SV>,\n ResBodyMap extends ResponsesObject<SV>,\n ReqBody extends Body<SV>,\n ReqQuery extends QueryObject<SV>,\n ReqHeaders extends HeadersObject<SV>,\n ResHeaders extends HeadersObject<SV>,\n LocalsObj extends Record<string, unknown>\n>(\n req: ForklaunchRequest<\n SV,\n MapParamsSchema<SV, P>,\n MapReqBodySchema<SV, ReqBody>,\n MapReqQuerySchema<SV, ReqQuery>,\n MapReqHeadersSchema<SV, ReqHeaders>\n >,\n res: ForklaunchResponse<\n MapResBodyMapSchema<SV, ResBodyMap>,\n MapResHeadersSchema<SV, ResHeaders>,\n LocalsObj\n >,\n next?: ForklaunchNextFunction\n) {\n const auth = req.contractDetails.auth as AuthMethods<\n SV,\n MapParamsSchema<SV, P>,\n MapReqBodySchema<SV, ReqBody>,\n MapReqQuerySchema<SV, ReqQuery>,\n MapReqHeadersSchema<SV, ReqHeaders>,\n unknown\n >;\n\n if (auth) {\n const [error, message] =\n (await checkAuthorizationToken<\n SV,\n MapParamsSchema<SV, P>,\n MapReqBodySchema<SV, ReqBody>,\n MapReqQuerySchema<SV, ReqQuery>,\n MapReqHeadersSchema<SV, ReqHeaders>,\n unknown\n >(\n auth,\n req.headers[\n (auth.method === 'other' ? auth.headerName : undefined) ??\n 'Authorization'\n ],\n req\n )) ?? [];\n if (error != null) {\n res.status(error).send(message);\n next?.(new Error(message));\n }\n }\n\n next?.();\n}\n","// This is a simple function that returns the value of an environment variable.\n// It casts a potentially undefined value to a string, since it will be validated in order to be bootstrapped.\n\nexport function getEnvVar(name: string): string {\n const value = process.env[name];\n return value as string;\n}\n","import { HyperExpressInstrumentation } from '@forklaunch/opentelemetry-instrumentation-hyper-express';\nimport {\n Counter,\n Gauge,\n Histogram,\n metrics,\n ObservableCounter,\n ObservableGauge,\n ObservableUpDownCounter,\n UpDownCounter\n} from '@opentelemetry/api';\nimport { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';\nimport { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';\nimport { ExpressInstrumentation } from '@opentelemetry/instrumentation-express';\nimport { HttpInstrumentation } from '@opentelemetry/instrumentation-http';\nimport { resourceFromAttributes } from '@opentelemetry/resources';\nimport { BatchLogRecordProcessor } from '@opentelemetry/sdk-logs';\nimport { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';\nimport { NodeSDK } from '@opentelemetry/sdk-node';\nimport {\n ATTR_HTTP_REQUEST_METHOD,\n ATTR_HTTP_RESPONSE_STATUS_CODE,\n ATTR_HTTP_ROUTE,\n ATTR_SERVICE_NAME\n} from '@opentelemetry/semantic-conventions';\nimport dotenv from 'dotenv';\nimport { LevelWithSilent, LevelWithSilentOrString } from 'pino';\nimport { v4 } from 'uuid';\nimport { getEnvVar } from '../../services/getEnvVar';\nimport { isForklaunchRequest } from '../guards/isForklaunchRequest';\nimport {\n LogFn,\n LoggerMeta,\n MetricsDefinition,\n MetricType\n} from '../types/openTelemetryCollector.types';\nimport { ATTR_API_NAME, ATTR_CORRELATION_ID } from './constants';\nimport { logger } from './pinoLogger';\n\nexport class OpenTelemetryCollector<\n AppliedMetricsDefinition extends MetricsDefinition\n> {\n private readonly uuid = v4();\n private readonly logger;\n private readonly metrics: Record<\n keyof AppliedMetricsDefinition,\n | Counter\n | Gauge\n | Histogram\n | UpDownCounter\n | ObservableCounter\n | ObservableGauge\n | ObservableUpDownCounter\n >;\n\n // scoped creation and create this in middleware when api execute. Also add correlation id\n constructor(\n private readonly serviceName: string,\n level?: LevelWithSilentOrString,\n metricDefinitions?: AppliedMetricsDefinition\n ) {\n this.logger = logger(level || 'info');\n\n this.metrics = {} as Record<\n keyof AppliedMetricsDefinition,\n MetricType<AppliedMetricsDefinition[keyof AppliedMetricsDefinition]>\n >;\n\n for (const [metricId, metricType] of Object.entries(\n metricDefinitions ?? {}\n )) {\n switch (metricType) {\n case 'counter':\n this.metrics[metricId as keyof AppliedMetricsDefinition] = metrics\n .getMeter(this.serviceName)\n .createCounter(metricId);\n break;\n case 'gauge':\n this.metrics[metricId as keyof AppliedMetricsDefinition] = metrics\n .getMeter(this.serviceName)\n .createGauge(metricId);\n break;\n case 'histogram':\n this.metrics[metricId as keyof AppliedMetricsDefinition] = metrics\n .getMeter(this.serviceName)\n .createHistogram(metricId);\n break;\n case 'upDownCounter':\n this.metrics[metricId as keyof AppliedMetricsDefinition] = metrics\n .getMeter(this.serviceName)\n .createUpDownCounter(metricId);\n break;\n case 'observableCounter':\n this.metrics[metricId as keyof AppliedMetricsDefinition] = metrics\n .getMeter(this.serviceName)\n .createObservableCounter(metricId);\n break;\n case 'observableGauge':\n this.metrics[metricId as keyof AppliedMetricsDefinition] = metrics\n .getMeter(this.serviceName)\n .createObservableGauge(metricId);\n break;\n case 'observableUpDownCounter':\n this.metrics[metricId as keyof AppliedMetricsDefinition] = metrics\n .getMeter(this.serviceName)\n .createObservableUpDownCounter(metricId);\n break;\n }\n }\n\n this.log(\n 'info',\n 'OpenTelemetry Collector (Traces + Logs + Metrics) started'\n );\n }\n\n log(level: LevelWithSilent, ...args: (string | unknown | LoggerMeta)[]) {\n this.logger.log(level, ...args);\n }\n\n info: LogFn = (\n msg: string | unknown | LoggerMeta,\n ...args: (string | unknown | LoggerMeta)[]\n ) => {\n this.logger.log('info', msg, ...args);\n };\n\n error: LogFn = (\n msg: string | unknown | LoggerMeta,\n ...args: (string | unknown | LoggerMeta)[]\n ) => {\n this.logger.log('error', msg, ...args);\n };\n\n warn: LogFn = (\n msg: string | unknown | LoggerMeta,\n ...args: (string | unknown | LoggerMeta)[]\n ) => {\n this.logger.log('warn', msg, ...args);\n };\n\n debug: LogFn = (\n msg: string | unknown | LoggerMeta,\n ...args: (string | unknown | LoggerMeta)[]\n ) => {\n this.logger.log('debug', msg, ...args);\n };\n\n trace: LogFn = (\n msg: string | unknown | LoggerMeta,\n ...args: (string | unknown | LoggerMeta)[]\n ) => {\n this.logger.log('trace', msg, ...args);\n };\n\n getMetric<T extends keyof AppliedMetricsDefinition>(\n metricId: T\n ): MetricType<AppliedMetricsDefinition[T]> {\n return this.metrics[metricId] as MetricType<AppliedMetricsDefinition[T]>;\n }\n}\n\ndotenv.config({ path: getEnvVar('ENV_FILE_PATH') });\n\nnew NodeSDK({\n resource: resourceFromAttributes({\n [ATTR_SERVICE_NAME]: getEnvVar('OTEL_SERVICE_NAME')\n }),\n traceExporter: new OTLPTraceExporter({\n url: `${\n getEnvVar('OTEL_EXPORTER_OTLP_ENDPOINT') ?? 'http://localhost:4318'\n }/v1/traces`\n }),\n metricReader: new PeriodicExportingMetricReader({\n exporter: new OTLPMetricExporter({\n url: `${\n getEnvVar('OTEL_EXPORTER_OTLP_ENDPOINT') ?? 'http://localhost:4318'\n }/v1/metrics`\n }),\n exportIntervalMillis: 5000\n }),\n logRecordProcessors: [\n new BatchLogRecordProcessor(\n new OTLPLogExporter({\n url: `${\n getEnvVar('OTEL_EXPORTER_OTLP_ENDPOINT') ?? 'http://localhost:4318'\n }/v1/logs`\n })\n )\n ],\n instrumentations: [\n new HttpInstrumentation({\n applyCustomAttributesOnSpan: (span, request) => {\n span.setAttribute(\n 'service.name',\n getEnvVar('OTEL_SERVICE_NAME') ?? 'unknown'\n );\n if (isForklaunchRequest(request)) {\n span.setAttribute('api.name', request.contractDetails?.name);\n }\n }\n }),\n new ExpressInstrumentation(),\n new HyperExpressInstrumentation()\n ]\n}).start();\n\n// begin static metrics -- these have to be in here in order to instantiate these after the SDK is instantiated\nexport const httpRequestsTotalCounter = metrics\n .getMeter(getEnvVar('OTEL_SERVICE_NAME') || 'unknown')\n .createCounter<{\n [ATTR_SERVICE_NAME]: string;\n [ATTR_API_NAME]: string;\n [ATTR_CORRELATION_ID]: string;\n [ATTR_HTTP_REQUEST_METHOD]: string;\n [ATTR_HTTP_ROUTE]: string;\n [ATTR_HTTP_RESPONSE_STATUS_CODE]: number;\n }>('http_requests_total', {\n description: 'Number of HTTP requests'\n });\n\nexport const httpServerDurationHistogram = metrics\n .getMeter(getEnvVar('OTEL_SERVICE_NAME') || 'unknown')\n .createHistogram<{\n [ATTR_SERVICE_NAME]: string;\n [ATTR_API_NAME]: string;\n [ATTR_HTTP_REQUEST_METHOD]: string;\n [ATTR_HTTP_ROUTE]: string;\n [ATTR_HTTP_RESPONSE_STATUS_CODE]: number;\n }>('http_server_duration', {\n description: 'Duration of HTTP server requests',\n unit: 's'\n });\n","import { AnySchemaValidator } from '@forklaunch/validator';\nimport { ParsedQs } from 'qs';\nimport { ForklaunchRequest } from '../types/apiDefinition.types';\nimport { ParamsDictionary } from '../types/contractDetails.types';\n\n/**\n * Type guard that checks if an object is a Forklaunch request.\n * A Forklaunch request is an object that contains contract details and follows the Forklaunch request structure.\n *\n * @template SV - A type that extends AnySchemaValidator\n * @template P - The type of route parameters that extends ParamsDictionary\n * @template ReqBody - The type of request body that extends Record<string, unknown>\n * @template ReqQuery - The type of request query parameters that extends ParsedQs\n * @template ReqHeaders - The type of request headers that extends Record<string, string>\n * @param {unknown} request - The object to check\n * @returns {boolean} A type predicate indicating whether the object is a ForklaunchRequest\n *\n * @example\n * ```ts\n * if (isForklaunchRequest(request)) {\n * // request is now typed as ForklaunchRequest\n * const { contractDetails } = request;\n * }\n * ```\n */\nexport function isForklaunchRequest<\n SV extends AnySchemaValidator,\n P extends ParamsDictionary,\n ReqBody extends Record<string, unknown>,\n ReqQuery extends ParsedQs,\n ReqHeaders extends Record<string, string>\n>(\n request: unknown\n): request is ForklaunchRequest<SV, P, ReqBody, ReqQuery, ReqHeaders> {\n return (\n request != null &&\n typeof request === 'object' &&\n 'contractDetails' in request\n );\n}\n","import { isNever, safeStringify } from '@forklaunch/common';\nimport { trace } from '@opentelemetry/api';\nimport { AnyValueMap, logs } from '@opentelemetry/api-logs';\nimport pino, { LevelWithSilent, LevelWithSilentOrString, Logger } from 'pino';\nimport PinoPretty from 'pino-pretty';\nimport { isLoggerMeta } from '../guards/isLoggerMeta';\nimport { LogFn, LoggerMeta } from '../types/openTelemetryCollector.types';\n\nexport function meta(meta: Record<string, unknown>) {\n return meta as LoggerMeta;\n}\n\nfunction mapSeverity(level: LevelWithSilent) {\n switch (level) {\n case 'silent':\n return 0;\n case 'trace':\n return 1;\n case 'debug':\n return 5;\n case 'info':\n return 9;\n case 'warn':\n return 13;\n case 'error':\n return 17;\n case 'fatal':\n return 21;\n default:\n isNever(level);\n return 0;\n }\n}\n\nclass PinoLogger {\n private pinoLogger: Logger;\n private meta: AnyValueMap;\n private prettyPrinter = PinoPretty.prettyFactory({\n colorize: true\n });\n\n constructor(level: LevelWithSilentOrString, meta: AnyValueMap = {}) {\n this.pinoLogger = pino({\n level: level || 'info',\n formatters: {\n level(label) {\n return { level: label };\n }\n },\n timestamp: pino.stdTimeFunctions.isoTime,\n transport: {\n target: 'pino-pretty',\n options: { colorize: true }\n }\n });\n this.meta = meta;\n }\n\n log(level: LevelWithSilent, ...args: (string | unknown | LoggerMeta)[]) {\n let meta: AnyValueMap = {};\n\n const filteredArgs = args\n .filter((arg) => {\n if (isLoggerMeta(arg)) {\n Object.assign(meta, arg);\n return false;\n }\n return true;\n })\n .map(safeStringify) as Parameters<pino.LogFn>;\n\n const activeSpan = trace.getActiveSpan();\n if (activeSpan) {\n const activeSpanContext = activeSpan.spanContext();\n meta.trace_id = activeSpanContext.traceId;\n meta.span_id = activeSpanContext.spanId;\n meta.trace_flags = activeSpanContext.traceFlags;\n meta = {\n // @ts-expect-error accessing private property\n ...activeSpan.attributes,\n ...meta\n };\n }\n\n meta = {\n 'api.name': 'none',\n 'correlation.id': 'none',\n ...meta\n };\n\n this.pinoLogger[level](...filteredArgs);\n logs.getLogger(process.env.OTEL_SERVICE_NAME ?? 'unknown').emit({\n severityText: level,\n severityNumber: mapSeverity(level),\n body: this.prettyPrinter(filteredArgs),\n attributes: { ...this.meta, ...meta }\n });\n }\n\n error: LogFn = (\n msg: string | unknown | LoggerMeta,\n ...args: (string | unknown | LoggerMeta)[]\n ) => this.log('error', msg, ...args);\n\n info: LogFn = (\n msg: string | unknown | LoggerMeta,\n ...args: (string | unknown | LoggerMeta)[]\n ) => this.log('info', msg, ...args);\n\n debug: LogFn = (\n msg: string | unknown | LoggerMeta,\n ...args: (string | unknown | LoggerMeta)[]\n ) => this.log('debug', msg, ...args);\n warn: LogFn = (\n msg: string | unknown | LoggerMeta,\n ...args: (string | unknown | LoggerMeta)[]\n ) => this.log('warn', msg, ...args);\n\n trace: LogFn = (\n msg: string | unknown | LoggerMeta,\n ...args: (string | unknown | LoggerMeta)[]\n ) => this.log('trace', msg, ...args);\n\n child(meta: AnyValueMap = {}) {\n return new PinoLogger(this.pinoLogger.level, { ...this.meta, ...meta });\n }\n\n getBaseLogger() {\n return this.pinoLogger;\n }\n}\n\nexport function logger(level: LevelWithSilentOrString, meta: AnyValueMap = {}) {\n return new PinoLogger(level, meta);\n}\n","import {\n AnySchemaValidator,\n prettyPrintParseErrors,\n SchemaValidator\n} from '@forklaunch/validator';\nimport { ParsedQs } from 'qs';\nimport { hasSend } from '../../guards/hasSend';\nimport { isResponseShape } from '../../guards/isResponseShape';\nimport {\n ForklaunchNextFunction,\n ForklaunchRequest,\n ForklaunchResponse\n} from '../../types/apiDefinition.types';\nimport { ParamsDictionary } from '../../types/contractDetails.types';\n\n/**\n * Pre-handler function to parse and validate input.\n *\n * @template SV - A type that extends AnySchemaValidator.\n * @template Request - A type that extends ForklaunchRequest.\n * @template Response - A type that extends ForklaunchResponse.\n * @template NextFunction - A type that extends ForklaunchNextFunction.\n * @param {Request} req - The request object.\n * @param {Response} res - The response object.\n * @param {NextFunction} [next] - The next middleware function.\n */\nexport function parse<\n SV extends AnySchemaValidator,\n P extends ParamsDictionary,\n ResBodyMap extends Record<number, unknown>,\n ReqBody extends Record<string, unknown>,\n ReqQuery extends ParsedQs,\n ReqHeaders extends Record<string, string>,\n ResHeaders extends Record<string, string>,\n LocalsObj extends Record<string, unknown>\n>(\n req: ForklaunchRequest<SV, P, ReqBody, ReqQuery, ReqHeaders>,\n res: ForklaunchResponse<ResBodyMap, ResHeaders, LocalsObj>,\n next?: ForklaunchNextFunction\n) {\n const request = {\n params: req.params,\n query: req.query,\n headers: req.headers,\n body: req.body\n };\n\n const parsedRequest = (req.schemaValidator as SchemaValidator).parse(\n req.requestSchema,\n request\n );\n\n if (\n parsedRequest.ok &&\n isResponseShape<P, ReqHeaders, ReqQuery, ReqBody>(parsedRequest.value)\n ) {\n req.body = parsedRequest.value.body;\n req.params = parsedRequest.value.params;\n Object.defineProperty(req, 'query', {\n value: parsedRequest.value.query,\n writable: false,\n enumerable: true,\n configurable: false\n });\n req.headers = parsedRequest.value.headers;\n }\n if (!parsedRequest.ok) {\n switch (req.contractDetails.options?.requestValidation) {\n default:\n case 'error':\n res.type('application/json');\n res.status(400);\n if (hasSend(res)) {\n res.send(\n `${prettyPrintParseErrors(\n parsedRequest.errors,\n 'Request'\n )}\\n\\nCorrelation id: ${\n req.context.correlationId ?? 'No correlation ID'\n }`\n );\n } else {\n next?.(new Error('Request is not sendable.'));\n }\n return;\n case 'warning':\n req.openTelemetryCollector.warn(\n prettyPrintParseErrors(parsedRequest.errors, 'Request')\n );\n break;\n case 'none':\n break;\n }\n }\n\n next?.();\n}\n","import {\n AnySchemaValidator,\n prettyPrintParseErrors,\n SchemaValidator\n} from '@forklaunch/validator';\nimport { ParsedQs } from 'qs';\nimport { hasSend } from '../../guards/hasSend';\nimport {\n ForklaunchNextFunction,\n ForklaunchRequest,\n ForklaunchResHeaders,\n ForklaunchResponse\n} from '../../types/apiDefinition.types';\nimport { ParamsDictionary } from '../../types/contractDetails.types';\n\n/**\n * Middleware to parse and validate the response according to the provided schema.\n *\n * This function validates the response against a schema provided by the request's schema validator.\n * If the response does not conform to the schema, the behavior is determined by the `responseValidation`\n * option in `req.contractDetails.options`:\n * - `'error'` (default): Calls `next` with an error.\n * - `'warning'`: Logs a warning to the console.\n * - `'none'`: Silently continues without action.\n *\n * @template SV - The type of the schema validator used in the request.\n * @template P - The type of the parameters dictionary used in the request.\n * @template ResBodyMap - A record type mapping status codes to response body types.\n * @template ReqBody - The type of the request body.\n * @template ReqQuery - The type of the parsed query string.\n * @template ReqHeaders - The type of the request headers.\n * @template ResHeaders - The type of the response headers, extended from `ForklaunchResHeaders`.\n * @template LocalsObj - The type of the locals object in the response.\n *\n * @param {ForklaunchRequest<SV, P, ReqBody, ReqQuery, ReqHeaders>} req - The request object, containing the schema validator and other request data.\n * @param {ForklaunchResponse<ResBodyMap, ForklaunchResHeaders & ResHeaders, LocalsObj>} res - The response object, including headers and body data.\n * @param {ForklaunchNextFunction} [next] - The next middleware function to be called. If not provided, the function will return after processing.\n *\n * @returns {void} This function does not return a value.\n */\nexport function parse<\n SV extends AnySchemaValidator,\n P extends ParamsDictionary,\n ResBodyMap extends Record<number, unknown>,\n ReqBody extends Record<string, unknown>,\n ReqQuery extends ParsedQs,\n ReqHeaders extends Record<string, string>,\n ResHeaders extends Record<string, string>,\n LocalsObj extends Record<string, unknown>\n>(\n req: ForklaunchRequest<SV, P, ReqBody, ReqQuery, ReqHeaders>,\n res: ForklaunchResponse<\n ResBodyMap,\n ForklaunchResHeaders & ResHeaders,\n LocalsObj\n >,\n next?: ForklaunchNextFunction\n) {\n const { headers, responses } = res.responseSchemas;\n\n const parsedResponse = (req.schemaValidator as SchemaValidator).parse(\n responses?.[res.statusCode],\n res.bodyData\n );\n\n const parsedHeaders = (req.schemaValidator as SchemaValidator).parse(\n headers ?? req.schemaValidator.unknown,\n res.getHeaders()\n );\n const parseErrors: string[] = [];\n if (!parsedHeaders.ok) {\n const headerErrors = prettyPrintParseErrors(parsedHeaders.errors, 'Header');\n if (headerErrors) {\n parseErrors.push(headerErrors);\n }\n }\n\n if (!parsedResponse.ok) {\n const responseErrors = prettyPrintParseErrors(\n parsedResponse.errors,\n 'Response'\n );\n if (responseErrors) {\n parseErrors.push(responseErrors);\n }\n }\n\n if (parseErrors.length > 0) {\n switch (req.contractDetails.options?.responseValidation) {\n default:\n case 'error':\n res.type('text/plain');\n res.status(400);\n if (hasSend(res)) {\n res.send(\n `Invalid response:\\n${parseErrors.join(\n '\\n\\n'\n )}\\n\\nCorrelation id: ${\n req.context.correlationId ?? 'No correlation ID'\n }`\n );\n } else {\n next?.(new Error('Response is not sendable.'));\n }\n // next?.(new Error(`Invalid response:\\n${parseErrors.join('\\n\\n')}`));\n // break;\n return;\n case 'warning':\n req.openTelemetryCollector.warn(\n `Invalid response:\\n${parseErrors.join('\\n\\n')}`\n );\n break;\n case 'none':\n break;\n }\n }\n next?.();\n}\n","import { AnySchemaValidator } from '@forklaunch/validator';\nimport {\n ATTR_HTTP_REQUEST_METHOD,\n ATTR_HTTP_RESPONSE_STATUS_CODE,\n ATTR_HTTP_ROUTE,\n ATTR_SERVICE_NAME\n} from '@opentelemetry/semantic-conventions';\nimport { ParsedQs } from 'qs';\nimport { ForklaunchRequest, ForklaunchResponse, ParamsDictionary } from '..';\nimport { getEnvVar } from '../../services';\nimport { ATTR_API_NAME, ATTR_CORRELATION_ID } from './constants';\nimport { httpRequestsTotalCounter } from './openTelemetryCollector';\n\nexport function recordMetric<\n SV extends AnySchemaValidator,\n P extends ParamsDictionary,\n ReqBody extends Record<string, unknown>,\n ReqQuery extends ParsedQs,\n ResBodyMap extends Record<string, unknown>,\n ReqHeaders extends Record<string, string>,\n ResHeaders extends Record<string, string>,\n LocalsObj extends Record<string, unknown>\n>(\n req: ForklaunchRequest<SV, P, ReqBody, ReqQuery, ReqHeaders>,\n res: ForklaunchResponse<ResBodyMap, ResHeaders, LocalsObj>\n) {\n if (res.metricRecorded) {\n return;\n }\n\n httpRequestsTotalCounter.add(1, {\n [ATTR_SERVICE_NAME]: getEnvVar('OTEL_SERVICE_NAME'),\n [ATTR_API_NAME]: req.contractDetails?.name,\n [ATTR_CORRELATION_ID]: req.context.correlationId,\n [ATTR_HTTP_REQUEST_METHOD]: req.method,\n [ATTR_HTTP_ROUTE]: req.originalPath,\n [ATTR_HTTP_RESPONSE_STATUS_CODE]: res.statusCode || 0\n });\n res.metricRecorded = true;\n}\n","import { extractArgumentNames, isNever } from '@forklaunch/common';\nimport {\n AnySchemaValidator,\n IdiomaticSchema,\n ParseResult,\n prettyPrintParseErrors,\n SchemaValidator\n} from '@forklaunch/validator';\nimport { isConstructed } from './guards/isConstructed';\nimport { isConstructedSingleton } from './guards/isConstructedSingleton';\nimport { isConstructor } from './guards/isConstructor';\nimport {\n ConfigValidator,\n Constructed,\n ConstructedSingleton,\n Lifetime,\n ResolvedConfigValidator,\n SchemaConstructor,\n SchemaFunction,\n Singleton\n} from './types/configInjector.types';\n\nexport function createConfigInjector<\n SV extends AnySchemaValidator,\n CV extends ConfigValidator<SV>\n>(\n schemaValidator: SV,\n dependenciesDefinition: {\n [K in keyof CV]:\n | Singleton<\n CV[K],\n Omit<ResolvedConfigValidator<SV, CV>, K>,\n ResolvedConfigValidator<SV, CV>[K]\n >\n | Constructed<\n CV[K],\n Omit<ResolvedConfigValidator<SV, CV>, K>,\n ResolvedConfigValidator<SV, CV>[K]\n >;\n }\n) {\n return new ConfigInjector<SV, CV>(\n schemaValidator,\n dependenciesDefinition\n ).load();\n}\n\nexport class ConfigInjector<\n SV extends AnySchemaValidator,\n CV extends ConfigValidator<SV>\n> {\n instances: {\n [K in keyof CV]?: ResolvedConfigValidator<SV, CV>[K];\n } = {};\n\n readonly configShapes: CV;\n\n load(inheritedScopeInstances?: {\n [K in keyof CV]?: ResolvedConfigValidator<SV, CV>[K];\n }): this {\n for (const token in inheritedScopeInstances) {\n this.instances[token] = inheritedScopeInstances[token];\n }\n\n for (const token in this.dependenciesDefinition) {\n const definition = this.dependenciesDefinition[token];\n if (\n definition.lifetime === Lifetime.Singleton &&\n !this.instances[token]\n ) {\n if (\n isConstructedSingleton<\n CV[typeof token],\n Omit<ResolvedConfigValidator<SV, CV>, typeof token>,\n ResolvedConfigValidator<SV, CV>[typeof token]\n >(definition)\n ) {\n this.instances[token] = this.resolveInstance<typeof token>(\n token,\n definition\n );\n } else {\n this.instances[token] = definition.value;\n }\n }\n }\n return this;\n }\n\n private resolveInstance<T extends keyof CV>(\n token: T,\n definition:\n | ConstructedSingleton<\n CV[T],\n Omit<ResolvedConfigValidator<SV, CV>, T>,\n ResolvedConfigValidator<SV, CV>[T]\n >\n | Constructed<\n CV[T],\n Omit<ResolvedConfigValidator<SV, CV>, T>,\n ResolvedConfigValidator<SV, CV>[T]\n >,\n context?: Record<string, unknown>,\n resolutionPath: (keyof CV)[] = []\n ): ResolvedConfigValidator<SV, CV>[T] {\n const injectorArgument = extractArgumentNames(definition.factory)[0];\n // short circuit as no args\n if (!injectorArgument || injectorArgument === '_args') {\n return definition.factory(\n {} as Omit<ResolvedConfigValidator<SV, CV>, T>,\n this.resolve.bind(this),\n context ?? ({} as Record<string, unknown>)\n );\n }\n\n if (!injectorArgument.startsWith('{') || !injectorArgument.endsWith('}')) {\n throw new Error(\n `Invalid injector argument for ${String(\n token\n )}: ${injectorArgument}. Please use object destructuring syntax: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment.`\n );\n }\n const resolvedArguments = Object.fromEntries(\n injectorArgument\n .replace('{', '')\n .replace('}', '')\n .split(',')\n .map((arg) => arg.split(':')[0].trim())\n .map((arg) => {\n const newResolutionPath = [...resolutionPath, token];\n if (resolutionPath.includes(arg)) {\n throw new Error(\n `Circular dependency detected: ${newResolutionPath.join(\n ' -> '\n )} -> ${arg}`\n );\n }\n const resolvedArg = this.resolve(arg, context, newResolutionPath);\n return [arg, resolvedArg];\n })\n ) as unknown as Omit<ResolvedConfigValidator<SV, CV>, T>;\n return definition.factory(\n resolvedArguments,\n this.resolve.bind(this),\n context ?? ({} as Record<string, unknown>)\n );\n }\n\n constructor(\n private schemaValidator: SV,\n private dependenciesDefinition: {\n [K in keyof CV]: (\n | Singleton<\n CV[K],\n Omit<ResolvedConfigValidator<SV, CV>, K>,\n ResolvedConfigValidator<SV, CV>[K]\n >\n | Constructed<\n CV[K],\n Omit<ResolvedConfigValidator<SV, CV>, K>,\n ResolvedConfigValidator<SV, CV>[K]\n >\n ) & {\n type: CV[K];\n };\n }\n ) {\n this.configShapes = Object.entries(this.dependenciesDefinition).reduce(\n (acc, [key, { type }]) => ({\n ...acc,\n [key]: type\n }),\n {} as Record<keyof CV, CV[keyof CV]>\n ) as CV;\n }\n\n safeValidateConfigSingletons(): ParseResult<ValidConfigInjector<SV, CV>> {\n const validNonSchemaSingletons = Object.entries(this.configShapes).reduce<\n ParseResult<ResolvedConfigValidator<SV, CV>>\n >(\n (acc, [key, value]) => {\n if (\n this.dependenciesDefinition[key].lifetime === Lifetime.Singleton &&\n !(this.schemaValidator as SchemaValidator).isSchema<\n SchemaFunction<SV> | SchemaConstructor<SV> | IdiomaticSchema<SV>\n >(value) &&\n isConstructor(value)\n ) {\n if (!(this.instances[key] instanceof value)) {\n const expected = value.name;\n const receivedValue: unknown = this.instances[key];\n const received = isConstructed(receivedValue)\n ? receivedValue.constructor.name\n : typeof receivedValue;\n\n if (acc.ok) {\n acc = {\n ok: false,\n errors: []\n };\n }\n acc.errors?.push({\n message: `Expected ${expected}, received ${received}`,\n path: [key]\n });\n } else {\n if (acc.ok) {\n acc = {\n ok: true,\n value: {\n ...acc.value,\n [key]: this.instances[key]\n }\n };\n }\n }\n return acc;\n }\n return acc;\n },\n {\n ok: true,\n value: {} as ResolvedConfigValidator<SV, CV>\n }\n );\n\n const singletons = Object.fromEntries(\n Object.entries(this.configShapes).filter(\n ([key, value]) =>\n this.dependenciesDefinition[key].lifetime === Lifetime.Singleton &&\n (this.schemaValidator as SchemaValidator).isSchema(value)\n )\n );\n const schemaSingletonParseResult = (\n this.schemaValidator as SchemaValidator\n ).parse(\n (this.schemaValidator as SchemaValidator).schemify(singletons),\n Object.fromEntries(\n Object.keys(singletons).map((key) => {\n const dependency = this.dependenciesDefinition[key];\n return [\n key,\n dependency.lifetime === Lifetime.Singleton\n ? this.instances[key]\n : undefined\n ];\n })\n )\n );\n\n const configKeys = Object.keys(this.configShapes);\n\n return validNonSchemaSingletons.ok && schemaSingletonParseResult.ok\n ? {\n ok: true as const,\n value: new ValidConfigInjector<SV, CV>(\n this.schemaValidator,\n this.dependenciesDefinition\n ).load({ ...this.instances })\n }\n : {\n ok: false as const,\n errors: [\n ...(!validNonSchemaSingletons.ok && validNonSchemaSingletons.errors\n ? validNonSchemaSingletons.errors\n : []),\n ...(!schemaSingletonParseResult.ok &&\n schemaSingletonParseResult.errors\n ? schemaSingletonParseResult.errors\n : [])\n ].sort(\n (a, b) =>\n configKeys.indexOf(a.path[0]) - configKeys.indexOf(b.path[0])\n )\n };\n }\n\n validateConfigSingletons(configName: string): ValidConfigInjector<SV, CV> {\n const safeValidateResult = this.safeValidateConfigSingletons();\n\n if (safeValidateResult.ok) {\n return safeValidateResult.value;\n }\n\n throw new Error(\n prettyPrintParseErrors(safeValidateResult.errors, configName)\n );\n }\n\n resolve<T extends keyof CV>(\n token: T,\n context?: Record<string, unknown>,\n resolutionPath: (keyof CV)[] = []\n ): ResolvedConfigValidator<SV, CV>[T] {\n const instance = this.instances[token];\n if (!instance) {\n const definition = this.dependenciesDefinition[token];\n\n if (!definition) {\n throw new Error(`Unable to resolve dependency ${String(token)}`);\n }\n\n switch (definition.lifetime) {\n case Lifetime.Singleton: {\n if (\n isConstructedSingleton<\n CV[T],\n Omit<ResolvedConfigValidator<SV, CV>, T>,\n ResolvedConfigValidator<SV, CV>[T]\n >(definition) &&\n !this.instances[token]\n ) {\n this.instances[token] = this.resolveInstance<T>(\n token,\n definition,\n context,\n resolutionPath\n );\n }\n return this.instances[token] as ResolvedConfigValidator<SV, CV>[T];\n }\n case Lifetime.Scoped: {\n if (\n !isConstructed<\n CV[T],\n Omit<ResolvedConfigValidator<SV, CV>, T>,\n ResolvedConfigValidator<SV, CV>[T]\n >(definition)\n ) {\n throw new Error(\n `Invalid dependency definition for ${String(token)}`\n );\n }\n\n const scopedInstance = this.resolveInstance<T>(\n token,\n definition,\n context,\n resolutionPath\n );\n this.instances[token] = scopedInstance;\n return scopedInstance;\n }\n case Lifetime.Transient: {\n if (\n !isConstructed<\n CV[T],\n Omit<ResolvedConfigValidator<SV, CV>, T>,\n ResolvedConfigValidator<SV, CV>[T]\n >(definition)\n ) {\n throw new Error(\n `Invalid dependency definition for ${String(token)}`\n );\n }\n\n return this.resolveInstance<T>(\n token,\n definition,\n context,\n resolutionPath\n );\n }\n default: {\n isNever(definition);\n throw new Error(\n `Unable to resolve lifetime for dependency ${String(\n token\n )}, ${resolutionPath}`\n );\n }\n }\n } else {\n return instance;\n }\n }\n\n scopedResolver<T extends keyof CV>(\n token: T,\n context?: Record<string, unknown>,\n resolutionPath: (keyof CV)[] = []\n ): (scope?: ConfigInjector<SV, CV>) => ResolvedConfigValidator<SV, CV>[T] {\n return (scope) =>\n (scope ?? this.createScope()).resolve<T>(token, context, resolutionPath);\n }\n\n createScope(): ConfigInjector<SV, CV> {\n return new ConfigInjector<SV, CV>(\n this.schemaValidator,\n this.dependenciesDefinition\n ).load({ ...this.instances });\n }\n\n dispose(): void {\n this.instances = {};\n this.load();\n }\n\n chain<ChainedCV extends ConfigValidator<SV>>(dependenciesDefinition: {\n [K in keyof ChainedCV]: {\n type: ChainedCV[K];\n } & (\n | Singleton<\n ChainedCV[K],\n Omit<ResolvedConfigValidator<SV, CV & ChainedCV>, K>,\n ResolvedConfigValidator<SV, ChainedCV>[K]\n >\n | Constructed<\n ChainedCV[K],\n Omit<ResolvedConfigValidator<SV, CV & ChainedCV>, K>,\n ResolvedConfigValidator<SV, ChainedCV>[K]\n >\n );\n }): ConfigInjector<SV, CV & ChainedCV> {\n return new ConfigInjector<SV, CV>(this.schemaValidator, {\n ...this.dependenciesDefinition,\n ...dependenciesDefinition\n }).load({ ...this.instances }) as unknown as ConfigInjector<\n SV,\n CV & ChainedCV\n >;\n }\n\n tokens(): {\n [K in keyof CV]: K;\n } {\n return Object.fromEntries(\n Object.keys(this.dependenciesDefinition).map((key) => [key, key])\n ) as {\n [K in keyof CV]: K;\n };\n }\n}\n\nexport class ValidConfigInjector<\n SV extends AnySchemaValidator,\n CV extends ConfigValidator<SV>\n> extends ConfigInjector<SV, CV> {\n validConfigInjector!: void;\n}\n","import { TelemetryOptions } from '../types/openTelemetryCollector.types';\n\nexport function evaluateTelemetryOptions(telemetryOptions: TelemetryOptions) {\n return {\n enabled:\n typeof telemetryOptions.enabled === 'boolean'\n ? {\n metrics: telemetryOptions.enabled,\n tracing: telemetryOptions.enabled,\n logging: telemetryOptions.enabled\n }\n : {\n metrics: telemetryOptions.enabled.metrics,\n tracing: telemetryOptions.enabled.tracing,\n logging: telemetryOptions.enabled.logging\n },\n level: telemetryOptions.level\n };\n}\n"],"mappings":"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","names":["import_api","import_semantic_conventions","import_uuid","import_common","import_api","dotenv","import_validator","import_semantic_conventions","import_common","import_validator"]}
|
1
|
+
{"version":3,"sources":["../../../src/cache/index.ts","../../../src/cache/cacheKey.ts","../../../src/cache/redisTtlCache.ts","../../../src/http/middleware/request/cors.middleware.ts","../../../src/http/middleware/request/createContext.middleware.ts","../../../src/http/telemetry/constants.ts","../../../src/http/guards/isExpressLikeSchemaHandler.ts","../../../src/http/middleware/request/auth.middleware.ts","../../../src/services/getEnvVar.ts","../../../src/http/telemetry/openTelemetryCollector.ts","../../../src/http/guards/isForklaunchRequest.ts","../../../src/http/telemetry/pinoLogger.ts","../../../src/http/middleware/request/parse.middleware.ts","../../../src/http/middleware/response/parse.middleware.ts","../../../src/http/middleware/response/enrichExpressLikeSend.middleware.ts","../../../src/http/telemetry/recordMetric.ts","../../../src/services/configInjector.ts","../../../src/http/telemetry/evaluateTelemetryOptions.ts"],"sourcesContent":["export * from './cacheKey';\nexport * from './interfaces/ttlCache.interface';\nexport * from './redisTtlCache';\nexport * from './types/ttlCacheRecord.types';\n","/**\n * Creates a function that generates cache keys with a given prefix.\n *\n * @param {string} cacheKeyPrefix - The prefix to use for all cache keys\n * @returns {Function} A function that takes an ID and returns a formatted cache key\n * @example\n * const createUserCacheKey = createCacheKey('user');\n * const key = createUserCacheKey('123'); // Returns 'user:123'\n */\nexport const createCacheKey = (cacheKeyPrefix: string) => (id: string) => {\n return `${cacheKeyPrefix}:${id}`;\n};\n","import { safeParse, safeStringify } from '@forklaunch/common';\nimport { createClient, RedisClientOptions } from 'redis';\nimport {\n evaluateTelemetryOptions,\n MetricsDefinition,\n TelemetryOptions\n} from '../http';\nimport { OpenTelemetryCollector } from '../http/telemetry/openTelemetryCollector';\nimport { TtlCache } from './interfaces/ttlCache.interface';\nimport { TtlCacheRecord } from './types/ttlCacheRecord.types';\n\n/**\n * Type representing a raw reply from Redis commands.\n * Can be a string, number, Buffer, null, undefined, or array of raw replies.\n */\ntype RedisCommandRawReply =\n | string\n | number\n | Buffer\n | null\n | undefined\n | Array<RedisCommandRawReply>;\n\n/**\n * Class representing a Redis-based TTL (Time-To-Live) cache.\n * Implements the TtlCache interface to provide caching functionality with automatic expiration.\n */\nexport class RedisTtlCache implements TtlCache {\n private client;\n private telemetryOptions;\n\n /**\n * Creates an instance of RedisTtlCache.\n *\n * @param {number} ttlMilliseconds - The default Time-To-Live in milliseconds for cache entries\n * @param {OpenTelemetryCollector<MetricsDefinition>} openTelemetryCollector - Collector for OpenTelemetry metrics\n * @param {RedisClientOptions} hostingOptions - Configuration options for the Redis client\n * @param {TelemetryOptions} telemetryOptions - Configuration options for telemetry\n */\n constructor(\n private ttlMilliseconds: number,\n private openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>,\n hostingOptions: RedisClientOptions,\n telemetryOptions: TelemetryOptions\n ) {\n this.telemetryOptions = evaluateTelemetryOptions(telemetryOptions);\n this.client = createClient(hostingOptions);\n if (this.telemetryOptions.enabled.logging) {\n this.client.on('error', (err) => this.openTelemetryCollector.error(err));\n this.client.connect().catch(this.openTelemetryCollector.error);\n }\n }\n\n /**\n * Parses a raw Redis reply into the expected type.\n * Handles null values, arrays, buffers, and JSON strings.\n *\n * @template T - The expected type of the parsed value\n * @param {RedisCommandRawReply} value - The raw value from Redis to parse\n * @returns {T} The parsed value cast to type T\n */\n private parseValue<T>(value: RedisCommandRawReply): T {\n if (value == null) {\n return null as T;\n }\n\n if (Array.isArray(value)) {\n return value.map((v) => this.parseValue<T>(v)) as T;\n }\n\n if (Buffer.isBuffer(value)) {\n return value.toJSON() as T;\n }\n\n switch (typeof value) {\n case 'object':\n case 'string':\n return safeParse(value) as T;\n case 'number':\n return value as T;\n }\n }\n\n /**\n * Puts a record into the Redis cache.\n *\n * @template T - The type of value being cached\n * @param {TtlCacheRecord<T>} param0 - The cache record containing key, value and optional TTL\n * @param {string} param0.key - The key to store the value under\n * @param {T} param0.value - The value to cache\n * @param {number} [param0.ttlMilliseconds] - Optional TTL in milliseconds, defaults to constructor value\n * @returns {Promise<void>} A promise that resolves when the value is cached\n */\n async putRecord<T>({\n key,\n value,\n ttlMilliseconds = this.ttlMilliseconds\n }: TtlCacheRecord<T>): Promise<void> {\n if (this.telemetryOptions.enabled.logging) {\n this.openTelemetryCollector.info(`Putting record into cache: ${key}`);\n }\n await this.client.set(key, safeStringify(value), {\n PX: ttlMilliseconds\n });\n }\n\n /**\n * Puts multiple records into the Redis cache in a single transaction.\n *\n * @template T - The type of values being cached\n * @param {TtlCacheRecord<T>[]} cacheRecords - Array of cache records to store\n * @returns {Promise<void>} A promise that resolves when all values are cached\n */\n async putBatchRecords<T>(cacheRecords: TtlCacheRecord<T>[]): Promise<void> {\n const multiCommand = this.client.multi();\n for (const { key, value, ttlMilliseconds } of cacheRecords) {\n multiCommand.set(key, safeStringify(value), {\n PX: ttlMilliseconds || this.ttlMilliseconds\n });\n }\n await multiCommand.exec();\n }\n\n /**\n * Adds a value to the left end of a Redis list.\n *\n * @template T - The type of value being enqueued\n * @param {string} queueName - The name of the Redis list\n * @param {T} value - The value to add to the list\n * @returns {Promise<void>} A promise that resolves when the value is enqueued\n */\n async enqueueRecord<T>(queueName: string, value: T): Promise<void> {\n await this.client.lPush(queueName, safeStringify(value));\n }\n\n /**\n * Adds multiple values to the left end of a Redis list in a single transaction.\n *\n * @template T - The type of values being enqueued\n * @param {string} queueName - The name of the Redis list\n * @param {T[]} values - Array of values to add to the list\n * @returns {Promise<void>} A promise that resolves when all values are enqueued\n */\n async enqueueBatchRecords<T>(queueName: string, values: T[]): Promise<void> {\n const multiCommand = this.client.multi();\n for (const value of values) {\n multiCommand.lPush(queueName, safeStringify(value));\n }\n await multiCommand.exec();\n }\n\n /**\n * Deletes a record from the Redis cache.\n *\n * @param {string} cacheRecordKey - The key of the record to delete\n * @returns {Promise<void>} A promise that resolves when the record is deleted\n */\n async deleteRecord(cacheRecordKey: string): Promise<void> {\n await this.client.del(cacheRecordKey);\n }\n\n /**\n * Deletes multiple records from the Redis cache in a single transaction.\n *\n * @param {string[]} cacheRecordKeys - Array of keys to delete\n * @returns {Promise<void>} A promise that resolves when all records are deleted\n */\n async deleteBatchRecords(cacheRecordKeys: string[]): Promise<void> {\n const multiCommand = this.client.multi();\n for (const key of cacheRecordKeys) {\n multiCommand.del(key);\n }\n await multiCommand.exec();\n }\n\n /**\n * Removes and returns the rightmost element from a Redis list.\n *\n * @template T - The type of value being dequeued\n * @param {string} queueName - The name of the Redis list\n * @returns {Promise<T>} A promise that resolves with the dequeued value\n * @throws {Error} If the queue is empty\n */\n async dequeueRecord<T>(queueName: string): Promise<T> {\n const value = await this.client.rPop(queueName);\n if (value === null) {\n throw new Error(`Queue is empty: ${queueName}`);\n }\n return safeParse(value) as T;\n }\n\n /**\n * Removes and returns multiple elements from the right end of a Redis list.\n *\n * @template T - The type of values being dequeued\n * @param {string} queueName - The name of the Redis list\n * @param {number} pageSize - Maximum number of elements to dequeue\n * @returns {Promise<T[]>} A promise that resolves with an array of dequeued values\n */\n async dequeueBatchRecords<T>(\n queueName: string,\n pageSize: number\n ): Promise<T[]> {\n const multiCommand = this.client.multi();\n for (let i = 0; i < pageSize; i++) {\n multiCommand.rPop(queueName);\n }\n const values = await multiCommand.exec();\n return values\n .map((value) =>\n this.parseValue<T>(value as unknown as RedisCommandRawReply)\n )\n .filter(Boolean);\n }\n\n /**\n * Reads a record from the Redis cache.\n *\n * @template T - The type of value being read\n * @param {string} cacheRecordKey - The key of the record to read\n * @returns {Promise<TtlCacheRecord<T>>} A promise that resolves with the cache record\n * @throws {Error} If the record is not found\n */\n async readRecord<T>(cacheRecordKey: string): Promise<TtlCacheRecord<T>> {\n const [value, ttl] = await this.client\n .multi()\n .get(cacheRecordKey)\n .ttl(cacheRecordKey)\n .exec();\n if (value === null) {\n throw new Error(`Record not found for key: ${cacheRecordKey}`);\n }\n\n return {\n key: cacheRecordKey,\n value: this.parseValue<T>(value as unknown as RedisCommandRawReply),\n ttlMilliseconds:\n this.parseValue<number>(ttl as unknown as RedisCommandRawReply) * 1000\n };\n }\n\n /**\n * Reads multiple records from the Redis cache.\n *\n * @template T - The type of values being read\n * @param {string[] | string} cacheRecordKeysOrPrefix - Array of keys to read, or a prefix pattern\n * @returns {Promise<TtlCacheRecord<T>[]>} A promise that resolves with an array of cache records\n */\n async readBatchRecords<T>(\n cacheRecordKeysOrPrefix: string[] | string\n ): Promise<TtlCacheRecord<T>[]> {\n const keys = Array.isArray(cacheRecordKeysOrPrefix)\n ? cacheRecordKeysOrPrefix\n : await this.client.keys(cacheRecordKeysOrPrefix + '*');\n const multiCommand = this.client.multi();\n for (const key of keys) {\n multiCommand.get(key);\n multiCommand.ttl(key);\n }\n const values = await multiCommand.exec();\n return values.reduce<TtlCacheRecord<T>[]>((acc, value, index) => {\n if (index % 2 === 0) {\n const maybeValue = this.parseValue<T>(\n value as unknown as RedisCommandRawReply\n );\n const ttl = this.parseValue<number>(\n values[index + 1] as unknown as RedisCommandRawReply\n );\n if (maybeValue && ttl) {\n acc.push({\n key: keys[index / 2],\n value: maybeValue,\n ttlMilliseconds: ttl * 1000\n });\n }\n }\n return acc;\n }, []);\n }\n\n /**\n * Lists all keys in the Redis cache that match a pattern prefix.\n *\n * @param {string} pattern_prefix - The prefix pattern to match keys against\n * @returns {Promise<string[]>} A promise that resolves with an array of matching keys\n */\n async listKeys(pattern_prefix: string): Promise<string[]> {\n const keys = await this.client.keys(pattern_prefix + '*');\n return keys;\n }\n\n /**\n * Checks if a record exists in the Redis cache.\n *\n * @param {string} cacheRecordKey - The key to check\n * @returns {Promise<boolean>} A promise that resolves with true if the record exists, false otherwise\n */\n async peekRecord(cacheRecordKey: string): Promise<boolean> {\n const result = await this.client.exists(cacheRecordKey);\n return result === 1;\n }\n\n /**\n * Checks if multiple records exist in the Redis cache.\n *\n * @param {string[] | string} cacheRecordKeysOrPrefix - Array of keys to check, or a prefix pattern\n * @returns {Promise<boolean[]>} A promise that resolves with an array of existence booleans\n */\n async peekBatchRecords(\n cacheRecordKeysOrPrefix: string[] | string\n ): Promise<boolean[]> {\n const keys = Array.isArray(cacheRecordKeysOrPrefix)\n ? cacheRecordKeysOrPrefix\n : await this.client.keys(cacheRecordKeysOrPrefix + '*');\n const multiCommand = this.client.multi();\n for (const key of keys) {\n multiCommand.exists(key);\n }\n const results = await multiCommand.exec();\n return results.map((result) => (result as unknown as number) === 1);\n }\n\n /**\n * Peeks at a record in the Redis cache.\n *\n * @template T - The type of value being peeked at\n * @param {string} queueName - The name of the Redis queue\n * @returns {Promise<T>} A promise that resolves with the peeked value\n */\n async peekQueueRecord<T>(queueName: string): Promise<T> {\n const value = await this.client.lRange(queueName, 0, 0);\n return this.parseValue<T>(value[0]);\n }\n\n /**\n * Peeks at multiple records in the Redis cache.\n *\n * @template T - The type of values being peeked at\n * @param {string} queueName - The name of the Redis queue\n * @param {number} pageSize - The number of records to peek at\n * @returns {Promise<T[]>} A promise that resolves with an array of peeked values\n */\n async peekQueueRecords<T>(queueName: string, pageSize: number): Promise<T[]> {\n const values = await this.client.lRange(queueName, 0, pageSize - 1);\n return values.map((value) => this.parseValue<T>(value)).filter(Boolean);\n }\n\n /**\n * Gracefully disconnects from the Redis server.\n *\n * @returns {Promise<void>} A promise that resolves when the connection is closed\n */\n async disconnect(): Promise<void> {\n await this.client.quit();\n }\n\n /**\n * Gets the default Time-To-Live value in milliseconds.\n *\n * @returns {number} The default TTL in milliseconds\n */\n getTtlMilliseconds(): number {\n return this.ttlMilliseconds;\n }\n\n /**\n * Gets the underlying Redis client instance.\n *\n * @returns {typeof this.client} The Redis client instance\n */\n getClient(): typeof this.client {\n return this.client;\n }\n}\n","import { AnySchemaValidator } from '@forklaunch/validator';\nimport corsMiddleware from 'cors';\nimport { ParsedQs } from 'qs';\nimport {\n ForklaunchNextFunction,\n ForklaunchRequest,\n ForklaunchResHeaders,\n ForklaunchResponse\n} from '../../types/apiDefinition.types';\nimport { ParamsDictionary } from '../../types/contractDetails.types';\n\n/**\n * Cors middleware handler\n *\n * @param req - Express-like request object\n * @param res - Express-like response object\n * @param next - Express-like next function\n */\nexport function cors<\n SV extends AnySchemaValidator,\n P extends ParamsDictionary,\n ResBodyMap extends Record<number, unknown>,\n ReqBody extends Record<string, unknown>,\n ReqQuery extends ParsedQs,\n ReqHeaders extends Record<string, string>,\n ResHeaders extends Record<string, string>,\n LocalsObj extends Record<string, unknown>\n>(\n req: ForklaunchRequest<SV, P, ReqBody, ReqQuery, ReqHeaders>,\n res: ForklaunchResponse<\n unknown,\n ResBodyMap,\n ForklaunchResHeaders & ResHeaders,\n LocalsObj\n >,\n next?: ForklaunchNextFunction\n) {\n if (req.method === 'OPTIONS') {\n res.cors = true;\n }\n corsMiddleware()(req, res, next ?? (() => {}));\n}\n","import { AnySchemaValidator } from '@forklaunch/validator';\nimport { trace } from '@opentelemetry/api';\nimport { v4 } from 'uuid';\nimport { ATTR_CORRELATION_ID } from '../../telemetry/constants';\nimport {\n ExpressLikeSchemaHandler,\n ForklaunchNextFunction\n} from '../../types/apiDefinition.types';\nimport {\n Body,\n HeadersObject,\n ParamsObject,\n QueryObject,\n ResponsesObject\n} from '../../types/contractDetails.types';\n\n/**\n * Middleware to create and add a request context.\n *\n * @template SV - A type that extends AnySchemaValidator.\n * @template Request - A type that extends ForklaunchRequest.\n * @template Response - A type that extends ForklaunchResponse.\n * @template NextFunction - A type that extends ForklaunchNextFunction.\n * @param {SV} schemaValidator - The schema validator.\n * @returns {Function} - Middleware function to create request context.\n */\nexport function createContext<\n SV extends AnySchemaValidator,\n P extends ParamsObject<SV>,\n ResBodyMap extends ResponsesObject<SV>,\n ReqBody extends Body<SV>,\n ReqQuery extends QueryObject<SV>,\n ReqHeaders extends HeadersObject<SV>,\n ResHeaders extends HeadersObject<SV>,\n LocalsObj extends Record<string, unknown>\n>(\n schemaValidator: SV\n): ExpressLikeSchemaHandler<\n SV,\n P,\n ResBodyMap,\n ReqBody,\n ReqQuery,\n ReqHeaders,\n ResHeaders,\n LocalsObj,\n unknown,\n unknown,\n ForklaunchNextFunction\n> {\n return function setContext(req, res, next?) {\n req.schemaValidator = schemaValidator;\n\n let correlationId = v4();\n\n if (req.headers['x-correlation-id']) {\n correlationId = req.headers['x-correlation-id'];\n }\n\n res.setHeader('x-correlation-id', correlationId);\n\n req.context = {\n correlationId: correlationId\n };\n\n const span = trace.getActiveSpan();\n if (span != null) {\n req.context.span = span;\n req.context.span?.setAttribute(ATTR_CORRELATION_ID, correlationId);\n }\n\n next?.();\n };\n}\n","import {\n ATTR_HTTP_REQUEST_METHOD,\n ATTR_HTTP_RESPONSE_STATUS_CODE,\n ATTR_HTTP_ROUTE,\n ATTR_SERVICE_NAME\n} from '@opentelemetry/semantic-conventions';\n\nexport const ATTR_API_NAME = 'api.name';\nexport const ATTR_CORRELATION_ID = 'correlation.id';\n\nexport {\n ATTR_HTTP_REQUEST_METHOD,\n ATTR_HTTP_RESPONSE_STATUS_CODE,\n ATTR_HTTP_ROUTE,\n ATTR_SERVICE_NAME\n};\n","import { extractArgumentNames } from '@forklaunch/common';\nimport { AnySchemaValidator } from '@forklaunch/validator';\nimport { ExpressLikeSchemaHandler } from '../types/apiDefinition.types';\nimport {\n Body,\n HeadersObject,\n ParamsObject,\n QueryObject,\n ResponsesObject\n} from '../types/contractDetails.types';\n\n/**\n * Type guard that checks if an object is an Express-like schema handler.\n * A schema handler is a function that takes up to 3 arguments (request, response, next)\n * and is used for middleware or route handlers in an Express-like application.\n *\n * @template SV - A type that extends AnySchemaValidator\n * @template P - The type of route parameters\n * @template ResBodyMap - The type of response body mapping\n * @template ReqBody - The type of request body\n * @template ReqQuery - The type of request query parameters\n * @template ReqHeaders - The type of request headers\n * @template ResHeaders - The type of response headers\n * @template LocalsObj - The type of locals object\n * @template BaseRequest - The base request type\n * @template BaseResponse - The base response type\n * @template NextFunction - The type of next function\n * @param {unknown} middleware - The object to check\n * @returns {boolean} A type predicate indicating whether the object is an ExpressLikeSchemaHandler\n *\n * @example\n * ```ts\n * if (isExpressLikeSchemaHandler(middleware)) {\n * // middleware is now typed as ExpressLikeSchemaHandler\n * middleware(req, res, next);\n * }\n * ```\n */\nexport function isExpressLikeSchemaHandler<\n SV extends AnySchemaValidator,\n P extends ParamsObject<SV>,\n ResBodyMap extends ResponsesObject<SV>,\n ReqBody extends Body<SV>,\n ReqQuery extends QueryObject<SV>,\n ReqHeaders extends HeadersObject<SV>,\n ResHeaders extends HeadersObject<SV>,\n LocalsObj extends Record<string, unknown>,\n BaseRequest,\n BaseResponse,\n NextFunction\n>(\n middleware: unknown\n): middleware is ExpressLikeSchemaHandler<\n SV,\n P,\n ResBodyMap,\n ReqBody,\n ReqQuery,\n ReqHeaders,\n ResHeaders,\n LocalsObj,\n BaseRequest,\n BaseResponse,\n NextFunction\n> {\n const extractedArgumentNames =\n typeof middleware === 'function' &&\n new Set(\n extractArgumentNames(middleware).map((argumentName) =>\n argumentName.toLowerCase()\n )\n );\n return extractedArgumentNames && extractedArgumentNames.size <= 3;\n}\n","import { AnySchemaValidator } from '@forklaunch/validator';\nimport { jwtVerify } from 'jose';\nimport { ParsedQs } from 'qs';\nimport {\n ForklaunchNextFunction,\n ForklaunchRequest,\n ForklaunchResponse,\n MapParamsSchema,\n MapReqBodySchema,\n MapReqHeadersSchema,\n MapReqQuerySchema,\n MapResBodyMapSchema,\n MapResHeadersSchema,\n ResolvedForklaunchRequest\n} from '../../types/apiDefinition.types';\nimport {\n AuthMethods,\n Body,\n HeadersObject,\n ParamsDictionary,\n ParamsObject,\n QueryObject,\n ResponsesObject\n} from '../../types/contractDetails.types';\n\nconst invalidAuthorizationTokenFormat = [\n 401,\n 'Invalid Authorization token format.'\n] as const;\nconst invalidAuthorizationSubject = [\n 403,\n 'Invalid Authorization subject.'\n] as const;\nconst invalidAuthorizationTokenPermissions = [\n 403,\n 'Invalid Authorization permissions.'\n] as const;\nconst invalidAuthorizationTokenRoles = [\n 403,\n 'Invalid Authorization roles.'\n] as const;\nconst invalidAuthorizationToken = [\n 403,\n 'Invalid Authorization token.'\n] as const;\nconst invalidAuthorizationLogin = [\n 403,\n 'Invalid Authorization login.'\n] as const;\n\n/**\n * Checks the authorization token for validity.\n *\n * @param {AuthMethod} [authorizationMethod] - The method of authorization.\n * @param {string} [authorizationToken] - The authorization string.\n * @returns {Promise<[401 | 403, string] | string | undefined>} - The result of the authorization check.\n */\nasync function checkAuthorizationToken<\n SV extends AnySchemaValidator,\n P extends ParamsDictionary,\n ReqBody extends Record<string, unknown>,\n ReqQuery extends ParsedQs,\n ReqHeaders extends Record<string, string>,\n BaseRequest\n>(\n authorizationMethod: AuthMethods<\n SV,\n P,\n ReqBody,\n ReqQuery,\n ReqHeaders,\n BaseRequest\n >,\n authorizationToken?: string,\n req?: ResolvedForklaunchRequest<\n SV,\n P,\n ReqBody,\n ReqQuery,\n ReqHeaders,\n BaseRequest\n >\n): Promise<readonly [401 | 403 | 500, string] | undefined> {\n if (authorizationToken == null) {\n return [401, 'No Authorization token provided.'];\n }\n\n const [tokenPrefix, token] = authorizationToken.split(' ');\n\n let resourceId;\n\n switch (authorizationMethod.method) {\n case 'jwt': {\n if (tokenPrefix !== 'Bearer') {\n return invalidAuthorizationTokenFormat;\n }\n\n try {\n const decodedJwt = await jwtVerify(\n token,\n new TextEncoder().encode(\n // TODO: Check this at application startup if there is any route with jwt checking\n process.env.JWT_SECRET\n )\n );\n\n if (!decodedJwt.payload.sub) {\n return invalidAuthorizationSubject;\n }\n\n resourceId = decodedJwt.payload.sub;\n } catch (error) {\n (\n req as ForklaunchRequest<SV, P, ReqBody, ReqQuery, ReqHeaders>\n ).openTelemetryCollector.error(error);\n return invalidAuthorizationToken;\n }\n\n break;\n }\n case 'basic': {\n if (authorizationToken !== 'Basic') {\n return invalidAuthorizationTokenFormat;\n }\n\n const [username, password] = Buffer.from(token, 'base64')\n .toString('utf-8')\n .split(':');\n\n if (!username || !password) {\n return invalidAuthorizationTokenFormat;\n }\n\n if (!authorizationMethod.login(username, password)) {\n return invalidAuthorizationLogin;\n }\n\n resourceId = username;\n break;\n }\n case 'other':\n if (tokenPrefix !== authorizationMethod.tokenPrefix) {\n return invalidAuthorizationTokenFormat;\n }\n\n resourceId = authorizationMethod.decodeResource(token);\n\n break;\n }\n\n if (\n authorizationMethod.allowedPermissions ||\n authorizationMethod.forbiddenPermissions\n ) {\n if (!authorizationMethod.mapPermissions) {\n return [500, 'No permission mapping function provided.'];\n }\n\n const resourcePermissions = await authorizationMethod.mapPermissions(\n resourceId,\n req\n );\n\n if (authorizationMethod.allowedPermissions) {\n if (\n resourcePermissions.intersection(authorizationMethod.allowedPermissions)\n .size === 0\n ) {\n return invalidAuthorizationTokenPermissions;\n }\n }\n\n if (authorizationMethod.forbiddenPermissions) {\n if (\n resourcePermissions.intersection(\n authorizationMethod.forbiddenPermissions\n ).size !== 0\n ) {\n return invalidAuthorizationTokenPermissions;\n }\n }\n }\n\n if (authorizationMethod.allowedRoles || authorizationMethod.forbiddenRoles) {\n if (!authorizationMethod.mapRoles) {\n return [500, 'No role mapping function provided.'];\n }\n\n const resourceRoles = await authorizationMethod.mapRoles(resourceId, req);\n\n if (authorizationMethod.allowedRoles) {\n if (\n resourceRoles.intersection(authorizationMethod.allowedRoles).size === 0\n ) {\n return invalidAuthorizationTokenRoles;\n }\n }\n\n if (authorizationMethod.forbiddenRoles) {\n if (\n resourceRoles.intersection(authorizationMethod.forbiddenRoles).size !==\n 0\n ) {\n return invalidAuthorizationTokenRoles;\n }\n }\n }\n\n return [401, 'Invalid Authorization method.'];\n}\n\n/**\n * Middleware to parse request authorization.\n *\n * @template SV - A type that extends AnySchemaValidator.\n * @template Request - A type that extends ForklaunchRequest.\n * @template Response - A type that extends ForklaunchResponse.\n * @template NextFunction - A type that extends ForklaunchNextFunction.\n * @param {Request} req - The request object.\n * @param {Response} res - The response object.\n * @param {NextFunction} [next] - The next middleware function.\n */\nexport async function parseRequestAuth<\n SV extends AnySchemaValidator,\n P extends ParamsObject<SV>,\n ResBodyMap extends ResponsesObject<SV>,\n ReqBody extends Body<SV>,\n ReqQuery extends QueryObject<SV>,\n ReqHeaders extends HeadersObject<SV>,\n ResHeaders extends HeadersObject<SV>,\n LocalsObj extends Record<string, unknown>\n>(\n req: ForklaunchRequest<\n SV,\n MapParamsSchema<SV, P>,\n MapReqBodySchema<SV, ReqBody>,\n MapReqQuerySchema<SV, ReqQuery>,\n MapReqHeadersSchema<SV, ReqHeaders>\n >,\n res: ForklaunchResponse<\n unknown,\n MapResBodyMapSchema<SV, ResBodyMap>,\n MapResHeadersSchema<SV, ResHeaders>,\n LocalsObj\n >,\n next?: ForklaunchNextFunction\n) {\n const auth = req.contractDetails.auth as AuthMethods<\n SV,\n MapParamsSchema<SV, P>,\n MapReqBodySchema<SV, ReqBody>,\n MapReqQuerySchema<SV, ReqQuery>,\n MapReqHeadersSchema<SV, ReqHeaders>,\n unknown\n >;\n\n if (auth) {\n const [error, message] =\n (await checkAuthorizationToken<\n SV,\n MapParamsSchema<SV, P>,\n MapReqBodySchema<SV, ReqBody>,\n MapReqQuerySchema<SV, ReqQuery>,\n MapReqHeadersSchema<SV, ReqHeaders>,\n unknown\n >(\n auth,\n req.headers[\n (auth.method === 'other' ? auth.headerName : undefined) ??\n 'Authorization'\n ],\n req\n )) ?? [];\n if (error != null) {\n res.type('text/plain');\n res.status(error).send(message as never);\n next?.(new Error(message));\n }\n }\n\n next?.();\n}\n","// This is a simple function that returns the value of an environment variable.\n// It casts a potentially undefined value to a string, since it will be validated in order to be bootstrapped.\n\nexport function getEnvVar(name: string): string {\n const value = process.env[name];\n return value as string;\n}\n","import { HyperExpressInstrumentation } from '@forklaunch/opentelemetry-instrumentation-hyper-express';\nimport {\n Counter,\n Gauge,\n Histogram,\n metrics,\n ObservableCounter,\n ObservableGauge,\n ObservableUpDownCounter,\n UpDownCounter\n} from '@opentelemetry/api';\nimport { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';\nimport { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';\nimport { ExpressInstrumentation } from '@opentelemetry/instrumentation-express';\nimport { HttpInstrumentation } from '@opentelemetry/instrumentation-http';\nimport { resourceFromAttributes } from '@opentelemetry/resources';\nimport { BatchLogRecordProcessor } from '@opentelemetry/sdk-logs';\nimport { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';\nimport { NodeSDK } from '@opentelemetry/sdk-node';\nimport {\n ATTR_HTTP_REQUEST_METHOD,\n ATTR_HTTP_RESPONSE_STATUS_CODE,\n ATTR_HTTP_ROUTE,\n ATTR_SERVICE_NAME\n} from '@opentelemetry/semantic-conventions';\nimport dotenv from 'dotenv';\nimport { LevelWithSilent, LevelWithSilentOrString } from 'pino';\nimport { v4 } from 'uuid';\nimport { getEnvVar } from '../../services/getEnvVar';\nimport { isForklaunchRequest } from '../guards/isForklaunchRequest';\nimport {\n LogFn,\n LoggerMeta,\n MetricsDefinition,\n MetricType\n} from '../types/openTelemetryCollector.types';\nimport { ATTR_API_NAME, ATTR_CORRELATION_ID } from './constants';\nimport { logger } from './pinoLogger';\n\nexport class OpenTelemetryCollector<\n AppliedMetricsDefinition extends MetricsDefinition\n> {\n private readonly uuid = v4();\n private readonly logger;\n private readonly metrics: Record<\n keyof AppliedMetricsDefinition,\n | Counter\n | Gauge\n | Histogram\n | UpDownCounter\n | ObservableCounter\n | ObservableGauge\n | ObservableUpDownCounter\n >;\n\n // scoped creation and create this in middleware when api execute. Also add correlation id\n constructor(\n private readonly serviceName: string,\n level?: LevelWithSilentOrString,\n metricDefinitions?: AppliedMetricsDefinition\n ) {\n this.logger = logger(level || 'info');\n\n this.metrics = {} as Record<\n keyof AppliedMetricsDefinition,\n MetricType<AppliedMetricsDefinition[keyof AppliedMetricsDefinition]>\n >;\n\n for (const [metricId, metricType] of Object.entries(\n metricDefinitions ?? {}\n )) {\n switch (metricType) {\n case 'counter':\n this.metrics[metricId as keyof AppliedMetricsDefinition] = metrics\n .getMeter(this.serviceName)\n .createCounter(metricId);\n break;\n case 'gauge':\n this.metrics[metricId as keyof AppliedMetricsDefinition] = metrics\n .getMeter(this.serviceName)\n .createGauge(metricId);\n break;\n case 'histogram':\n this.metrics[metricId as keyof AppliedMetricsDefinition] = metrics\n .getMeter(this.serviceName)\n .createHistogram(metricId);\n break;\n case 'upDownCounter':\n this.metrics[metricId as keyof AppliedMetricsDefinition] = metrics\n .getMeter(this.serviceName)\n .createUpDownCounter(metricId);\n break;\n case 'observableCounter':\n this.metrics[metricId as keyof AppliedMetricsDefinition] = metrics\n .getMeter(this.serviceName)\n .createObservableCounter(metricId);\n break;\n case 'observableGauge':\n this.metrics[metricId as keyof AppliedMetricsDefinition] = metrics\n .getMeter(this.serviceName)\n .createObservableGauge(metricId);\n break;\n case 'observableUpDownCounter':\n this.metrics[metricId as keyof AppliedMetricsDefinition] = metrics\n .getMeter(this.serviceName)\n .createObservableUpDownCounter(metricId);\n break;\n }\n }\n\n this.log(\n 'info',\n 'OpenTelemetry Collector (Traces + Logs + Metrics) started'\n );\n }\n\n log(level: LevelWithSilent, ...args: (string | unknown | LoggerMeta)[]) {\n this.logger.log(level, ...args);\n }\n\n info: LogFn = (\n msg: string | unknown | LoggerMeta,\n ...args: (string | unknown | LoggerMeta)[]\n ) => {\n this.logger.log('info', msg, ...args);\n };\n\n error: LogFn = (\n msg: string | unknown | LoggerMeta,\n ...args: (string | unknown | LoggerMeta)[]\n ) => {\n this.logger.log('error', msg, ...args);\n };\n\n warn: LogFn = (\n msg: string | unknown | LoggerMeta,\n ...args: (string | unknown | LoggerMeta)[]\n ) => {\n this.logger.log('warn', msg, ...args);\n };\n\n debug: LogFn = (\n msg: string | unknown | LoggerMeta,\n ...args: (string | unknown | LoggerMeta)[]\n ) => {\n this.logger.log('debug', msg, ...args);\n };\n\n trace: LogFn = (\n msg: string | unknown | LoggerMeta,\n ...args: (string | unknown | LoggerMeta)[]\n ) => {\n this.logger.log('trace', msg, ...args);\n };\n\n getMetric<T extends keyof AppliedMetricsDefinition>(\n metricId: T\n ): MetricType<AppliedMetricsDefinition[T]> {\n return this.metrics[metricId] as MetricType<AppliedMetricsDefinition[T]>;\n }\n}\n\ndotenv.config({ path: getEnvVar('ENV_FILE_PATH') });\n\nnew NodeSDK({\n resource: resourceFromAttributes({\n [ATTR_SERVICE_NAME]: getEnvVar('OTEL_SERVICE_NAME')\n }),\n traceExporter: new OTLPTraceExporter({\n url: `${\n getEnvVar('OTEL_EXPORTER_OTLP_ENDPOINT') ?? 'http://localhost:4318'\n }/v1/traces`\n }),\n metricReader: new PeriodicExportingMetricReader({\n exporter: new OTLPMetricExporter({\n url: `${\n getEnvVar('OTEL_EXPORTER_OTLP_ENDPOINT') ?? 'http://localhost:4318'\n }/v1/metrics`\n }),\n exportIntervalMillis: 5000\n }),\n logRecordProcessors: [\n new BatchLogRecordProcessor(\n new OTLPLogExporter({\n url: `${\n getEnvVar('OTEL_EXPORTER_OTLP_ENDPOINT') ?? 'http://localhost:4318'\n }/v1/logs`\n })\n )\n ],\n instrumentations: [\n new HttpInstrumentation({\n applyCustomAttributesOnSpan: (span, request) => {\n span.setAttribute(\n 'service.name',\n getEnvVar('OTEL_SERVICE_NAME') ?? 'unknown'\n );\n if (isForklaunchRequest(request)) {\n span.setAttribute('api.name', request.contractDetails?.name);\n }\n }\n }),\n new ExpressInstrumentation(),\n new HyperExpressInstrumentation()\n ]\n}).start();\n\n// begin static metrics -- these have to be in here in order to instantiate these after the SDK is instantiated\nexport const httpRequestsTotalCounter = metrics\n .getMeter(getEnvVar('OTEL_SERVICE_NAME') || 'unknown')\n .createCounter<{\n [ATTR_SERVICE_NAME]: string;\n [ATTR_API_NAME]: string;\n [ATTR_CORRELATION_ID]: string;\n [ATTR_HTTP_REQUEST_METHOD]: string;\n [ATTR_HTTP_ROUTE]: string;\n [ATTR_HTTP_RESPONSE_STATUS_CODE]: number;\n }>('http_requests_total', {\n description: 'Number of HTTP requests'\n });\n\nexport const httpServerDurationHistogram = metrics\n .getMeter(getEnvVar('OTEL_SERVICE_NAME') || 'unknown')\n .createHistogram<{\n [ATTR_SERVICE_NAME]: string;\n [ATTR_API_NAME]: string;\n [ATTR_HTTP_REQUEST_METHOD]: string;\n [ATTR_HTTP_ROUTE]: string;\n [ATTR_HTTP_RESPONSE_STATUS_CODE]: number;\n }>('http_server_duration', {\n description: 'Duration of HTTP server requests',\n unit: 's'\n });\n","import { AnySchemaValidator } from '@forklaunch/validator';\nimport { ParsedQs } from 'qs';\nimport { ForklaunchRequest } from '../types/apiDefinition.types';\nimport { ParamsDictionary } from '../types/contractDetails.types';\n\n/**\n * Type guard that checks if an object is a Forklaunch request.\n * A Forklaunch request is an object that contains contract details and follows the Forklaunch request structure.\n *\n * @template SV - A type that extends AnySchemaValidator\n * @template P - The type of route parameters that extends ParamsDictionary\n * @template ReqBody - The type of request body that extends Record<string, unknown>\n * @template ReqQuery - The type of request query parameters that extends ParsedQs\n * @template ReqHeaders - The type of request headers that extends Record<string, string>\n * @param {unknown} request - The object to check\n * @returns {boolean} A type predicate indicating whether the object is a ForklaunchRequest\n *\n * @example\n * ```ts\n * if (isForklaunchRequest(request)) {\n * // request is now typed as ForklaunchRequest\n * const { contractDetails } = request;\n * }\n * ```\n */\nexport function isForklaunchRequest<\n SV extends AnySchemaValidator,\n P extends ParamsDictionary,\n ReqBody extends Record<string, unknown>,\n ReqQuery extends ParsedQs,\n ReqHeaders extends Record<string, string>\n>(\n request: unknown\n): request is ForklaunchRequest<SV, P, ReqBody, ReqQuery, ReqHeaders> {\n return (\n request != null &&\n typeof request === 'object' &&\n 'contractDetails' in request\n );\n}\n","import { isNever, safeStringify } from '@forklaunch/common';\nimport { trace } from '@opentelemetry/api';\nimport { AnyValueMap, logs } from '@opentelemetry/api-logs';\nimport pino, { LevelWithSilent, LevelWithSilentOrString, Logger } from 'pino';\nimport PinoPretty from 'pino-pretty';\nimport { isLoggerMeta } from '../guards/isLoggerMeta';\nimport { LogFn, LoggerMeta } from '../types/openTelemetryCollector.types';\n\nexport function meta(meta: Record<string, unknown>) {\n return meta as LoggerMeta;\n}\n\nfunction mapSeverity(level: LevelWithSilent) {\n switch (level) {\n case 'silent':\n return 0;\n case 'trace':\n return 1;\n case 'debug':\n return 5;\n case 'info':\n return 9;\n case 'warn':\n return 13;\n case 'error':\n return 17;\n case 'fatal':\n return 21;\n default:\n isNever(level);\n return 0;\n }\n}\n\nclass PinoLogger {\n private pinoLogger: Logger;\n private meta: AnyValueMap;\n private prettyPrinter = PinoPretty.prettyFactory({\n colorize: true\n });\n\n constructor(level: LevelWithSilentOrString, meta: AnyValueMap = {}) {\n this.pinoLogger = pino({\n level: level || 'info',\n formatters: {\n level(label) {\n return { level: label };\n }\n },\n timestamp: pino.stdTimeFunctions.isoTime,\n transport: {\n target: 'pino-pretty',\n options: { colorize: true }\n }\n });\n this.meta = meta;\n }\n\n log(level: LevelWithSilent, ...args: (string | unknown | LoggerMeta)[]) {\n let meta: AnyValueMap = {};\n\n const filteredArgs = args\n .filter((arg) => {\n if (isLoggerMeta(arg)) {\n Object.assign(meta, arg);\n return false;\n }\n return true;\n })\n .map(safeStringify) as Parameters<pino.LogFn>;\n\n const activeSpan = trace.getActiveSpan();\n if (activeSpan) {\n const activeSpanContext = activeSpan.spanContext();\n meta.trace_id = activeSpanContext.traceId;\n meta.span_id = activeSpanContext.spanId;\n meta.trace_flags = activeSpanContext.traceFlags;\n meta = {\n // @ts-expect-error accessing private property\n ...activeSpan.attributes,\n ...meta\n };\n }\n\n meta = {\n 'api.name': 'none',\n 'correlation.id': 'none',\n ...meta\n };\n\n this.pinoLogger[level](...filteredArgs);\n logs.getLogger(process.env.OTEL_SERVICE_NAME ?? 'unknown').emit({\n severityText: level,\n severityNumber: mapSeverity(level),\n body: this.prettyPrinter(filteredArgs),\n attributes: { ...this.meta, ...meta }\n });\n }\n\n error: LogFn = (\n msg: string | unknown | LoggerMeta,\n ...args: (string | unknown | LoggerMeta)[]\n ) => this.log('error', msg, ...args);\n\n info: LogFn = (\n msg: string | unknown | LoggerMeta,\n ...args: (string | unknown | LoggerMeta)[]\n ) => this.log('info', msg, ...args);\n\n debug: LogFn = (\n msg: string | unknown | LoggerMeta,\n ...args: (string | unknown | LoggerMeta)[]\n ) => this.log('debug', msg, ...args);\n warn: LogFn = (\n msg: string | unknown | LoggerMeta,\n ...args: (string | unknown | LoggerMeta)[]\n ) => this.log('warn', msg, ...args);\n\n trace: LogFn = (\n msg: string | unknown | LoggerMeta,\n ...args: (string | unknown | LoggerMeta)[]\n ) => this.log('trace', msg, ...args);\n\n child(meta: AnyValueMap = {}) {\n return new PinoLogger(this.pinoLogger.level, { ...this.meta, ...meta });\n }\n\n getBaseLogger() {\n return this.pinoLogger;\n }\n}\n\nexport function logger(level: LevelWithSilentOrString, meta: AnyValueMap = {}) {\n return new PinoLogger(level, meta);\n}\n","import {\n AnySchemaValidator,\n prettyPrintParseErrors,\n SchemaValidator\n} from '@forklaunch/validator';\nimport { ParsedQs } from 'qs';\nimport { hasSend } from '../../guards/hasSend';\nimport { isRequestShape } from '../../guards/isRequestShape';\nimport {\n ForklaunchNextFunction,\n ForklaunchRequest,\n ForklaunchResponse\n} from '../../types/apiDefinition.types';\nimport { ParamsDictionary } from '../../types/contractDetails.types';\n\n/**\n * Pre-handler function to parse and validate input.\n *\n * @template SV - A type that extends AnySchemaValidator.\n * @template Request - A type that extends ForklaunchRequest.\n * @template Response - A type that extends ForklaunchResponse.\n * @template NextFunction - A type that extends ForklaunchNextFunction.\n * @param {Request} req - The request object.\n * @param {Response} res - The response object.\n * @param {NextFunction} [next] - The next middleware function.\n */\nexport function parse<\n SV extends AnySchemaValidator,\n P extends ParamsDictionary,\n ResBodyMap extends Record<number, unknown>,\n ReqBody extends Record<string, unknown>,\n ReqQuery extends ParsedQs,\n ReqHeaders extends Record<string, string>,\n ResHeaders extends Record<string, string>,\n LocalsObj extends Record<string, unknown>\n>(\n req: ForklaunchRequest<SV, P, ReqBody, ReqQuery, ReqHeaders>,\n res: ForklaunchResponse<unknown, ResBodyMap, ResHeaders, LocalsObj>,\n next?: ForklaunchNextFunction\n) {\n const request = {\n params: req.params,\n query: req.query,\n headers: req.headers,\n body: req.body\n };\n\n const parsedRequest = (req.schemaValidator as SchemaValidator).parse(\n req.requestSchema,\n request\n );\n\n if (\n parsedRequest.ok &&\n isRequestShape<P, ReqHeaders, ReqQuery, ReqBody>(parsedRequest.value)\n ) {\n req.body = parsedRequest.value.body;\n req.params = parsedRequest.value.params;\n Object.defineProperty(req, 'query', {\n value: parsedRequest.value.query,\n writable: false,\n enumerable: true,\n configurable: false\n });\n req.headers = parsedRequest.value.headers ?? {};\n }\n if (!parsedRequest.ok) {\n switch (req.contractDetails.options?.requestValidation) {\n default:\n case 'error':\n res.type('application/json');\n res.status(400);\n if (hasSend(res)) {\n res.send(\n `${prettyPrintParseErrors(\n parsedRequest.errors,\n 'Request'\n )}\\n\\nCorrelation id: ${\n req.context.correlationId ?? 'No correlation ID'\n }`\n );\n } else {\n next?.(new Error('Request is not sendable.'));\n }\n return;\n case 'warning':\n req.openTelemetryCollector.warn(\n prettyPrintParseErrors(parsedRequest.errors, 'Request')\n );\n break;\n case 'none':\n break;\n }\n }\n\n next?.();\n}\n","import {\n AnySchemaValidator,\n prettyPrintParseErrors,\n SchemaValidator\n} from '@forklaunch/validator';\nimport { ParsedQs } from 'qs';\nimport { hasSend } from '../../guards/hasSend';\nimport {\n ForklaunchNextFunction,\n ForklaunchRequest,\n ForklaunchResHeaders,\n ForklaunchResponse\n} from '../../types/apiDefinition.types';\nimport { ParamsDictionary } from '../../types/contractDetails.types';\n\n/**\n * Middleware to parse and validate the response according to the provided schema.\n *\n * This function validates the response against a schema provided by the request's schema validator.\n * If the response does not conform to the schema, the behavior is determined by the `responseValidation`\n * option in `req.contractDetails.options`:\n * - `'error'` (default): Calls `next` with an error.\n * - `'warning'`: Logs a warning to the console.\n * - `'none'`: Silently continues without action.\n *\n * @template SV - The type of the schema validator used in the request.\n * @template P - The type of the parameters dictionary used in the request.\n * @template ResBodyMap - A record type mapping status codes to response body types.\n * @template ReqBody - The type of the request body.\n * @template ReqQuery - The type of the parsed query string.\n * @template ReqHeaders - The type of the request headers.\n * @template ResHeaders - The type of the response headers, extended from `ForklaunchResHeaders`.\n * @template LocalsObj - The type of the locals object in the response.\n *\n * @param {ForklaunchRequest<SV, P, ReqBody, ReqQuery, ReqHeaders>} req - The request object, containing the schema validator and other request data.\n * @param {ForklaunchResponse<ResBodyMap, ForklaunchResHeaders & ResHeaders, LocalsObj>} res - The response object, including headers and body data.\n * @param {ForklaunchNextFunction} [next] - The next middleware function to be called. If not provided, the function will return after processing.\n *\n * @returns {void} This function does not return a value.\n */\nexport function parse<\n SV extends AnySchemaValidator,\n P extends ParamsDictionary,\n ResBodyMap extends Record<number, unknown>,\n ReqBody extends Record<string, unknown>,\n ReqQuery extends ParsedQs,\n ReqHeaders extends Record<string, string>,\n ResHeaders extends Record<string, string>,\n LocalsObj extends Record<string, unknown>\n>(\n req: ForklaunchRequest<SV, P, ReqBody, ReqQuery, ReqHeaders>,\n res: ForklaunchResponse<\n unknown,\n ResBodyMap,\n ForklaunchResHeaders & ResHeaders,\n LocalsObj\n >,\n next?: ForklaunchNextFunction\n) {\n const { headers, responses } = res.responseSchemas;\n\n const statusCode = Number(res.statusCode);\n\n const parsedResponse = (req.schemaValidator as SchemaValidator).parse(\n responses?.[statusCode],\n res.bodyData\n );\n\n const parsedHeaders = (req.schemaValidator as SchemaValidator).parse(\n headers ?? req.schemaValidator.unknown,\n res.getHeaders()\n );\n const parseErrors: string[] = [];\n if (!parsedHeaders.ok) {\n const headerErrors = prettyPrintParseErrors(parsedHeaders.errors, 'Header');\n if (headerErrors) {\n parseErrors.push(headerErrors);\n }\n }\n\n if (!parsedResponse.ok) {\n const responseErrors = prettyPrintParseErrors(\n parsedResponse.errors,\n 'Response'\n );\n if (responseErrors) {\n parseErrors.push(responseErrors);\n }\n }\n\n if (parseErrors.length > 0) {\n switch (req.contractDetails.options?.responseValidation) {\n default:\n case 'error':\n res.type('text/plain');\n res.status(500);\n if (hasSend(res)) {\n res.send(\n `Invalid response:\\n${parseErrors.join(\n '\\n\\n'\n )}\\n\\nCorrelation id: ${\n req.context.correlationId ?? 'No correlation ID'\n }`\n );\n } else {\n next?.(new Error('Response is not sendable.'));\n }\n return;\n case 'warning':\n req.openTelemetryCollector.warn(\n `Invalid response:\\n${parseErrors.join('\\n\\n')}`\n );\n break;\n case 'none':\n break;\n }\n }\n next?.();\n}\n","import { AnySchemaValidator } from '@forklaunch/validator';\nimport { ParsedQs } from 'qs';\nimport { parse } from './parse.middleware';\n\nimport {\n isAsyncGenerator,\n isNever,\n isNodeJsWriteableStream,\n isRecord,\n readableStreamToAsyncIterable,\n safeStringify\n} from '@forklaunch/common';\nimport { Readable, Transform } from 'stream';\nimport { discriminateResponseBodies } from '../../router/discriminateBody';\nimport { logger } from '../../telemetry/pinoLogger';\nimport { recordMetric } from '../../telemetry/recordMetric';\nimport {\n ForklaunchRequest,\n ForklaunchResHeaders,\n ForklaunchResponse,\n ForklaunchSendableData,\n ForklaunchStatusResponse\n} from '../../types/apiDefinition.types';\nimport { ParamsDictionary } from '../../types/contractDetails.types';\n\n/**\n * Enhances the Express-like `send` method with additional logic for response handling and validation.\n *\n * This function intercepts the `send` method to provide custom behavior, including response validation\n * through the `parseResponse` middleware. If the response status is 404, it sends a \"Not Found\" message.\n * If the response validation fails, it sends a parsing error message. Otherwise, it calls the original `send`\n * method with the provided data.\n *\n * @template SV - The type of the schema validator used in the request.\n * @template P - The type of the parameters dictionary used in the request.\n * @template ResBodyMap - A record type mapping status codes to response body types.\n * @template ReqBody - The type of the request body.\n * @template ReqQuery - The type of the parsed query string.\n * @template ReqHeaders - The type of the request headers.\n * @template ResHeaders - The type of the response headers, extended from `ForklaunchResHeaders`.\n * @template LocalsObj - The type of the locals object in the response.\n *\n * @param {unknown} instance - The context (typically `this`) in which the original `send` method is called.\n * @param {ForklaunchRequest<SV, P, ReqBody, ReqQuery, ReqHeaders>} req - The request object, containing the schema validator and other request data.\n * @param {ForklaunchResponse<ResBodyMap, ForklaunchResHeaders & ResHeaders, LocalsObj>} res - The response object, including headers and body data.\n * @param {Function} originalSend - The original `send` method from the response object, to be called after custom logic.\n * @param {string | ArrayBuffer | ArrayBufferView | NodeJS.ReadableStream | null | undefined} data - The data to be sent as the response body.\n * @param {boolean} shouldEnrich - A flag indicating whether the response should be sent immediately.\n *\n * @returns {unknown} The return value of the original `send` method, typically the response itself.\n */\nexport function enrichExpressLikeSend<\n SV extends AnySchemaValidator,\n P extends ParamsDictionary,\n ResBodyMap extends Record<number, unknown>,\n ReqBody extends Record<string, unknown>,\n ReqQuery extends ParsedQs,\n ReqHeaders extends Record<string, string>,\n ResHeaders extends Record<string, string>,\n LocalsObj extends Record<string, unknown>\n>(\n instance: unknown,\n req: ForklaunchRequest<SV, P, ReqBody, ReqQuery, ReqHeaders>,\n res: ForklaunchResponse<\n unknown,\n ResBodyMap,\n ForklaunchResHeaders & ResHeaders,\n LocalsObj\n >,\n originalOperation:\n | ForklaunchStatusResponse<ForklaunchSendableData>['json']\n | ForklaunchStatusResponse<ForklaunchSendableData>['jsonp'],\n originalSend: ForklaunchStatusResponse<ForklaunchSendableData>['send'],\n data:\n | ForklaunchSendableData\n | File\n | Blob\n | AsyncGenerator<Record<string, unknown>>\n | null\n | undefined,\n shouldEnrich: boolean\n) {\n let errorSent = false;\n\n if (data == null) {\n originalSend.call(instance, data);\n return;\n }\n\n if (res.statusCode === 404) {\n res.type('text/plain');\n res.status(404);\n logger('error').error('Not Found');\n originalSend.call(instance, 'Not Found');\n errorSent = true;\n }\n\n const responseBodies = discriminateResponseBodies(\n req.schemaValidator,\n req.contractDetails.responses\n );\n\n if (\n responseBodies != null &&\n responseBodies[Number(res.statusCode)] != null\n ) {\n res.type(responseBodies[Number(res.statusCode)].contentType);\n }\n\n if (data instanceof File || data instanceof Blob) {\n if (data instanceof File) {\n res.setHeader(\n 'Content-Disposition',\n `attachment; filename=\"${data.name}\"` as ResHeaders['Content-Disposition']\n );\n }\n if (isNodeJsWriteableStream(res)) {\n Readable.from(readableStreamToAsyncIterable(data.stream())).pipe(\n res as unknown as NodeJS.WritableStream\n );\n } else {\n res.type('text/plain');\n res.status(500);\n originalSend.call(instance, 'Not a NodeJS WritableStream');\n errorSent = true;\n }\n } else if (isAsyncGenerator(data)) {\n let firstPass = true;\n const transformer = new Transform({\n objectMode: true,\n transform(\n chunk: Record<string, unknown>,\n _encoding: unknown,\n callback: (error?: Error | null, data?: unknown) => void\n ) {\n if (firstPass) {\n res.bodyData = chunk;\n parse(req, res, (err?: unknown) => {\n if (err) {\n let errorString = (err as Error).message;\n if (res.locals.errorMessage) {\n errorString += `\\n------------------\\n${res.locals.errorMessage}`;\n }\n logger('error').error(errorString);\n res.type('text/plain');\n res.status(500);\n originalSend.call(instance, errorString);\n errorSent = true;\n callback(new Error(errorString));\n }\n });\n firstPass = false;\n }\n if (!errorSent) {\n let data = '';\n for (const [key, value] of Object.entries(chunk)) {\n data += `${key}: ${typeof value === 'string' ? value : safeStringify(value)}\\n`;\n }\n data += '\\n';\n callback(null, data);\n }\n }\n });\n if (isNodeJsWriteableStream(res)) {\n Readable.from(data).pipe(transformer).pipe(res);\n } else {\n res.type('text/plain');\n res.status(500);\n originalSend.call(instance, 'Not a NodeJS WritableStream');\n errorSent = true;\n }\n } else {\n const parserType = responseBodies?.[Number(res.statusCode)]?.parserType;\n\n res.bodyData = data;\n if (isRecord(data)) {\n switch (parserType) {\n case 'json':\n res.bodyData = 'json' in data ? data.json : data;\n break;\n case 'text':\n res.bodyData = 'text' in data ? data.text : data;\n break;\n case 'file':\n res.bodyData = 'file' in data ? data.file : data;\n break;\n case 'serverSentEvent':\n res.bodyData = 'event' in data ? data.event : data;\n break;\n case 'multipart':\n res.bodyData = 'multipart' in data ? data.multipart : data;\n break;\n case undefined:\n res.bodyData = data;\n break;\n default:\n isNever(parserType);\n res.bodyData = data;\n break;\n }\n }\n\n parse(req, res, (err?: unknown) => {\n if (err) {\n let errorString = (err as Error).message;\n if (res.locals.errorMessage) {\n errorString += `\\n------------------\\n${res.locals.errorMessage}`;\n }\n logger('error').error(errorString);\n res.type('text/plain');\n res.status(500);\n originalSend.call(instance, errorString);\n errorSent = true;\n }\n });\n\n if (!errorSent) {\n if (typeof data === 'string') {\n res.type('text/plain');\n originalSend.call(instance, data);\n } else if (!(data instanceof File)) {\n res.sent = true;\n originalOperation.call(instance, data);\n }\n }\n }\n\n if (shouldEnrich) {\n recordMetric<\n SV,\n P,\n ReqBody,\n ReqQuery,\n ResBodyMap,\n ReqHeaders,\n ForklaunchResHeaders & ResHeaders,\n LocalsObj\n >(req, res);\n }\n}\n","import { AnySchemaValidator } from '@forklaunch/validator';\nimport {\n ATTR_HTTP_REQUEST_METHOD,\n ATTR_HTTP_RESPONSE_STATUS_CODE,\n ATTR_HTTP_ROUTE,\n ATTR_SERVICE_NAME\n} from '@opentelemetry/semantic-conventions';\nimport { ParsedQs } from 'qs';\nimport { ForklaunchRequest, ForklaunchResponse, ParamsDictionary } from '..';\nimport { getEnvVar } from '../../services';\nimport { ATTR_API_NAME, ATTR_CORRELATION_ID } from './constants';\nimport { httpRequestsTotalCounter } from './openTelemetryCollector';\n\nexport function recordMetric<\n SV extends AnySchemaValidator,\n P extends ParamsDictionary,\n ReqBody extends Record<string, unknown>,\n ReqQuery extends ParsedQs,\n ResBodyMap extends Record<string, unknown>,\n ReqHeaders extends Record<string, string>,\n ResHeaders extends Record<string, string>,\n LocalsObj extends Record<string, unknown>\n>(\n req: ForklaunchRequest<SV, P, ReqBody, ReqQuery, ReqHeaders>,\n res: ForklaunchResponse<unknown, ResBodyMap, ResHeaders, LocalsObj>\n) {\n if (res.metricRecorded) {\n return;\n }\n\n httpRequestsTotalCounter.add(1, {\n [ATTR_SERVICE_NAME]: getEnvVar('OTEL_SERVICE_NAME'),\n [ATTR_API_NAME]: req.contractDetails?.name,\n [ATTR_CORRELATION_ID]: req.context.correlationId,\n [ATTR_HTTP_REQUEST_METHOD]: req.method,\n [ATTR_HTTP_ROUTE]: req.originalPath,\n [ATTR_HTTP_RESPONSE_STATUS_CODE]: Number(res.statusCode) || 0\n });\n res.metricRecorded = true;\n}\n","import { extractArgumentNames, isNever } from '@forklaunch/common';\nimport {\n AnySchemaValidator,\n IdiomaticSchema,\n ParseResult,\n prettyPrintParseErrors,\n SchemaValidator\n} from '@forklaunch/validator';\nimport { isConstructed } from './guards/isConstructed';\nimport { isConstructedSingleton } from './guards/isConstructedSingleton';\nimport { isConstructor } from './guards/isConstructor';\nimport {\n ConfigValidator,\n Constructed,\n ConstructedSingleton,\n Lifetime,\n ResolvedConfigValidator,\n SchemaConstructor,\n SchemaFunction,\n Singleton\n} from './types/configInjector.types';\n\nexport function createConfigInjector<\n SV extends AnySchemaValidator,\n CV extends ConfigValidator<SV>\n>(\n schemaValidator: SV,\n dependenciesDefinition: {\n [K in keyof CV]:\n | Singleton<\n CV[K],\n Omit<ResolvedConfigValidator<SV, CV>, K>,\n ResolvedConfigValidator<SV, CV>[K]\n >\n | Constructed<\n CV[K],\n Omit<ResolvedConfigValidator<SV, CV>, K>,\n ResolvedConfigValidator<SV, CV>[K]\n >;\n }\n) {\n return new ConfigInjector<SV, CV>(\n schemaValidator,\n dependenciesDefinition\n ).load();\n}\n\nexport class ConfigInjector<\n SV extends AnySchemaValidator,\n CV extends ConfigValidator<SV>\n> {\n instances: {\n [K in keyof CV]?: ResolvedConfigValidator<SV, CV>[K];\n } = {};\n\n readonly configShapes: CV;\n\n load(inheritedScopeInstances?: {\n [K in keyof CV]?: ResolvedConfigValidator<SV, CV>[K];\n }): this {\n for (const token in inheritedScopeInstances) {\n this.instances[token] = inheritedScopeInstances[token];\n }\n\n for (const token in this.dependenciesDefinition) {\n const definition = this.dependenciesDefinition[token];\n if (\n definition.lifetime === Lifetime.Singleton &&\n !this.instances[token]\n ) {\n if (\n isConstructedSingleton<\n CV[typeof token],\n Omit<ResolvedConfigValidator<SV, CV>, typeof token>,\n ResolvedConfigValidator<SV, CV>[typeof token]\n >(definition)\n ) {\n this.instances[token] = this.resolveInstance<typeof token>(\n token,\n definition\n );\n } else {\n this.instances[token] = definition.value;\n }\n }\n }\n return this;\n }\n\n private resolveInstance<T extends keyof CV>(\n token: T,\n definition:\n | ConstructedSingleton<\n CV[T],\n Omit<ResolvedConfigValidator<SV, CV>, T>,\n ResolvedConfigValidator<SV, CV>[T]\n >\n | Constructed<\n CV[T],\n Omit<ResolvedConfigValidator<SV, CV>, T>,\n ResolvedConfigValidator<SV, CV>[T]\n >,\n context?: Record<string, unknown>,\n resolutionPath: (keyof CV)[] = []\n ): ResolvedConfigValidator<SV, CV>[T] {\n const injectorArgument = extractArgumentNames(definition.factory)[0];\n // short circuit as no args\n if (!injectorArgument || injectorArgument === '_args') {\n return definition.factory(\n {} as Omit<ResolvedConfigValidator<SV, CV>, T>,\n this.resolve.bind(this),\n context ?? ({} as Record<string, unknown>)\n );\n }\n\n if (!injectorArgument.startsWith('{') || !injectorArgument.endsWith('}')) {\n throw new Error(\n `Invalid injector argument for ${String(\n token\n )}: ${injectorArgument}. Please use object destructuring syntax: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment.`\n );\n }\n const resolvedArguments = Object.fromEntries(\n injectorArgument\n .replace('{', '')\n .replace('}', '')\n .split(',')\n .map((arg) => arg.split(':')[0].trim())\n .map((arg) => {\n const newResolutionPath = [...resolutionPath, token];\n if (resolutionPath.includes(arg)) {\n throw new Error(\n `Circular dependency detected: ${newResolutionPath.join(\n ' -> '\n )} -> ${arg}`\n );\n }\n const resolvedArg = this.resolve(arg, context, newResolutionPath);\n return [arg, resolvedArg];\n })\n ) as unknown as Omit<ResolvedConfigValidator<SV, CV>, T>;\n return definition.factory(\n resolvedArguments,\n this.resolve.bind(this),\n context ?? ({} as Record<string, unknown>)\n );\n }\n\n constructor(\n private schemaValidator: SV,\n private dependenciesDefinition: {\n [K in keyof CV]: (\n | Singleton<\n CV[K],\n Omit<ResolvedConfigValidator<SV, CV>, K>,\n ResolvedConfigValidator<SV, CV>[K]\n >\n | Constructed<\n CV[K],\n Omit<ResolvedConfigValidator<SV, CV>, K>,\n ResolvedConfigValidator<SV, CV>[K]\n >\n ) & {\n type: CV[K];\n };\n }\n ) {\n this.configShapes = Object.entries(this.dependenciesDefinition).reduce(\n (acc, [key, { type }]) => ({\n ...acc,\n [key]: type\n }),\n {} as Record<keyof CV, CV[keyof CV]>\n ) as CV;\n }\n\n safeValidateConfigSingletons(): ParseResult<ValidConfigInjector<SV, CV>> {\n const validNonSchemaSingletons = Object.entries(this.configShapes).reduce<\n ParseResult<ResolvedConfigValidator<SV, CV>>\n >(\n (acc, [key, value]) => {\n if (\n this.dependenciesDefinition[key].lifetime === Lifetime.Singleton &&\n !(this.schemaValidator as SchemaValidator).isSchema<\n SchemaFunction<SV> | SchemaConstructor<SV> | IdiomaticSchema<SV>\n >(value) &&\n isConstructor(value)\n ) {\n if (!(this.instances[key] instanceof value)) {\n const expected = value.name;\n const receivedValue: unknown = this.instances[key];\n const received = isConstructed(receivedValue)\n ? receivedValue.constructor.name\n : typeof receivedValue;\n\n if (acc.ok) {\n acc = {\n ok: false,\n errors: []\n };\n }\n acc.errors?.push({\n message: `Expected ${expected}, received ${received}`,\n path: [key]\n });\n } else {\n if (acc.ok) {\n acc = {\n ok: true,\n value: {\n ...acc.value,\n [key]: this.instances[key]\n }\n };\n }\n }\n return acc;\n }\n return acc;\n },\n {\n ok: true,\n value: {} as ResolvedConfigValidator<SV, CV>\n }\n );\n\n const singletons = Object.fromEntries(\n Object.entries(this.configShapes).filter(\n ([key, value]) =>\n this.dependenciesDefinition[key].lifetime === Lifetime.Singleton &&\n (this.schemaValidator as SchemaValidator).isSchema(value)\n )\n );\n const schemaSingletonParseResult = (\n this.schemaValidator as SchemaValidator\n ).parse(\n (this.schemaValidator as SchemaValidator).schemify(singletons),\n Object.fromEntries(\n Object.keys(singletons).map((key) => {\n const dependency = this.dependenciesDefinition[key];\n return [\n key,\n dependency.lifetime === Lifetime.Singleton\n ? this.instances[key]\n : undefined\n ];\n })\n )\n );\n\n const configKeys = Object.keys(this.configShapes);\n\n return validNonSchemaSingletons.ok && schemaSingletonParseResult.ok\n ? {\n ok: true as const,\n value: new ValidConfigInjector<SV, CV>(\n this.schemaValidator,\n this.dependenciesDefinition\n ).load({ ...this.instances })\n }\n : {\n ok: false as const,\n errors: [\n ...(!validNonSchemaSingletons.ok && validNonSchemaSingletons.errors\n ? validNonSchemaSingletons.errors\n : []),\n ...(!schemaSingletonParseResult.ok &&\n schemaSingletonParseResult.errors\n ? schemaSingletonParseResult.errors\n : [])\n ].sort(\n (a, b) =>\n configKeys.indexOf(a.path[0]) - configKeys.indexOf(b.path[0])\n )\n };\n }\n\n validateConfigSingletons(configName: string): ValidConfigInjector<SV, CV> {\n const safeValidateResult = this.safeValidateConfigSingletons();\n\n if (safeValidateResult.ok) {\n return safeValidateResult.value;\n }\n\n throw new Error(\n prettyPrintParseErrors(safeValidateResult.errors, configName)\n );\n }\n\n resolve<T extends keyof CV>(\n token: T,\n context?: Record<string, unknown>,\n resolutionPath: (keyof CV)[] = []\n ): ResolvedConfigValidator<SV, CV>[T] {\n const instance = this.instances[token];\n if (!instance) {\n const definition = this.dependenciesDefinition[token];\n\n if (!definition) {\n throw new Error(`Unable to resolve dependency ${String(token)}`);\n }\n\n switch (definition.lifetime) {\n case Lifetime.Singleton: {\n if (\n isConstructedSingleton<\n CV[T],\n Omit<ResolvedConfigValidator<SV, CV>, T>,\n ResolvedConfigValidator<SV, CV>[T]\n >(definition) &&\n !this.instances[token]\n ) {\n this.instances[token] = this.resolveInstance<T>(\n token,\n definition,\n context,\n resolutionPath\n );\n }\n return this.instances[token] as ResolvedConfigValidator<SV, CV>[T];\n }\n case Lifetime.Scoped: {\n if (\n !isConstructed<\n CV[T],\n Omit<ResolvedConfigValidator<SV, CV>, T>,\n ResolvedConfigValidator<SV, CV>[T]\n >(definition)\n ) {\n throw new Error(\n `Invalid dependency definition for ${String(token)}`\n );\n }\n\n const scopedInstance = this.resolveInstance<T>(\n token,\n definition,\n context,\n resolutionPath\n );\n this.instances[token] = scopedInstance;\n return scopedInstance;\n }\n case Lifetime.Transient: {\n if (\n !isConstructed<\n CV[T],\n Omit<ResolvedConfigValidator<SV, CV>, T>,\n ResolvedConfigValidator<SV, CV>[T]\n >(definition)\n ) {\n throw new Error(\n `Invalid dependency definition for ${String(token)}`\n );\n }\n\n return this.resolveInstance<T>(\n token,\n definition,\n context,\n resolutionPath\n );\n }\n default: {\n isNever(definition);\n throw new Error(\n `Unable to resolve lifetime for dependency ${String(\n token\n )}, ${resolutionPath}`\n );\n }\n }\n } else {\n return instance;\n }\n }\n\n scopedResolver<T extends keyof CV>(\n token: T,\n context?: Record<string, unknown>,\n resolutionPath: (keyof CV)[] = []\n ): (scope?: ConfigInjector<SV, CV>) => ResolvedConfigValidator<SV, CV>[T] {\n return (scope) =>\n (scope ?? this.createScope()).resolve<T>(token, context, resolutionPath);\n }\n\n createScope(): ConfigInjector<SV, CV> {\n const singletons: Record<string, unknown> = {};\n for (const dependency in this.dependenciesDefinition) {\n if (\n this.dependenciesDefinition[dependency].lifetime === Lifetime.Singleton\n ) {\n singletons[dependency] = this.instances[dependency];\n }\n }\n return new ConfigInjector<SV, CV>(\n this.schemaValidator,\n this.dependenciesDefinition\n ).load(singletons as ResolvedConfigValidator<SV, CV>);\n }\n\n dispose(): void {\n this.instances = {};\n this.load();\n }\n\n chain<ChainedCV extends ConfigValidator<SV>>(dependenciesDefinition: {\n [K in keyof ChainedCV]: {\n type: ChainedCV[K];\n } & (\n | Singleton<\n ChainedCV[K],\n Omit<ResolvedConfigValidator<SV, CV & ChainedCV>, K>,\n ResolvedConfigValidator<SV, ChainedCV>[K]\n >\n | Constructed<\n ChainedCV[K],\n Omit<ResolvedConfigValidator<SV, CV & ChainedCV>, K>,\n ResolvedConfigValidator<SV, ChainedCV>[K]\n >\n );\n }): ConfigInjector<SV, CV & ChainedCV> {\n return new ConfigInjector<SV, CV>(this.schemaValidator, {\n ...this.dependenciesDefinition,\n ...dependenciesDefinition\n }).load({ ...this.instances }) as unknown as ConfigInjector<\n SV,\n CV & ChainedCV\n >;\n }\n\n tokens(): {\n [K in keyof CV]: K;\n } {\n return Object.fromEntries(\n Object.keys(this.dependenciesDefinition).map((key) => [key, key])\n ) as {\n [K in keyof CV]: K;\n };\n }\n}\n\nexport class ValidConfigInjector<\n SV extends AnySchemaValidator,\n CV extends ConfigValidator<SV>\n> extends ConfigInjector<SV, CV> {\n validConfigInjector!: void;\n}\n","import { TelemetryOptions } from '../types/openTelemetryCollector.types';\n\nexport function evaluateTelemetryOptions(telemetryOptions: TelemetryOptions) {\n return {\n enabled:\n typeof telemetryOptions.enabled === 'boolean'\n ? {\n metrics: telemetryOptions.enabled,\n tracing: telemetryOptions.enabled,\n logging: telemetryOptions.enabled\n }\n : {\n metrics: telemetryOptions.enabled.metrics,\n tracing: telemetryOptions.enabled.tracing,\n logging: telemetryOptions.enabled.logging\n },\n level: telemetryOptions.level\n };\n}\n"],"mappings":"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","names":["import_common","import_api","import_semantic_conventions","import_uuid","import_common","import_api","dotenv","import_validator","import_common","import_semantic_conventions","import_common","import_validator"]}
|