@forklaunch/core 0.8.2 → 0.8.4
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/src/cache/index.d.mts +1 -177
- package/lib/src/cache/index.d.ts +1 -177
- package/lib/src/cache/index.js +0 -448
- package/lib/src/cache/index.js.map +1 -1
- package/lib/src/cache/index.mjs +0 -464
- package/lib/src/cache/index.mjs.map +1 -1
- package/lib/src/http/index.d.mts +53 -6
- package/lib/src/http/index.d.ts +53 -6
- package/lib/src/objectstore/index.d.mts +97 -0
- package/lib/src/objectstore/index.d.ts +97 -0
- package/lib/src/objectstore/index.js +36 -0
- package/lib/src/objectstore/index.js.map +1 -0
- package/lib/src/objectstore/index.mjs +9 -0
- package/lib/src/objectstore/index.mjs.map +1 -0
- package/package.json +11 -5
- package/lib/openTelemetryCollector-DQC2FfOu.d.mts +0 -53
- package/lib/openTelemetryCollector-DQC2FfOu.d.ts +0 -53
@@ -1 +1 @@
|
|
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/middleware/response/enrichExpressLikeSend.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 { 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":["safeStringify","ATTR_SERVICE_NAME","v4","trace","ATTR_SERVICE_NAME","prettyPrintParseErrors","isNever","safeStringify","ATTR_HTTP_REQUEST_METHOD","ATTR_HTTP_RESPONSE_STATUS_CODE","ATTR_HTTP_ROUTE","ATTR_SERVICE_NAME","extractArgumentNames","isNever","prettyPrintParseErrors","safeStringify"]}
|
1
|
+
{"version":3,"sources":["../../../src/cache/cacheKey.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"],"mappings":";AASO,IAAM,iBAAiB,CAAC,mBAA2B,CAAC,OAAe;AACxE,SAAO,GAAG,cAAc,IAAI,EAAE;AAChC;","names":[]}
|
package/lib/src/http/index.d.mts
CHANGED
@@ -1,13 +1,11 @@
|
|
1
1
|
import { AnySchemaValidator, UnboxedObjectSchema, IdiomaticSchema, Schema } from '@forklaunch/validator';
|
2
|
-
import {
|
3
|
-
|
2
|
+
import { Counter, Gauge, Histogram, UpDownCounter, ObservableCounter, ObservableGauge, ObservableUpDownCounter, Span } from '@opentelemetry/api';
|
3
|
+
import * as pino from 'pino';
|
4
|
+
import { LevelWithSilentOrString, LevelWithSilent, Logger } from 'pino';
|
4
5
|
import { UnionToIntersection, MimeType, Prettify, ExclusiveRecord, RemoveTrailingSlash, MakePropertyOptionalIfChildrenOptional } from '@forklaunch/common';
|
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 * as pino from 'pino';
|
10
|
-
import { LevelWithSilentOrString, LevelWithSilent, Logger } from 'pino';
|
11
9
|
import { AnyValueMap } from '@opentelemetry/api-logs';
|
12
10
|
import { ApiReferenceConfiguration } from '@scalar/express-api-reference';
|
13
11
|
export { ATTR_HTTP_REQUEST_METHOD, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_HTTP_ROUTE, ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
|
@@ -36,6 +34,55 @@ interface ExpressLikeRouter<RouterHandler, Router> {
|
|
36
34
|
trace: PathBasedHandler<RouterHandler>;
|
37
35
|
}
|
38
36
|
|
37
|
+
type MetricType<T extends string> = T extends 'counter' ? Counter : T extends 'gauge' ? Gauge : T extends 'histogram' ? Histogram : T extends 'upDownCounter' ? UpDownCounter : T extends 'observableCounter' ? ObservableCounter : T extends 'observableGauge' ? ObservableGauge : T extends 'observableUpDownCounter' ? ObservableUpDownCounter : undefined;
|
38
|
+
type MetricsDefinition = Record<string, 'counter' | 'gauge' | 'histogram' | 'upDownCounter' | 'observableCounter' | 'observableGauge' | 'observableUpDownCounter'>;
|
39
|
+
type LoggerMeta = Record<string, unknown> & {
|
40
|
+
_meta: true;
|
41
|
+
};
|
42
|
+
interface LogFn {
|
43
|
+
<T extends object>(obj: T | LoggerMeta, msg?: string | LoggerMeta, ...args: unknown[]): void;
|
44
|
+
(obj: unknown | LoggerMeta, msg?: string | LoggerMeta, ...args: unknown[]): void;
|
45
|
+
(msg: string | LoggerMeta, ...args: unknown[]): void;
|
46
|
+
}
|
47
|
+
interface TelemetryOptions {
|
48
|
+
enabled: boolean | {
|
49
|
+
metrics?: boolean;
|
50
|
+
tracing?: boolean;
|
51
|
+
logging?: boolean;
|
52
|
+
};
|
53
|
+
level: LevelWithSilentOrString;
|
54
|
+
}
|
55
|
+
|
56
|
+
declare class OpenTelemetryCollector<AppliedMetricsDefinition extends MetricsDefinition> {
|
57
|
+
private readonly serviceName;
|
58
|
+
private readonly uuid;
|
59
|
+
private readonly logger;
|
60
|
+
private readonly metrics;
|
61
|
+
constructor(serviceName: string, level?: LevelWithSilentOrString, metricDefinitions?: AppliedMetricsDefinition);
|
62
|
+
log(level: LevelWithSilent, ...args: (string | unknown | LoggerMeta)[]): void;
|
63
|
+
info: LogFn;
|
64
|
+
error: LogFn;
|
65
|
+
warn: LogFn;
|
66
|
+
debug: LogFn;
|
67
|
+
trace: LogFn;
|
68
|
+
getMetric<T extends keyof AppliedMetricsDefinition>(metricId: T): MetricType<AppliedMetricsDefinition[T]>;
|
69
|
+
}
|
70
|
+
declare const httpRequestsTotalCounter: Counter<{
|
71
|
+
"service.name": string;
|
72
|
+
"api.name": string;
|
73
|
+
"correlation.id": string;
|
74
|
+
"http.request.method": string;
|
75
|
+
"http.route": string;
|
76
|
+
"http.response.status_code": number;
|
77
|
+
}>;
|
78
|
+
declare const httpServerDurationHistogram: Histogram<{
|
79
|
+
"service.name": string;
|
80
|
+
"api.name": string;
|
81
|
+
"http.request.method": string;
|
82
|
+
"http.route": string;
|
83
|
+
"http.response.status_code": number;
|
84
|
+
}>;
|
85
|
+
|
39
86
|
/**
|
40
87
|
* Dictionary type for URL parameters.
|
41
88
|
*/
|
@@ -1173,4 +1220,4 @@ type DocsConfiguration = ({
|
|
1173
1220
|
type: 'swagger';
|
1174
1221
|
};
|
1175
1222
|
|
1176
|
-
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 ExtractBody, type ExtractContentType, type ExtractResponseBody, type ExtractedParamsObject, type FileBody, 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 JsonBody, 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 MultipartForm, type NestableRouterBasedHandler, type NumberOnlyObject, OpenTelemetryCollector, type ParamsDictionary, type ParamsObject, type PathBasedHandler, type PathMatch, type PathOrMiddlewareBasedHandler, type PathParamHttpContractDetails, type PathParamMethod, type QueryObject, type RawTypedResponseBody, type RequestContext, type ResolvedForklaunchRequest, type ResponseBody, type ResponseCompiledSchema, type ResponseShape, type ResponsesObject, type SchemaAuthMethods, type ServerSentEventBody, type StatusCode, type StringOnlyObject, TelemetryOptions, type TextBody, type TypedBody, type TypedHandler, type TypedMiddlewareDefinition, type TypedNestableMiddlewareDefinition, type TypedResponseBody, type UnknownBody, type UnknownResponseBody, type UrlEncodedForm, delete_, discriminateBody, discriminateResponseBodies, 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 };
|
1223
|
+
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 ExtractBody, type ExtractContentType, type ExtractResponseBody, type ExtractedParamsObject, type FileBody, 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 JsonBody, type LiveTypeFunction, type LiveTypeRouteDefinition, type LogFn, type LoggerMeta, type MapParamsSchema, type MapReqBodySchema, type MapReqHeadersSchema, type MapReqQuerySchema, type MapResBodyMapSchema, type MapResHeadersSchema, type MapSchema, type Method, type MetricType, type MetricsDefinition, type MiddlewareContractDetails, type MiddlewareOrMiddlewareWithTypedHandler, type MultipartForm, type NestableRouterBasedHandler, type NumberOnlyObject, OpenTelemetryCollector, type ParamsDictionary, type ParamsObject, type PathBasedHandler, type PathMatch, type PathOrMiddlewareBasedHandler, type PathParamHttpContractDetails, type PathParamMethod, type QueryObject, type RawTypedResponseBody, type RequestContext, type ResolvedForklaunchRequest, type ResponseBody, type ResponseCompiledSchema, type ResponseShape, type ResponsesObject, type SchemaAuthMethods, type ServerSentEventBody, type StatusCode, type StringOnlyObject, type TelemetryOptions, type TextBody, type TypedBody, type TypedHandler, type TypedMiddlewareDefinition, type TypedNestableMiddlewareDefinition, type TypedResponseBody, type UnknownBody, type UnknownResponseBody, type UrlEncodedForm, delete_, discriminateBody, discriminateResponseBodies, enrichExpressLikeSend, evaluateTelemetryOptions, generateSwaggerDocument, get, getCodeForStatus, head, httpRequestsTotalCounter, httpServerDurationHistogram, isClientError, isForklaunchRequest, isForklaunchRouter, isInformational, isRedirection, isServerError, isSuccessful, isValidStatusCode, logger, meta, metricsDefinitions, middleware, options, patch, post, put, recordMetric, trace, typedHandler };
|
package/lib/src/http/index.d.ts
CHANGED
@@ -1,13 +1,11 @@
|
|
1
1
|
import { AnySchemaValidator, UnboxedObjectSchema, IdiomaticSchema, Schema } from '@forklaunch/validator';
|
2
|
-
import {
|
3
|
-
|
2
|
+
import { Counter, Gauge, Histogram, UpDownCounter, ObservableCounter, ObservableGauge, ObservableUpDownCounter, Span } from '@opentelemetry/api';
|
3
|
+
import * as pino from 'pino';
|
4
|
+
import { LevelWithSilentOrString, LevelWithSilent, Logger } from 'pino';
|
4
5
|
import { UnionToIntersection, MimeType, Prettify, ExclusiveRecord, RemoveTrailingSlash, MakePropertyOptionalIfChildrenOptional } from '@forklaunch/common';
|
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 * as pino from 'pino';
|
10
|
-
import { LevelWithSilentOrString, LevelWithSilent, Logger } from 'pino';
|
11
9
|
import { AnyValueMap } from '@opentelemetry/api-logs';
|
12
10
|
import { ApiReferenceConfiguration } from '@scalar/express-api-reference';
|
13
11
|
export { ATTR_HTTP_REQUEST_METHOD, ATTR_HTTP_RESPONSE_STATUS_CODE, ATTR_HTTP_ROUTE, ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
|
@@ -36,6 +34,55 @@ interface ExpressLikeRouter<RouterHandler, Router> {
|
|
36
34
|
trace: PathBasedHandler<RouterHandler>;
|
37
35
|
}
|
38
36
|
|
37
|
+
type MetricType<T extends string> = T extends 'counter' ? Counter : T extends 'gauge' ? Gauge : T extends 'histogram' ? Histogram : T extends 'upDownCounter' ? UpDownCounter : T extends 'observableCounter' ? ObservableCounter : T extends 'observableGauge' ? ObservableGauge : T extends 'observableUpDownCounter' ? ObservableUpDownCounter : undefined;
|
38
|
+
type MetricsDefinition = Record<string, 'counter' | 'gauge' | 'histogram' | 'upDownCounter' | 'observableCounter' | 'observableGauge' | 'observableUpDownCounter'>;
|
39
|
+
type LoggerMeta = Record<string, unknown> & {
|
40
|
+
_meta: true;
|
41
|
+
};
|
42
|
+
interface LogFn {
|
43
|
+
<T extends object>(obj: T | LoggerMeta, msg?: string | LoggerMeta, ...args: unknown[]): void;
|
44
|
+
(obj: unknown | LoggerMeta, msg?: string | LoggerMeta, ...args: unknown[]): void;
|
45
|
+
(msg: string | LoggerMeta, ...args: unknown[]): void;
|
46
|
+
}
|
47
|
+
interface TelemetryOptions {
|
48
|
+
enabled: boolean | {
|
49
|
+
metrics?: boolean;
|
50
|
+
tracing?: boolean;
|
51
|
+
logging?: boolean;
|
52
|
+
};
|
53
|
+
level: LevelWithSilentOrString;
|
54
|
+
}
|
55
|
+
|
56
|
+
declare class OpenTelemetryCollector<AppliedMetricsDefinition extends MetricsDefinition> {
|
57
|
+
private readonly serviceName;
|
58
|
+
private readonly uuid;
|
59
|
+
private readonly logger;
|
60
|
+
private readonly metrics;
|
61
|
+
constructor(serviceName: string, level?: LevelWithSilentOrString, metricDefinitions?: AppliedMetricsDefinition);
|
62
|
+
log(level: LevelWithSilent, ...args: (string | unknown | LoggerMeta)[]): void;
|
63
|
+
info: LogFn;
|
64
|
+
error: LogFn;
|
65
|
+
warn: LogFn;
|
66
|
+
debug: LogFn;
|
67
|
+
trace: LogFn;
|
68
|
+
getMetric<T extends keyof AppliedMetricsDefinition>(metricId: T): MetricType<AppliedMetricsDefinition[T]>;
|
69
|
+
}
|
70
|
+
declare const httpRequestsTotalCounter: Counter<{
|
71
|
+
"service.name": string;
|
72
|
+
"api.name": string;
|
73
|
+
"correlation.id": string;
|
74
|
+
"http.request.method": string;
|
75
|
+
"http.route": string;
|
76
|
+
"http.response.status_code": number;
|
77
|
+
}>;
|
78
|
+
declare const httpServerDurationHistogram: Histogram<{
|
79
|
+
"service.name": string;
|
80
|
+
"api.name": string;
|
81
|
+
"http.request.method": string;
|
82
|
+
"http.route": string;
|
83
|
+
"http.response.status_code": number;
|
84
|
+
}>;
|
85
|
+
|
39
86
|
/**
|
40
87
|
* Dictionary type for URL parameters.
|
41
88
|
*/
|
@@ -1173,4 +1220,4 @@ type DocsConfiguration = ({
|
|
1173
1220
|
type: 'swagger';
|
1174
1221
|
};
|
1175
1222
|
|
1176
|
-
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 ExtractBody, type ExtractContentType, type ExtractResponseBody, type ExtractedParamsObject, type FileBody, 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 JsonBody, 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 MultipartForm, type NestableRouterBasedHandler, type NumberOnlyObject, OpenTelemetryCollector, type ParamsDictionary, type ParamsObject, type PathBasedHandler, type PathMatch, type PathOrMiddlewareBasedHandler, type PathParamHttpContractDetails, type PathParamMethod, type QueryObject, type RawTypedResponseBody, type RequestContext, type ResolvedForklaunchRequest, type ResponseBody, type ResponseCompiledSchema, type ResponseShape, type ResponsesObject, type SchemaAuthMethods, type ServerSentEventBody, type StatusCode, type StringOnlyObject, TelemetryOptions, type TextBody, type TypedBody, type TypedHandler, type TypedMiddlewareDefinition, type TypedNestableMiddlewareDefinition, type TypedResponseBody, type UnknownBody, type UnknownResponseBody, type UrlEncodedForm, delete_, discriminateBody, discriminateResponseBodies, 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 };
|
1223
|
+
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 ExtractBody, type ExtractContentType, type ExtractResponseBody, type ExtractedParamsObject, type FileBody, 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 JsonBody, type LiveTypeFunction, type LiveTypeRouteDefinition, type LogFn, type LoggerMeta, type MapParamsSchema, type MapReqBodySchema, type MapReqHeadersSchema, type MapReqQuerySchema, type MapResBodyMapSchema, type MapResHeadersSchema, type MapSchema, type Method, type MetricType, type MetricsDefinition, type MiddlewareContractDetails, type MiddlewareOrMiddlewareWithTypedHandler, type MultipartForm, type NestableRouterBasedHandler, type NumberOnlyObject, OpenTelemetryCollector, type ParamsDictionary, type ParamsObject, type PathBasedHandler, type PathMatch, type PathOrMiddlewareBasedHandler, type PathParamHttpContractDetails, type PathParamMethod, type QueryObject, type RawTypedResponseBody, type RequestContext, type ResolvedForklaunchRequest, type ResponseBody, type ResponseCompiledSchema, type ResponseShape, type ResponsesObject, type SchemaAuthMethods, type ServerSentEventBody, type StatusCode, type StringOnlyObject, type TelemetryOptions, type TextBody, type TypedBody, type TypedHandler, type TypedMiddlewareDefinition, type TypedNestableMiddlewareDefinition, type TypedResponseBody, type UnknownBody, type UnknownResponseBody, type UrlEncodedForm, delete_, discriminateBody, discriminateResponseBodies, enrichExpressLikeSend, evaluateTelemetryOptions, generateSwaggerDocument, get, getCodeForStatus, head, httpRequestsTotalCounter, httpServerDurationHistogram, isClientError, isForklaunchRequest, isForklaunchRouter, isInformational, isRedirection, isServerError, isSuccessful, isValidStatusCode, logger, meta, metricsDefinitions, middleware, options, patch, post, put, recordMetric, trace, typedHandler };
|
@@ -0,0 +1,97 @@
|
|
1
|
+
import { Readable } from 'stream';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Interface representing a objectstore.
|
5
|
+
*/
|
6
|
+
interface ObjectStore<Client> {
|
7
|
+
/**
|
8
|
+
* Puts a record into the objectstore.
|
9
|
+
*
|
10
|
+
* @param {T} object - The object to put into the objectstore.
|
11
|
+
* @returns {Promise<void>} - A promise that resolves when the record is put into the objectstore.
|
12
|
+
*/
|
13
|
+
putObject<T>(object: T): Promise<void>;
|
14
|
+
/**
|
15
|
+
* Puts a batch of records into the objectstore.
|
16
|
+
*
|
17
|
+
* @param {T[]} objects - The objects to put into the objectstore.
|
18
|
+
* @returns {Promise<void>} - A promise that resolves when the records are put into the objectstore.
|
19
|
+
*/
|
20
|
+
putBatchObjects<T>(objects: T[]): Promise<void>;
|
21
|
+
/**
|
22
|
+
* Enqueues a record into the objectstore.
|
23
|
+
*
|
24
|
+
* @param {T} object - The object to enqueue into the objectstore.
|
25
|
+
* @returns {Promise<void>} - A promise that resolves when the record is enqueued into the objectstore.
|
26
|
+
*/
|
27
|
+
streamUploadObject<T>(object: T): Promise<void>;
|
28
|
+
/**
|
29
|
+
*
|
30
|
+
* Enqueues a batch of records into the objectstore.
|
31
|
+
*
|
32
|
+
* @param {T[]} objects - The objects to enqueue into the objectstore.
|
33
|
+
* @returns {Promise<void>} - A promise that resolves when the records are enqueued into the objectstore.
|
34
|
+
*/
|
35
|
+
streamUploadBatchObjects<T>(objects: T[]): Promise<void>;
|
36
|
+
/**
|
37
|
+
* Deletes a record from the objectstore.
|
38
|
+
*
|
39
|
+
* @param {string} objectKey - The key of the object to delete.
|
40
|
+
* @returns {Promise<void>} - A promise that resolves when the object is deleted from the objectstore.
|
41
|
+
*/
|
42
|
+
deleteObject(objectKey: string): Promise<void>;
|
43
|
+
/**
|
44
|
+
* Deletes a batch of records from the objectstore.
|
45
|
+
*
|
46
|
+
* @param {string[]} objectKeys - The keys of the objects to delete.
|
47
|
+
* @returns {Promise<void>} - A promise that resolves when the records are deleted from the objectstore.
|
48
|
+
*/
|
49
|
+
deleteBatchObjects(objectKeys: string[]): Promise<void>;
|
50
|
+
/**
|
51
|
+
* Reads a record from the objectstore.
|
52
|
+
*
|
53
|
+
* @param {string} objectKey - The key of the object to read.
|
54
|
+
* @returns {Promise<TtlCacheRecord>} - A promise that resolves with the objectstore record.
|
55
|
+
*/
|
56
|
+
readObject<T>(objectKey: string): Promise<T>;
|
57
|
+
/**
|
58
|
+
* Reads a batch of records from the objectstore.
|
59
|
+
*
|
60
|
+
* @param {string[]} objectKeys - The keys of the objects to read.
|
61
|
+
* @returns {Promise<TtlCacheRecord<T>[]>} - A promise that resolves with the objects.
|
62
|
+
*/
|
63
|
+
readBatchObjects<T>(objectKeys: string[]): Promise<T[]>;
|
64
|
+
/**
|
65
|
+
* Peeks at a record in the objectstore to check if it exists.
|
66
|
+
*
|
67
|
+
* @param {string} objectstoreRecordKey - The key of the objectstore record to peek at.
|
68
|
+
* @returns {Promise<Readable>} - A promise that resolves with a boolean indicating if the record exists.
|
69
|
+
*/
|
70
|
+
streamDownloadObject(objectKey: string): Promise<Readable>;
|
71
|
+
/**
|
72
|
+
* Peeks at a batch of records in the objectstore to check if they exist.
|
73
|
+
*
|
74
|
+
* @param {string[]} objectKeys - The keys of the objectstore records to peek at or a prefix to match.
|
75
|
+
* @returns {Promise<Readable[]>} - A promise that resolves with an array of booleans indicating if the records exist.
|
76
|
+
*/
|
77
|
+
streamDownloadBatchObjects(objectKeys: string[]): Promise<Readable[]>;
|
78
|
+
/**
|
79
|
+
* Gets the underlying objectstore client instance.
|
80
|
+
*
|
81
|
+
* @returns {Client} The objectstore client instance
|
82
|
+
*/
|
83
|
+
getClient(): Client;
|
84
|
+
}
|
85
|
+
|
86
|
+
/**
|
87
|
+
* Creates a function that generates objectstore keys with a given prefix.
|
88
|
+
*
|
89
|
+
* @param {string} objectStoreKeyPrefix - The prefix to use for all objectstore keys
|
90
|
+
* @returns {Function} A function that takes an ID and returns a formatted objectstore key
|
91
|
+
* @example
|
92
|
+
* const createUserObjectStoreKey = createObjectStoreKey('user');
|
93
|
+
* const key = createUserObjectStoreKey('123'); // Returns 'user:123'
|
94
|
+
*/
|
95
|
+
declare const createObjectStoreKey: (objectStoreKeyPrefix: string) => (id: string | string[]) => string;
|
96
|
+
|
97
|
+
export { type ObjectStore, createObjectStoreKey };
|
@@ -0,0 +1,97 @@
|
|
1
|
+
import { Readable } from 'stream';
|
2
|
+
|
3
|
+
/**
|
4
|
+
* Interface representing a objectstore.
|
5
|
+
*/
|
6
|
+
interface ObjectStore<Client> {
|
7
|
+
/**
|
8
|
+
* Puts a record into the objectstore.
|
9
|
+
*
|
10
|
+
* @param {T} object - The object to put into the objectstore.
|
11
|
+
* @returns {Promise<void>} - A promise that resolves when the record is put into the objectstore.
|
12
|
+
*/
|
13
|
+
putObject<T>(object: T): Promise<void>;
|
14
|
+
/**
|
15
|
+
* Puts a batch of records into the objectstore.
|
16
|
+
*
|
17
|
+
* @param {T[]} objects - The objects to put into the objectstore.
|
18
|
+
* @returns {Promise<void>} - A promise that resolves when the records are put into the objectstore.
|
19
|
+
*/
|
20
|
+
putBatchObjects<T>(objects: T[]): Promise<void>;
|
21
|
+
/**
|
22
|
+
* Enqueues a record into the objectstore.
|
23
|
+
*
|
24
|
+
* @param {T} object - The object to enqueue into the objectstore.
|
25
|
+
* @returns {Promise<void>} - A promise that resolves when the record is enqueued into the objectstore.
|
26
|
+
*/
|
27
|
+
streamUploadObject<T>(object: T): Promise<void>;
|
28
|
+
/**
|
29
|
+
*
|
30
|
+
* Enqueues a batch of records into the objectstore.
|
31
|
+
*
|
32
|
+
* @param {T[]} objects - The objects to enqueue into the objectstore.
|
33
|
+
* @returns {Promise<void>} - A promise that resolves when the records are enqueued into the objectstore.
|
34
|
+
*/
|
35
|
+
streamUploadBatchObjects<T>(objects: T[]): Promise<void>;
|
36
|
+
/**
|
37
|
+
* Deletes a record from the objectstore.
|
38
|
+
*
|
39
|
+
* @param {string} objectKey - The key of the object to delete.
|
40
|
+
* @returns {Promise<void>} - A promise that resolves when the object is deleted from the objectstore.
|
41
|
+
*/
|
42
|
+
deleteObject(objectKey: string): Promise<void>;
|
43
|
+
/**
|
44
|
+
* Deletes a batch of records from the objectstore.
|
45
|
+
*
|
46
|
+
* @param {string[]} objectKeys - The keys of the objects to delete.
|
47
|
+
* @returns {Promise<void>} - A promise that resolves when the records are deleted from the objectstore.
|
48
|
+
*/
|
49
|
+
deleteBatchObjects(objectKeys: string[]): Promise<void>;
|
50
|
+
/**
|
51
|
+
* Reads a record from the objectstore.
|
52
|
+
*
|
53
|
+
* @param {string} objectKey - The key of the object to read.
|
54
|
+
* @returns {Promise<TtlCacheRecord>} - A promise that resolves with the objectstore record.
|
55
|
+
*/
|
56
|
+
readObject<T>(objectKey: string): Promise<T>;
|
57
|
+
/**
|
58
|
+
* Reads a batch of records from the objectstore.
|
59
|
+
*
|
60
|
+
* @param {string[]} objectKeys - The keys of the objects to read.
|
61
|
+
* @returns {Promise<TtlCacheRecord<T>[]>} - A promise that resolves with the objects.
|
62
|
+
*/
|
63
|
+
readBatchObjects<T>(objectKeys: string[]): Promise<T[]>;
|
64
|
+
/**
|
65
|
+
* Peeks at a record in the objectstore to check if it exists.
|
66
|
+
*
|
67
|
+
* @param {string} objectstoreRecordKey - The key of the objectstore record to peek at.
|
68
|
+
* @returns {Promise<Readable>} - A promise that resolves with a boolean indicating if the record exists.
|
69
|
+
*/
|
70
|
+
streamDownloadObject(objectKey: string): Promise<Readable>;
|
71
|
+
/**
|
72
|
+
* Peeks at a batch of records in the objectstore to check if they exist.
|
73
|
+
*
|
74
|
+
* @param {string[]} objectKeys - The keys of the objectstore records to peek at or a prefix to match.
|
75
|
+
* @returns {Promise<Readable[]>} - A promise that resolves with an array of booleans indicating if the records exist.
|
76
|
+
*/
|
77
|
+
streamDownloadBatchObjects(objectKeys: string[]): Promise<Readable[]>;
|
78
|
+
/**
|
79
|
+
* Gets the underlying objectstore client instance.
|
80
|
+
*
|
81
|
+
* @returns {Client} The objectstore client instance
|
82
|
+
*/
|
83
|
+
getClient(): Client;
|
84
|
+
}
|
85
|
+
|
86
|
+
/**
|
87
|
+
* Creates a function that generates objectstore keys with a given prefix.
|
88
|
+
*
|
89
|
+
* @param {string} objectStoreKeyPrefix - The prefix to use for all objectstore keys
|
90
|
+
* @returns {Function} A function that takes an ID and returns a formatted objectstore key
|
91
|
+
* @example
|
92
|
+
* const createUserObjectStoreKey = createObjectStoreKey('user');
|
93
|
+
* const key = createUserObjectStoreKey('123'); // Returns 'user:123'
|
94
|
+
*/
|
95
|
+
declare const createObjectStoreKey: (objectStoreKeyPrefix: string) => (id: string | string[]) => string;
|
96
|
+
|
97
|
+
export { type ObjectStore, createObjectStoreKey };
|
@@ -0,0 +1,36 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __defProp = Object.defineProperty;
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
6
|
+
var __export = (target, all) => {
|
7
|
+
for (var name in all)
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
9
|
+
};
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
12
|
+
for (let key of __getOwnPropNames(from))
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
15
|
+
}
|
16
|
+
return to;
|
17
|
+
};
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
19
|
+
|
20
|
+
// src/objectstore/index.ts
|
21
|
+
var objectstore_exports = {};
|
22
|
+
__export(objectstore_exports, {
|
23
|
+
createObjectStoreKey: () => createObjectStoreKey
|
24
|
+
});
|
25
|
+
module.exports = __toCommonJS(objectstore_exports);
|
26
|
+
|
27
|
+
// src/objectstore/objectstoreKey.ts
|
28
|
+
var createObjectStoreKey = (objectStoreKeyPrefix) => (id) => {
|
29
|
+
const idString = Array.isArray(id) ? id.join("-") : id;
|
30
|
+
return `${objectStoreKeyPrefix}-${idString}`;
|
31
|
+
};
|
32
|
+
// Annotate the CommonJS export names for ESM import in node:
|
33
|
+
0 && (module.exports = {
|
34
|
+
createObjectStoreKey
|
35
|
+
});
|
36
|
+
//# sourceMappingURL=index.js.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"sources":["../../../src/objectstore/index.ts","../../../src/objectstore/objectstoreKey.ts"],"sourcesContent":["export * from './interfaces/objectstore.interface';\nexport * from './objectstoreKey';\n","/**\n * Creates a function that generates objectstore keys with a given prefix.\n *\n * @param {string} objectStoreKeyPrefix - The prefix to use for all objectstore keys\n * @returns {Function} A function that takes an ID and returns a formatted objectstore key\n * @example\n * const createUserObjectStoreKey = createObjectStoreKey('user');\n * const key = createUserObjectStoreKey('123'); // Returns 'user:123'\n */\nexport const createObjectStoreKey =\n (objectStoreKeyPrefix: string) => (id: string | string[]) => {\n const idString = Array.isArray(id) ? id.join('-') : id;\n return `${objectStoreKeyPrefix}-${idString}`;\n };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACSO,IAAM,uBACX,CAAC,yBAAiC,CAAC,OAA0B;AAC3D,QAAM,WAAW,MAAM,QAAQ,EAAE,IAAI,GAAG,KAAK,GAAG,IAAI;AACpD,SAAO,GAAG,oBAAoB,IAAI,QAAQ;AAC5C;","names":[]}
|
@@ -0,0 +1,9 @@
|
|
1
|
+
// src/objectstore/objectstoreKey.ts
|
2
|
+
var createObjectStoreKey = (objectStoreKeyPrefix) => (id) => {
|
3
|
+
const idString = Array.isArray(id) ? id.join("-") : id;
|
4
|
+
return `${objectStoreKeyPrefix}-${idString}`;
|
5
|
+
};
|
6
|
+
export {
|
7
|
+
createObjectStoreKey
|
8
|
+
};
|
9
|
+
//# sourceMappingURL=index.mjs.map
|
@@ -0,0 +1 @@
|
|
1
|
+
{"version":3,"sources":["../../../src/objectstore/objectstoreKey.ts"],"sourcesContent":["/**\n * Creates a function that generates objectstore keys with a given prefix.\n *\n * @param {string} objectStoreKeyPrefix - The prefix to use for all objectstore keys\n * @returns {Function} A function that takes an ID and returns a formatted objectstore key\n * @example\n * const createUserObjectStoreKey = createObjectStoreKey('user');\n * const key = createUserObjectStoreKey('123'); // Returns 'user:123'\n */\nexport const createObjectStoreKey =\n (objectStoreKeyPrefix: string) => (id: string | string[]) => {\n const idString = Array.isArray(id) ? id.join('-') : id;\n return `${objectStoreKeyPrefix}-${idString}`;\n };\n"],"mappings":";AASO,IAAM,uBACX,CAAC,yBAAiC,CAAC,OAA0B;AAC3D,QAAM,WAAW,MAAM,QAAQ,EAAE,IAAI,GAAG,KAAK,GAAG,IAAI;AACpD,SAAO,GAAG,oBAAoB,IAAI,QAAQ;AAC5C;","names":[]}
|