@forklaunch/core 0.6.6 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/cache/redisTtlCache.ts","../../../src/cache/utils/cacheKey.ts"],"sourcesContent":["import { RedisClientOptions, createClient } from 'redis';\nimport { TtlCache } from './interfaces/ttlCache.interface';\nimport { TtlCacheRecord } from './types/ttlCacheRecord.types';\nimport { OpenTelemetryCollector } from '../http/telemetry/openTelemetryCollector';\nimport { MetricsDefinition } from '../http';\n\n/**\n * Class representing a Redis-based TTL (Time-To-Live) cache.\n * Implements the TtlCache interface.\n */\nexport class RedisTtlCache implements TtlCache {\n private client;\n\n /**\n * Creates an instance of RedisTtlCache.\n *\n * @param {number} ttlMilliseconds - The default TTL in milliseconds.\n * @param {RedisClientOptions} [hostingOptions] - The Redis client options.\n */\n constructor(\n private ttlMilliseconds: number,\n private openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>,\n hostingOptions?: RedisClientOptions\n ) {\n // Connects to localhost:6379 by default\n // url usage: redis[s]://[[username][:password]@][host][:port][/db-number]\n this.client = createClient(hostingOptions);\n this.client.on('error', (err) => openTelemetryCollector.error(err));\n this.client.connect().catch(openTelemetryCollector.error);\n }\n\n /**\n * Puts a record into the Redis cache.\n *\n * @param {TtlCacheRecord} param0 - The cache record to put into the cache.\n * @returns {Promise<void>} - A promise that resolves when the record is put into the cache.\n */\n async putRecord<T>({\n key,\n value,\n ttlMilliseconds = this.ttlMilliseconds\n }: TtlCacheRecord<T>): Promise<void> {\n await this.client.set(key, JSON.stringify(value), {\n PX: ttlMilliseconds\n });\n }\n\n /**\n * Deletes a record from the Redis cache.\n *\n * @param {string} cacheRecordKey - The key of the cache record to delete.\n * @returns {Promise<void>} - A promise that resolves when the record is deleted from the cache.\n */\n async deleteRecord(cacheRecordKey: string): Promise<void> {\n await this.client.del(cacheRecordKey);\n }\n\n /**\n * Reads a record from the Redis cache.\n *\n * @param {string} cacheRecordKey - The key of the cache record to read.\n * @returns {Promise<TtlCacheRecord>} - A promise that resolves with the cache record.\n * @throws {Error} - Throws an error if the record is not found.\n */\n async readRecord<T>(cacheRecordKey: string): Promise<TtlCacheRecord<T>> {\n const value = await this.client.get(cacheRecordKey);\n if (value === null) {\n throw new Error(`Record not found for key: ${cacheRecordKey}`);\n }\n const ttl = await this.client.ttl(cacheRecordKey); // Fetch TTL from Redis\n return {\n key: cacheRecordKey,\n value: JSON.parse(value),\n ttlMilliseconds: ttl * 1000\n };\n }\n\n /**\n * Lists the keys in the Redis cache that match a pattern prefix.\n *\n * @param {string} pattern_prefix - The pattern prefix to match.\n * @returns {Promise<string[]>} - A promise that resolves with an array of keys matching the pattern prefix.\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 * Peeks at a record in the Redis cache to check if it exists.\n *\n * @param {string} cacheRecordKey - The key of the cache record to peek at.\n * @returns {Promise<boolean>} - A promise that resolves with a boolean indicating if the record exists.\n */\n async peekRecord(cacheRecordKey: string): Promise<boolean> {\n const result = await this.client.exists(cacheRecordKey);\n return result === 1;\n }\n\n /**\n * Disconnects the Redis client.\n *\n * @returns {Promise<void>} - A promise that resolves when the client is disconnected.\n */\n async disconnect(): Promise<void> {\n await this.client.quit();\n }\n\n /**\n * Gets the default TTL (Time-To-Live) in milliseconds.\n *\n * @returns {number} - The TTL in milliseconds.\n */\n getTtlMilliseconds(): number {\n return this.ttlMilliseconds;\n }\n}\n","export const createCacheKey = (cacheKeyPrefix: string) => (id: string) => {\n return `${cacheKeyPrefix}:${id}`;\n};\n"],"mappings":";AAAA,SAA6B,oBAAoB;AAU1C,IAAM,gBAAN,MAAwC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAS7C,YACU,iBACA,wBACR,gBACA;AAHQ;AACA;AAKR,SAAK,SAAS,aAAa,cAAc;AACzC,SAAK,OAAO,GAAG,SAAS,CAAC,QAAQ,uBAAuB,MAAM,GAAG,CAAC;AAClE,SAAK,OAAO,QAAQ,EAAE,MAAM,uBAAuB,KAAK;AAAA,EAC1D;AAAA,EAlBQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA0BR,MAAM,UAAa;AAAA,IACjB;AAAA,IACA;AAAA,IACA,kBAAkB,KAAK;AAAA,EACzB,GAAqC;AACnC,UAAM,KAAK,OAAO,IAAI,KAAK,KAAK,UAAU,KAAK,GAAG;AAAA,MAChD,IAAI;AAAA,IACN,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,aAAa,gBAAuC;AACxD,UAAM,KAAK,OAAO,IAAI,cAAc;AAAA,EACtC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,WAAc,gBAAoD;AACtE,UAAM,QAAQ,MAAM,KAAK,OAAO,IAAI,cAAc;AAClD,QAAI,UAAU,MAAM;AAClB,YAAM,IAAI,MAAM,6BAA6B,cAAc,EAAE;AAAA,IAC/D;AACA,UAAM,MAAM,MAAM,KAAK,OAAO,IAAI,cAAc;AAChD,WAAO;AAAA,MACL,KAAK;AAAA,MACL,OAAO,KAAK,MAAM,KAAK;AAAA,MACvB,iBAAiB,MAAM;AAAA,IACzB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,SAAS,gBAA2C;AACxD,UAAM,OAAO,MAAM,KAAK,OAAO,KAAK,iBAAiB,GAAG;AACxD,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,WAAW,gBAA0C;AACzD,UAAM,SAAS,MAAM,KAAK,OAAO,OAAO,cAAc;AACtD,WAAO,WAAW;AAAA,EACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,aAA4B;AAChC,UAAM,KAAK,OAAO,KAAK;AAAA,EACzB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,qBAA6B;AAC3B,WAAO,KAAK;AAAA,EACd;AACF;;;ACpHO,IAAM,iBAAiB,CAAC,mBAA2B,CAAC,OAAe;AACxE,SAAO,GAAG,cAAc,IAAI,EAAE;AAChC;","names":[]}
1
+ {"version":3,"sources":["../../../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":["/**\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));\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 acc.push({\n key: keys[index / 2],\n value: this.parseValue<T>(value),\n ttlMilliseconds: this.parseValue<number>(values[index + 1]) * 1000\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));\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 { 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 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('info', 'OpenTelemetry (Traces + Logs + Metrics) started');\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 } from '@forklaunch/common';\nimport { trace } from '@opentelemetry/api';\nimport { AnyValueMap, logs } from '@opentelemetry/api-logs';\nimport pino, { LevelWithSilent, LevelWithSilentOrString, Logger } from 'pino';\nimport { LogFn, LoggerMeta } from '../types/openTelemetryCollector.types';\nimport { isLoggerMeta } from '../guards/isLoggerMeta';\nimport PinoPretty from 'pino-pretty';\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.filter((arg) => {\n if (isLoggerMeta(arg)) {\n Object.assign(meta, arg);\n return false;\n }\n return true;\n }) 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 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}\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>(schemaValidator, dependenciesDefinition);\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 return this;\n }\n\n private loadSingletons(inheritedScopeInstances?: {\n [K in keyof CV]?: ResolvedConfigValidator<SV, CV>[K];\n }): this {\n for (const token in this.dependenciesDefinition) {\n const definition = this.dependenciesDefinition[token];\n if (definition.lifetime === Lifetime.Singleton) {\n if (inheritedScopeInstances && inheritedScopeInstances[token]) {\n this.instances[token] = inheritedScopeInstances[token];\n } else 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 this.loadSingletons();\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 ).loadSingletons({ ...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 ).loadSingletons({ ...this.instances });\n }\n\n dispose(): void {\n this.instances = {};\n this.loadSingletons();\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":["ATTR_SERVICE_NAME","trace","ATTR_SERVICE_NAME","prettyPrintParseErrors","ATTR_HTTP_REQUEST_METHOD","ATTR_HTTP_RESPONSE_STATUS_CODE","ATTR_HTTP_ROUTE","ATTR_SERVICE_NAME","extractArgumentNames","isNever","prettyPrintParseErrors"]}
@@ -1,3 +1,13 @@
1
+ /**
2
+ * Generic interface for a controller that wraps a service.
3
+ *
4
+ * @template Service - The service type that this controller wraps
5
+ * @template BaseRequest - The base request type (typically Express.Request)
6
+ * @template BaseResponse - The base response type (typically Express.Response)
7
+ * @template NextFunction - The next function type (typically Express.NextFunction)
8
+ * @template ParsedQs - The parsed query string type
9
+ *
10
+ */
1
11
  type Controller<Service, BaseRequest, BaseResponse, NextFunction, ParsedQs> = {
2
12
  [K in keyof Service]: unknown;
3
13
  } & {
@@ -1,3 +1,13 @@
1
+ /**
2
+ * Generic interface for a controller that wraps a service.
3
+ *
4
+ * @template Service - The service type that this controller wraps
5
+ * @template BaseRequest - The base request type (typically Express.Request)
6
+ * @template BaseResponse - The base response type (typically Express.Response)
7
+ * @template NextFunction - The next function type (typically Express.NextFunction)
8
+ * @template ParsedQs - The parsed query string type
9
+ *
10
+ */
1
11
  type Controller<Service, BaseRequest, BaseResponse, NextFunction, ParsedQs> = {
2
12
  [K in keyof Service]: unknown;
3
13
  } & {
@@ -1,13 +1,14 @@
1
1
  import { AnySchemaValidator, UnboxedObjectSchema, IdiomaticSchema, Schema } from '@forklaunch/validator';
2
- import { O as OpenTelemetryCollector, M as MetricsDefinition, L as LoggerMeta, a as LogFn } from '../../openTelemetryCollector-CWrfzmmW.mjs';
3
- export { c as MetricType, h as httpRequestsTotalCounter, b as httpServerDurationHistogram } from '../../openTelemetryCollector-CWrfzmmW.mjs';
2
+ import { O as OpenTelemetryCollector, M as MetricsDefinition, T as TelemetryOptions, L as LoggerMeta, a as LogFn } from '../../openTelemetryCollector-CmZ3T_2T.mjs';
3
+ export { c as MetricType, h as httpRequestsTotalCounter, b as httpServerDurationHistogram } from '../../openTelemetryCollector-CmZ3T_2T.mjs';
4
4
  import { Prettify, RemoveTrailingSlash, MakePropertyOptionalIfChildrenOptional } from '@forklaunch/common';
5
5
  import { Span } from '@opentelemetry/api';
6
6
  import { ParsedQs } from 'qs';
7
7
  import { Readable } from 'stream';
8
8
  import { OpenAPIObject } from 'openapi3-ts/oas31';
9
- import { AnyValueMap } from '@opentelemetry/api-logs';
9
+ import * as pino from 'pino';
10
10
  import { LevelWithSilentOrString, LevelWithSilent, Logger } from 'pino';
11
+ import { AnyValueMap } from '@opentelemetry/api-logs';
11
12
  import { ApiReferenceConfiguration } from '@scalar/express-api-reference';
12
13
  export { ATTR_HTTP_REQUEST_METHOD, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_HTTP_ROUTE, ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
13
14
 
@@ -786,8 +787,45 @@ declare abstract class ForklaunchExpressLikeApplication<SV extends AnySchemaVali
786
787
  abstract listen(...args: unknown[]): void;
787
788
  }
788
789
 
790
+ /**
791
+ * Type guard that checks if an object is a Forklaunch request.
792
+ * A Forklaunch request is an object that contains contract details and follows the Forklaunch request structure.
793
+ *
794
+ * @template SV - A type that extends AnySchemaValidator
795
+ * @template P - The type of route parameters that extends ParamsDictionary
796
+ * @template ReqBody - The type of request body that extends Record<string, unknown>
797
+ * @template ReqQuery - The type of request query parameters that extends ParsedQs
798
+ * @template ReqHeaders - The type of request headers that extends Record<string, string>
799
+ * @param {unknown} request - The object to check
800
+ * @returns {boolean} A type predicate indicating whether the object is a ForklaunchRequest
801
+ *
802
+ * @example
803
+ * ```ts
804
+ * if (isForklaunchRequest(request)) {
805
+ * // request is now typed as ForklaunchRequest
806
+ * const { contractDetails } = request;
807
+ * }
808
+ * ```
809
+ */
789
810
  declare function isForklaunchRequest<SV extends AnySchemaValidator, P extends ParamsDictionary, ReqBody extends Record<string, unknown>, ReqQuery extends ParsedQs, ReqHeaders extends Record<string, string>>(request: unknown): request is ForklaunchRequest<SV, P, ReqBody, ReqQuery, ReqHeaders>;
790
811
 
812
+ /**
813
+ * Type guard that checks if an object is a Forklaunch router.
814
+ * A Forklaunch router is an object that has a base path and an array of routes.
815
+ *
816
+ * @template SV - A type that extends AnySchemaValidator
817
+ * @param {unknown} maybeForklaunchRouter - The object to check
818
+ * @returns {boolean} A type predicate indicating whether the object is a ForklaunchRouter
819
+ *
820
+ * @example
821
+ * ```ts
822
+ * if (isForklaunchRouter(router)) {
823
+ * // router is now typed as ForklaunchRouter
824
+ * router.basePath; // '/api'
825
+ * router.routes; // Route[]
826
+ * }
827
+ * ```
828
+ */
791
829
  declare function isForklaunchRouter<SV extends AnySchemaValidator>(maybeForklaunchRouter: unknown): maybeForklaunchRouter is ForklaunchRouter<SV>;
792
830
 
793
831
  declare const delete_: <SV extends AnySchemaValidator, Path extends `/${string}`, P extends ParamsObject<SV>, ResBodyMap extends ResponsesObject<SV>, ReqBody extends Body<SV>, ReqQuery extends QueryObject<SV>, ReqHeaders extends HeadersObject<SV>, ResHeaders extends HeadersObject<SV>, LocalsObj extends Record<string, unknown>, BaseRequest, BaseResponse, NextFunction>(_schemaValidator: SV, path: Path, contractDetails: PathParamHttpContractDetails<SV, Path, P, ResBodyMap, ReqQuery, ReqHeaders, ResHeaders, BaseRequest>, ...handlers: ExpressLikeSchemaHandler<SV, P, ResBodyMap, ReqBody, ReqQuery, ReqHeaders, ResHeaders, LocalsObj, BaseRequest, BaseResponse, NextFunction>[]) => ExpressLikeTypedHandler<SV, "delete", Path, P, ResBodyMap, ReqBody, ReqQuery, ReqHeaders, ResHeaders, LocalsObj, BaseRequest, BaseResponse, NextFunction>;
@@ -939,6 +977,15 @@ declare function generateSwaggerDocument<SV extends AnySchemaValidator>(schemaVa
939
977
  declare const ATTR_API_NAME = "api.name";
940
978
  declare const ATTR_CORRELATION_ID = "correlation.id";
941
979
 
980
+ declare function evaluateTelemetryOptions(telemetryOptions: TelemetryOptions): {
981
+ enabled: {
982
+ metrics: boolean | undefined;
983
+ tracing: boolean | undefined;
984
+ logging: boolean | undefined;
985
+ };
986
+ level: pino.default.LevelWithSilentOrString;
987
+ };
988
+
942
989
  declare function metricsDefinitions<T extends MetricsDefinition>(metrics: T): T;
943
990
 
944
991
  declare function meta(meta: Record<string, unknown>): LoggerMeta;
@@ -966,4 +1013,4 @@ type DocsConfiguration = ({
966
1013
  type: 'swagger';
967
1014
  };
968
1015
 
969
- export { ATTR_API_NAME, ATTR_CORRELATION_ID, type ApiClient, type AuthMethods, type AuthMethodsBase, type Body, type BodyObject, type ConstrainedForklaunchRouter, type ContractDetails, type ContractDetailsExpressLikeSchemaHandler, type ContractDetailsOrMiddlewareOrTypedHandler, type DocsConfiguration, type ErrorContainer, type ExpressLikeAuthMapper, type ExpressLikeHandler, type ExpressLikeRouter, type ExpressLikeSchemaAuthMapper, type ExpressLikeSchemaHandler, type ExpressLikeTypedHandler, type ExtractedParamsObject, type ForklaunchBaseRequest, ForklaunchExpressLikeApplication, ForklaunchExpressLikeRouter, type ForklaunchNextFunction, type ForklaunchRequest, type ForklaunchResErrors, type ForklaunchResHeaders, type ForklaunchResponse, type ForklaunchRoute, type ForklaunchRouter, type ForklaunchSendableData, type ForklaunchStatusResponse, HTTPStatuses, type HeadersObject, type HttpContractDetails, type HttpMethod, type LiveTypeFunction, type LiveTypeRouteDefinition, LogFn, LoggerMeta, type MapParamsSchema, type MapReqBodySchema, type MapReqHeadersSchema, type MapReqQuerySchema, type MapResBodyMapSchema, type MapResHeadersSchema, type MapSchema, type Method, MetricsDefinition, type MiddlewareContractDetails, type MiddlewareOrMiddlewareWithTypedHandler, type NestableRouterBasedHandler, type NumberOnlyObject, OpenTelemetryCollector, type ParamsDictionary, type ParamsObject, type PathBasedHandler, type PathMatch, type PathOrMiddlewareBasedHandler, type PathParamHttpContractDetails, type PathParamMethod, type QueryObject, type RequestContext, type ResolvedForklaunchRequest, type ResponseCompiledSchema, type ResponseShape, type ResponsesObject, type SchemaAuthMethods, type StatusCode, type StringOnlyObject, type TypedHandler, type TypedMiddlewareDefinition, type TypedNestableMiddlewareDefinition, delete_, enrichExpressLikeSend, generateSwaggerDocument, get, getCodeForStatus, head, isClientError, isForklaunchRequest, isForklaunchRouter, isInformational, isRedirection, isServerError, isSuccessful, isValidStatusCode, logger, meta, metricsDefinitions, middleware, options, patch, post, put, recordMetric, trace, typedHandler };
1016
+ export { ATTR_API_NAME, ATTR_CORRELATION_ID, type ApiClient, type AuthMethods, type AuthMethodsBase, type Body, type BodyObject, type ConstrainedForklaunchRouter, type ContractDetails, type ContractDetailsExpressLikeSchemaHandler, type ContractDetailsOrMiddlewareOrTypedHandler, type DocsConfiguration, type ErrorContainer, type ExpressLikeAuthMapper, type ExpressLikeHandler, type ExpressLikeRouter, type ExpressLikeSchemaAuthMapper, type ExpressLikeSchemaHandler, type ExpressLikeTypedHandler, type ExtractedParamsObject, type ForklaunchBaseRequest, ForklaunchExpressLikeApplication, ForklaunchExpressLikeRouter, type ForklaunchNextFunction, type ForklaunchRequest, type ForklaunchResErrors, type ForklaunchResHeaders, type ForklaunchResponse, type ForklaunchRoute, type ForklaunchRouter, type ForklaunchSendableData, type ForklaunchStatusResponse, HTTPStatuses, type HeadersObject, type HttpContractDetails, type HttpMethod, type LiveTypeFunction, type LiveTypeRouteDefinition, LogFn, LoggerMeta, type MapParamsSchema, type MapReqBodySchema, type MapReqHeadersSchema, type MapReqQuerySchema, type MapResBodyMapSchema, type MapResHeadersSchema, type MapSchema, type Method, MetricsDefinition, type MiddlewareContractDetails, type MiddlewareOrMiddlewareWithTypedHandler, type NestableRouterBasedHandler, type NumberOnlyObject, OpenTelemetryCollector, type ParamsDictionary, type ParamsObject, type PathBasedHandler, type PathMatch, type PathOrMiddlewareBasedHandler, type PathParamHttpContractDetails, type PathParamMethod, type QueryObject, type RequestContext, type ResolvedForklaunchRequest, type ResponseCompiledSchema, type ResponseShape, type ResponsesObject, type SchemaAuthMethods, type StatusCode, type StringOnlyObject, TelemetryOptions, type TypedHandler, type TypedMiddlewareDefinition, type TypedNestableMiddlewareDefinition, delete_, enrichExpressLikeSend, evaluateTelemetryOptions, generateSwaggerDocument, get, getCodeForStatus, head, isClientError, isForklaunchRequest, isForklaunchRouter, isInformational, isRedirection, isServerError, isSuccessful, isValidStatusCode, logger, meta, metricsDefinitions, middleware, options, patch, post, put, recordMetric, trace, typedHandler };
@@ -1,13 +1,14 @@
1
1
  import { AnySchemaValidator, UnboxedObjectSchema, IdiomaticSchema, Schema } from '@forklaunch/validator';
2
- import { O as OpenTelemetryCollector, M as MetricsDefinition, L as LoggerMeta, a as LogFn } from '../../openTelemetryCollector-CWrfzmmW.js';
3
- export { c as MetricType, h as httpRequestsTotalCounter, b as httpServerDurationHistogram } from '../../openTelemetryCollector-CWrfzmmW.js';
2
+ import { O as OpenTelemetryCollector, M as MetricsDefinition, T as TelemetryOptions, L as LoggerMeta, a as LogFn } from '../../openTelemetryCollector-CmZ3T_2T.js';
3
+ export { c as MetricType, h as httpRequestsTotalCounter, b as httpServerDurationHistogram } from '../../openTelemetryCollector-CmZ3T_2T.js';
4
4
  import { Prettify, RemoveTrailingSlash, MakePropertyOptionalIfChildrenOptional } from '@forklaunch/common';
5
5
  import { Span } from '@opentelemetry/api';
6
6
  import { ParsedQs } from 'qs';
7
7
  import { Readable } from 'stream';
8
8
  import { OpenAPIObject } from 'openapi3-ts/oas31';
9
- import { AnyValueMap } from '@opentelemetry/api-logs';
9
+ import * as pino from 'pino';
10
10
  import { LevelWithSilentOrString, LevelWithSilent, Logger } from 'pino';
11
+ import { AnyValueMap } from '@opentelemetry/api-logs';
11
12
  import { ApiReferenceConfiguration } from '@scalar/express-api-reference';
12
13
  export { ATTR_HTTP_REQUEST_METHOD, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_HTTP_ROUTE, ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
13
14
 
@@ -786,8 +787,45 @@ declare abstract class ForklaunchExpressLikeApplication<SV extends AnySchemaVali
786
787
  abstract listen(...args: unknown[]): void;
787
788
  }
788
789
 
790
+ /**
791
+ * Type guard that checks if an object is a Forklaunch request.
792
+ * A Forklaunch request is an object that contains contract details and follows the Forklaunch request structure.
793
+ *
794
+ * @template SV - A type that extends AnySchemaValidator
795
+ * @template P - The type of route parameters that extends ParamsDictionary
796
+ * @template ReqBody - The type of request body that extends Record<string, unknown>
797
+ * @template ReqQuery - The type of request query parameters that extends ParsedQs
798
+ * @template ReqHeaders - The type of request headers that extends Record<string, string>
799
+ * @param {unknown} request - The object to check
800
+ * @returns {boolean} A type predicate indicating whether the object is a ForklaunchRequest
801
+ *
802
+ * @example
803
+ * ```ts
804
+ * if (isForklaunchRequest(request)) {
805
+ * // request is now typed as ForklaunchRequest
806
+ * const { contractDetails } = request;
807
+ * }
808
+ * ```
809
+ */
789
810
  declare function isForklaunchRequest<SV extends AnySchemaValidator, P extends ParamsDictionary, ReqBody extends Record<string, unknown>, ReqQuery extends ParsedQs, ReqHeaders extends Record<string, string>>(request: unknown): request is ForklaunchRequest<SV, P, ReqBody, ReqQuery, ReqHeaders>;
790
811
 
812
+ /**
813
+ * Type guard that checks if an object is a Forklaunch router.
814
+ * A Forklaunch router is an object that has a base path and an array of routes.
815
+ *
816
+ * @template SV - A type that extends AnySchemaValidator
817
+ * @param {unknown} maybeForklaunchRouter - The object to check
818
+ * @returns {boolean} A type predicate indicating whether the object is a ForklaunchRouter
819
+ *
820
+ * @example
821
+ * ```ts
822
+ * if (isForklaunchRouter(router)) {
823
+ * // router is now typed as ForklaunchRouter
824
+ * router.basePath; // '/api'
825
+ * router.routes; // Route[]
826
+ * }
827
+ * ```
828
+ */
791
829
  declare function isForklaunchRouter<SV extends AnySchemaValidator>(maybeForklaunchRouter: unknown): maybeForklaunchRouter is ForklaunchRouter<SV>;
792
830
 
793
831
  declare const delete_: <SV extends AnySchemaValidator, Path extends `/${string}`, P extends ParamsObject<SV>, ResBodyMap extends ResponsesObject<SV>, ReqBody extends Body<SV>, ReqQuery extends QueryObject<SV>, ReqHeaders extends HeadersObject<SV>, ResHeaders extends HeadersObject<SV>, LocalsObj extends Record<string, unknown>, BaseRequest, BaseResponse, NextFunction>(_schemaValidator: SV, path: Path, contractDetails: PathParamHttpContractDetails<SV, Path, P, ResBodyMap, ReqQuery, ReqHeaders, ResHeaders, BaseRequest>, ...handlers: ExpressLikeSchemaHandler<SV, P, ResBodyMap, ReqBody, ReqQuery, ReqHeaders, ResHeaders, LocalsObj, BaseRequest, BaseResponse, NextFunction>[]) => ExpressLikeTypedHandler<SV, "delete", Path, P, ResBodyMap, ReqBody, ReqQuery, ReqHeaders, ResHeaders, LocalsObj, BaseRequest, BaseResponse, NextFunction>;
@@ -939,6 +977,15 @@ declare function generateSwaggerDocument<SV extends AnySchemaValidator>(schemaVa
939
977
  declare const ATTR_API_NAME = "api.name";
940
978
  declare const ATTR_CORRELATION_ID = "correlation.id";
941
979
 
980
+ declare function evaluateTelemetryOptions(telemetryOptions: TelemetryOptions): {
981
+ enabled: {
982
+ metrics: boolean | undefined;
983
+ tracing: boolean | undefined;
984
+ logging: boolean | undefined;
985
+ };
986
+ level: pino.default.LevelWithSilentOrString;
987
+ };
988
+
942
989
  declare function metricsDefinitions<T extends MetricsDefinition>(metrics: T): T;
943
990
 
944
991
  declare function meta(meta: Record<string, unknown>): LoggerMeta;
@@ -966,4 +1013,4 @@ type DocsConfiguration = ({
966
1013
  type: 'swagger';
967
1014
  };
968
1015
 
969
- export { ATTR_API_NAME, ATTR_CORRELATION_ID, type ApiClient, type AuthMethods, type AuthMethodsBase, type Body, type BodyObject, type ConstrainedForklaunchRouter, type ContractDetails, type ContractDetailsExpressLikeSchemaHandler, type ContractDetailsOrMiddlewareOrTypedHandler, type DocsConfiguration, type ErrorContainer, type ExpressLikeAuthMapper, type ExpressLikeHandler, type ExpressLikeRouter, type ExpressLikeSchemaAuthMapper, type ExpressLikeSchemaHandler, type ExpressLikeTypedHandler, type ExtractedParamsObject, type ForklaunchBaseRequest, ForklaunchExpressLikeApplication, ForklaunchExpressLikeRouter, type ForklaunchNextFunction, type ForklaunchRequest, type ForklaunchResErrors, type ForklaunchResHeaders, type ForklaunchResponse, type ForklaunchRoute, type ForklaunchRouter, type ForklaunchSendableData, type ForklaunchStatusResponse, HTTPStatuses, type HeadersObject, type HttpContractDetails, type HttpMethod, type LiveTypeFunction, type LiveTypeRouteDefinition, LogFn, LoggerMeta, type MapParamsSchema, type MapReqBodySchema, type MapReqHeadersSchema, type MapReqQuerySchema, type MapResBodyMapSchema, type MapResHeadersSchema, type MapSchema, type Method, MetricsDefinition, type MiddlewareContractDetails, type MiddlewareOrMiddlewareWithTypedHandler, type NestableRouterBasedHandler, type NumberOnlyObject, OpenTelemetryCollector, type ParamsDictionary, type ParamsObject, type PathBasedHandler, type PathMatch, type PathOrMiddlewareBasedHandler, type PathParamHttpContractDetails, type PathParamMethod, type QueryObject, type RequestContext, type ResolvedForklaunchRequest, type ResponseCompiledSchema, type ResponseShape, type ResponsesObject, type SchemaAuthMethods, type StatusCode, type StringOnlyObject, type TypedHandler, type TypedMiddlewareDefinition, type TypedNestableMiddlewareDefinition, delete_, enrichExpressLikeSend, generateSwaggerDocument, get, getCodeForStatus, head, isClientError, isForklaunchRequest, isForklaunchRouter, isInformational, isRedirection, isServerError, isSuccessful, isValidStatusCode, logger, meta, metricsDefinitions, middleware, options, patch, post, put, recordMetric, trace, typedHandler };
1016
+ export { ATTR_API_NAME, ATTR_CORRELATION_ID, type ApiClient, type AuthMethods, type AuthMethodsBase, type Body, type BodyObject, type ConstrainedForklaunchRouter, type ContractDetails, type ContractDetailsExpressLikeSchemaHandler, type ContractDetailsOrMiddlewareOrTypedHandler, type DocsConfiguration, type ErrorContainer, type ExpressLikeAuthMapper, type ExpressLikeHandler, type ExpressLikeRouter, type ExpressLikeSchemaAuthMapper, type ExpressLikeSchemaHandler, type ExpressLikeTypedHandler, type ExtractedParamsObject, type ForklaunchBaseRequest, ForklaunchExpressLikeApplication, ForklaunchExpressLikeRouter, type ForklaunchNextFunction, type ForklaunchRequest, type ForklaunchResErrors, type ForklaunchResHeaders, type ForklaunchResponse, type ForklaunchRoute, type ForklaunchRouter, type ForklaunchSendableData, type ForklaunchStatusResponse, HTTPStatuses, type HeadersObject, type HttpContractDetails, type HttpMethod, type LiveTypeFunction, type LiveTypeRouteDefinition, LogFn, LoggerMeta, type MapParamsSchema, type MapReqBodySchema, type MapReqHeadersSchema, type MapReqQuerySchema, type MapResBodyMapSchema, type MapResHeadersSchema, type MapSchema, type Method, MetricsDefinition, type MiddlewareContractDetails, type MiddlewareOrMiddlewareWithTypedHandler, type NestableRouterBasedHandler, type NumberOnlyObject, OpenTelemetryCollector, type ParamsDictionary, type ParamsObject, type PathBasedHandler, type PathMatch, type PathOrMiddlewareBasedHandler, type PathParamHttpContractDetails, type PathParamMethod, type QueryObject, type RequestContext, type ResolvedForklaunchRequest, type ResponseCompiledSchema, type ResponseShape, type ResponsesObject, type SchemaAuthMethods, type StatusCode, type StringOnlyObject, TelemetryOptions, type TypedHandler, type TypedMiddlewareDefinition, type TypedNestableMiddlewareDefinition, delete_, enrichExpressLikeSend, evaluateTelemetryOptions, generateSwaggerDocument, get, getCodeForStatus, head, isClientError, isForklaunchRequest, isForklaunchRouter, isInformational, isRedirection, isServerError, isSuccessful, isValidStatusCode, logger, meta, metricsDefinitions, middleware, options, patch, post, put, recordMetric, trace, typedHandler };
@@ -42,6 +42,7 @@ __export(http_exports, {
42
42
  OpenTelemetryCollector: () => OpenTelemetryCollector,
43
43
  delete_: () => delete_,
44
44
  enrichExpressLikeSend: () => enrichExpressLikeSend,
45
+ evaluateTelemetryOptions: () => evaluateTelemetryOptions,
45
46
  generateSwaggerDocument: () => generateSwaggerDocument,
46
47
  get: () => get,
47
48
  getCodeForStatus: () => getCodeForStatus,
@@ -577,7 +578,12 @@ function parse(req, res, next) {
577
578
  if (parsedRequest.ok && isResponseShape(parsedRequest.value)) {
578
579
  req.body = parsedRequest.value.body;
579
580
  req.params = parsedRequest.value.params;
580
- req.query = parsedRequest.value.query;
581
+ Object.defineProperty(req, "query", {
582
+ value: parsedRequest.value.query,
583
+ writable: false,
584
+ enumerable: true,
585
+ configurable: false
586
+ });
581
587
  req.headers = parsedRequest.value.headers;
582
588
  }
583
589
  if (!parsedRequest.ok) {
@@ -2521,6 +2527,22 @@ function generateSwaggerDocument(schemaValidator, port, routers) {
2521
2527
  return generateOpenApiDocument(port, tags, paths);
2522
2528
  }
2523
2529
 
2530
+ // src/http/telemetry/evaluateTelemetryOptions.ts
2531
+ function evaluateTelemetryOptions(telemetryOptions) {
2532
+ return {
2533
+ enabled: typeof telemetryOptions.enabled === "boolean" ? {
2534
+ metrics: telemetryOptions.enabled,
2535
+ tracing: telemetryOptions.enabled,
2536
+ logging: telemetryOptions.enabled
2537
+ } : {
2538
+ metrics: telemetryOptions.enabled.metrics,
2539
+ tracing: telemetryOptions.enabled.tracing,
2540
+ logging: telemetryOptions.enabled.logging
2541
+ },
2542
+ level: telemetryOptions.level
2543
+ };
2544
+ }
2545
+
2524
2546
  // src/http/telemetry/metricsDefinition.ts
2525
2547
  function metricsDefinitions(metrics2) {
2526
2548
  return metrics2;
@@ -2539,6 +2561,7 @@ function metricsDefinitions(metrics2) {
2539
2561
  OpenTelemetryCollector,
2540
2562
  delete_,
2541
2563
  enrichExpressLikeSend,
2564
+ evaluateTelemetryOptions,
2542
2565
  generateSwaggerDocument,
2543
2566
  get,
2544
2567
  getCodeForStatus,