@forklaunch/core 0.3.6 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,6 +2,9 @@ import { AnySchemaValidator, UnboxedObjectSchema, IdiomaticSchema, Schema, Schem
2
2
  import { Prettify, RemoveTrailingSlash, MakePropertyOptionalIfChildrenOptional } from '@forklaunch/common';
3
3
  import { ParsedQs } from 'qs';
4
4
  import { OpenAPIObject } from 'openapi3-ts/oas31';
5
+ import { Counter, Gauge, Histogram, UpDownCounter, ObservableCounter, ObservableGauge, ObservableUpDownCounter } from '@opentelemetry/api';
6
+ import { AnyValueMap } from '@opentelemetry/api-logs';
7
+ import { LevelWithSilentOrString, LevelWithSilent } from 'pino';
5
8
 
6
9
  interface PathBasedHandler<RouterHandler> {
7
10
  (path: string, ...handlers: RouterHandler[]): unknown;
@@ -270,8 +273,12 @@ interface ForklaunchRequest<SV extends AnySchemaValidator, P extends ParamsDicti
270
273
  schemaValidator: SchemaValidator;
271
274
  /** Method */
272
275
  method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD' | 'CONNECT' | 'TRACE';
276
+ /** Request path */
277
+ path: string;
273
278
  /** Request schema, compiled */
274
279
  requestSchema: unknown;
280
+ /** Original path */
281
+ originalPath: string;
275
282
  }
276
283
  /**
277
284
  * Represents the types of data that can be sent in a response.
@@ -805,6 +812,39 @@ declare function typedHandler<SV extends AnySchemaValidator, ContractMethod exte
805
812
  */
806
813
  declare function generateSwaggerDocument<SV extends AnySchemaValidator>(schemaValidator: SV, port: string | number, routers: ForklaunchRouter<SV>[]): OpenAPIObject;
807
814
 
815
+ type Request = {
816
+ method: string;
817
+ originalPath: string;
818
+ path: string;
819
+ contractDetails: {
820
+ name: string;
821
+ };
822
+ };
823
+ type Response = {
824
+ statusCode: number;
825
+ };
826
+
827
+ declare const emitLoggerError: (req: Request, res: Response, errorString: string) => void;
828
+
829
+ 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;
830
+ type MetricsDefinition = Record<string, 'counter' | 'gauge' | 'histogram' | 'upDownCounter' | 'observableCounter' | 'observableGauge' | 'observableUpDownCounter'>;
831
+
832
+ declare class OpenTelemetryCollector<AppliedMetricsDefinition extends MetricsDefinition> {
833
+ private readonly serviceName;
834
+ private readonly logger;
835
+ private readonly metrics;
836
+ constructor(serviceName: string, level?: LevelWithSilentOrString, metricDefinitions?: AppliedMetricsDefinition);
837
+ log(level: LevelWithSilent, msg: string, meta?: AnyValueMap): void;
838
+ getMetric<T extends keyof AppliedMetricsDefinition>(metricId: T): MetricType<AppliedMetricsDefinition[T]>;
839
+ }
840
+ declare const httpRequestsTotalCounter: Counter<{
841
+ 'service.name': string;
842
+ 'api.name': string;
843
+ "http.method": string;
844
+ "http.route": string;
845
+ "http.status_code": number;
846
+ }>;
847
+
808
848
  /**
809
849
  * Enhances the Express-like `send` method with additional logic for response handling and validation.
810
850
  *
@@ -831,7 +871,7 @@ declare function generateSwaggerDocument<SV extends AnySchemaValidator>(schemaVa
831
871
  *
832
872
  * @returns {unknown} The return value of the original `send` method, typically the response itself.
833
873
  */
834
- declare function enrichExpressLikeSend<SV extends AnySchemaValidator, P extends ParamsDictionary, ResBodyMap extends Record<number, unknown>, ReqBody extends Record<string, unknown>, ReqQuery extends ParsedQs, ReqHeaders extends Record<string, string>, ResHeaders extends Record<string, string>, LocalsObj extends Record<string, unknown>>(instance: unknown, req: ForklaunchRequest<SV, P, ReqBody, ReqQuery, ReqHeaders>, res: ForklaunchResponse<ResBodyMap, ForklaunchResHeaders & ResHeaders, LocalsObj>, originalSend: ForklaunchStatusResponse<ForklaunchSendableData>['send'], data: ForklaunchSendableData, shouldEnrich: boolean): void;
874
+ declare function enrichExpressLikeSend<SV extends AnySchemaValidator, P extends ParamsDictionary, ResBodyMap extends Record<number, unknown>, ReqBody extends Record<string, unknown>, ReqQuery extends ParsedQs, ReqHeaders extends Record<string, string>, ResHeaders extends Record<string, string>, LocalsObj extends Record<string, unknown>>(instance: unknown, req: ForklaunchRequest<SV, P, ReqBody, ReqQuery, ReqHeaders>, res: ForklaunchResponse<ResBodyMap, ForklaunchResHeaders & ResHeaders, LocalsObj>, originalSend: ForklaunchStatusResponse<ForklaunchSendableData>['send'] | ForklaunchStatusResponse<ForklaunchSendableData>['json'], data: ForklaunchSendableData, shouldEnrich: boolean): void;
835
875
 
836
876
  /**
837
877
  * Object-map of the HTTP Status Codes. Maps from the status code, to the
@@ -907,4 +947,6 @@ declare const isServerError: (code: StatusCode) => boolean;
907
947
  */
908
948
  declare const getCodeForStatus: (status: string) => null | StatusCode;
909
949
 
910
- export { type ApiClient, type AuthMethods, type AuthMethodsBase, type Body, type BodyObject, type ConstrainedForklaunchRouter, type ContractDetails, type ContractDetailsExpressLikeSchemaHandler, type ContractDetailsOrMiddlewareOrTypedHandler, type ErrorContainer, type ExpressLikeAuthMapper, type ExpressLikeHandler, type ExpressLikeRouter, type ExpressLikeSchemaAuthMapper, type ExpressLikeSchemaHandler, type ExtractedParamsObject, type ForklaunchBaseRequest, ForklaunchExpressLikeApplication, ForklaunchExpressLikeRouter, type ForklaunchNextFunction, type ForklaunchRequest, type ForklaunchResErrors, type ForklaunchResHeaders, type ForklaunchResponse, type ForklaunchRoute, type ForklaunchRouter, type ForklaunchSendableData, type ForklaunchStatusResponse, HTTPStatuses, type HeadersObject, type HttpContractDetails, type HttpMethod, type LiveTypeFunction, type LiveTypeRouteDefinition, type MapParamsSchema, type MapReqBodySchema, type MapReqHeadersSchema, type MapReqQuerySchema, type MapResBodyMapSchema, type MapResHeadersSchema, type MapSchema, type Method, type MiddlewareContractDetails, type MiddlewareOrMiddlewareWithTypedHandler, type NestableRouterBasedHandler, type NumberOnlyObject, type ParamsDictionary, type ParamsObject, type PathBasedHandler, type PathMatch, type PathOrMiddlewareBasedHandler, type PathParamHttpContractDetails, type PathParamMethod, type QueryObject, type RequestContext, type ResponseCompiledSchema, type ResponseShape, type ResponsesObject, type SchemaAuthMethods, type StatusCode, type StringOnlyObject, type TypedHandler, type TypedMiddlewareDefinition, type TypedNestableMiddlewareDefinition, delete_, enrichExpressLikeSend, generateSwaggerDocument, get, getCodeForStatus, head, isClientError, isForklaunchRouter, isInformational, isRedirection, isServerError, isSuccessful, isValidStatusCode, middleware, options, patch, post, put, trace, typedHandler };
950
+ declare function recordMetric(req: Request, res: Response): void;
951
+
952
+ export { type ApiClient, type AuthMethods, type AuthMethodsBase, type Body, type BodyObject, type ConstrainedForklaunchRouter, type ContractDetails, type ContractDetailsExpressLikeSchemaHandler, type ContractDetailsOrMiddlewareOrTypedHandler, type ErrorContainer, type ExpressLikeAuthMapper, type ExpressLikeHandler, type ExpressLikeRouter, type ExpressLikeSchemaAuthMapper, type ExpressLikeSchemaHandler, type ExtractedParamsObject, type ForklaunchBaseRequest, ForklaunchExpressLikeApplication, ForklaunchExpressLikeRouter, type ForklaunchNextFunction, type ForklaunchRequest, type ForklaunchResErrors, type ForklaunchResHeaders, type ForklaunchResponse, type ForklaunchRoute, type ForklaunchRouter, type ForklaunchSendableData, type ForklaunchStatusResponse, HTTPStatuses, type HeadersObject, type HttpContractDetails, type HttpMethod, type LiveTypeFunction, type LiveTypeRouteDefinition, 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 NestableRouterBasedHandler, type NumberOnlyObject, OpenTelemetryCollector, type ParamsDictionary, type ParamsObject, type PathBasedHandler, type PathMatch, type PathOrMiddlewareBasedHandler, type PathParamHttpContractDetails, type PathParamMethod, type QueryObject, type RequestContext, type ResponseCompiledSchema, type ResponseShape, type ResponsesObject, type SchemaAuthMethods, type StatusCode, type StringOnlyObject, type TypedHandler, type TypedMiddlewareDefinition, type TypedNestableMiddlewareDefinition, delete_, emitLoggerError, enrichExpressLikeSend, generateSwaggerDocument, get, getCodeForStatus, head, httpRequestsTotalCounter, isClientError, isForklaunchRouter, isInformational, isRedirection, isServerError, isSuccessful, isValidStatusCode, middleware, options, patch, post, put, recordMetric, trace, typedHandler };
@@ -2,6 +2,9 @@ import { AnySchemaValidator, UnboxedObjectSchema, IdiomaticSchema, Schema, Schem
2
2
  import { Prettify, RemoveTrailingSlash, MakePropertyOptionalIfChildrenOptional } from '@forklaunch/common';
3
3
  import { ParsedQs } from 'qs';
4
4
  import { OpenAPIObject } from 'openapi3-ts/oas31';
5
+ import { Counter, Gauge, Histogram, UpDownCounter, ObservableCounter, ObservableGauge, ObservableUpDownCounter } from '@opentelemetry/api';
6
+ import { AnyValueMap } from '@opentelemetry/api-logs';
7
+ import { LevelWithSilentOrString, LevelWithSilent } from 'pino';
5
8
 
6
9
  interface PathBasedHandler<RouterHandler> {
7
10
  (path: string, ...handlers: RouterHandler[]): unknown;
@@ -270,8 +273,12 @@ interface ForklaunchRequest<SV extends AnySchemaValidator, P extends ParamsDicti
270
273
  schemaValidator: SchemaValidator;
271
274
  /** Method */
272
275
  method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD' | 'CONNECT' | 'TRACE';
276
+ /** Request path */
277
+ path: string;
273
278
  /** Request schema, compiled */
274
279
  requestSchema: unknown;
280
+ /** Original path */
281
+ originalPath: string;
275
282
  }
276
283
  /**
277
284
  * Represents the types of data that can be sent in a response.
@@ -805,6 +812,39 @@ declare function typedHandler<SV extends AnySchemaValidator, ContractMethod exte
805
812
  */
806
813
  declare function generateSwaggerDocument<SV extends AnySchemaValidator>(schemaValidator: SV, port: string | number, routers: ForklaunchRouter<SV>[]): OpenAPIObject;
807
814
 
815
+ type Request = {
816
+ method: string;
817
+ originalPath: string;
818
+ path: string;
819
+ contractDetails: {
820
+ name: string;
821
+ };
822
+ };
823
+ type Response = {
824
+ statusCode: number;
825
+ };
826
+
827
+ declare const emitLoggerError: (req: Request, res: Response, errorString: string) => void;
828
+
829
+ 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;
830
+ type MetricsDefinition = Record<string, 'counter' | 'gauge' | 'histogram' | 'upDownCounter' | 'observableCounter' | 'observableGauge' | 'observableUpDownCounter'>;
831
+
832
+ declare class OpenTelemetryCollector<AppliedMetricsDefinition extends MetricsDefinition> {
833
+ private readonly serviceName;
834
+ private readonly logger;
835
+ private readonly metrics;
836
+ constructor(serviceName: string, level?: LevelWithSilentOrString, metricDefinitions?: AppliedMetricsDefinition);
837
+ log(level: LevelWithSilent, msg: string, meta?: AnyValueMap): void;
838
+ getMetric<T extends keyof AppliedMetricsDefinition>(metricId: T): MetricType<AppliedMetricsDefinition[T]>;
839
+ }
840
+ declare const httpRequestsTotalCounter: Counter<{
841
+ 'service.name': string;
842
+ 'api.name': string;
843
+ "http.method": string;
844
+ "http.route": string;
845
+ "http.status_code": number;
846
+ }>;
847
+
808
848
  /**
809
849
  * Enhances the Express-like `send` method with additional logic for response handling and validation.
810
850
  *
@@ -831,7 +871,7 @@ declare function generateSwaggerDocument<SV extends AnySchemaValidator>(schemaVa
831
871
  *
832
872
  * @returns {unknown} The return value of the original `send` method, typically the response itself.
833
873
  */
834
- declare function enrichExpressLikeSend<SV extends AnySchemaValidator, P extends ParamsDictionary, ResBodyMap extends Record<number, unknown>, ReqBody extends Record<string, unknown>, ReqQuery extends ParsedQs, ReqHeaders extends Record<string, string>, ResHeaders extends Record<string, string>, LocalsObj extends Record<string, unknown>>(instance: unknown, req: ForklaunchRequest<SV, P, ReqBody, ReqQuery, ReqHeaders>, res: ForklaunchResponse<ResBodyMap, ForklaunchResHeaders & ResHeaders, LocalsObj>, originalSend: ForklaunchStatusResponse<ForklaunchSendableData>['send'], data: ForklaunchSendableData, shouldEnrich: boolean): void;
874
+ declare function enrichExpressLikeSend<SV extends AnySchemaValidator, P extends ParamsDictionary, ResBodyMap extends Record<number, unknown>, ReqBody extends Record<string, unknown>, ReqQuery extends ParsedQs, ReqHeaders extends Record<string, string>, ResHeaders extends Record<string, string>, LocalsObj extends Record<string, unknown>>(instance: unknown, req: ForklaunchRequest<SV, P, ReqBody, ReqQuery, ReqHeaders>, res: ForklaunchResponse<ResBodyMap, ForklaunchResHeaders & ResHeaders, LocalsObj>, originalSend: ForklaunchStatusResponse<ForklaunchSendableData>['send'] | ForklaunchStatusResponse<ForklaunchSendableData>['json'], data: ForklaunchSendableData, shouldEnrich: boolean): void;
835
875
 
836
876
  /**
837
877
  * Object-map of the HTTP Status Codes. Maps from the status code, to the
@@ -907,4 +947,6 @@ declare const isServerError: (code: StatusCode) => boolean;
907
947
  */
908
948
  declare const getCodeForStatus: (status: string) => null | StatusCode;
909
949
 
910
- export { type ApiClient, type AuthMethods, type AuthMethodsBase, type Body, type BodyObject, type ConstrainedForklaunchRouter, type ContractDetails, type ContractDetailsExpressLikeSchemaHandler, type ContractDetailsOrMiddlewareOrTypedHandler, type ErrorContainer, type ExpressLikeAuthMapper, type ExpressLikeHandler, type ExpressLikeRouter, type ExpressLikeSchemaAuthMapper, type ExpressLikeSchemaHandler, type ExtractedParamsObject, type ForklaunchBaseRequest, ForklaunchExpressLikeApplication, ForklaunchExpressLikeRouter, type ForklaunchNextFunction, type ForklaunchRequest, type ForklaunchResErrors, type ForklaunchResHeaders, type ForklaunchResponse, type ForklaunchRoute, type ForklaunchRouter, type ForklaunchSendableData, type ForklaunchStatusResponse, HTTPStatuses, type HeadersObject, type HttpContractDetails, type HttpMethod, type LiveTypeFunction, type LiveTypeRouteDefinition, type MapParamsSchema, type MapReqBodySchema, type MapReqHeadersSchema, type MapReqQuerySchema, type MapResBodyMapSchema, type MapResHeadersSchema, type MapSchema, type Method, type MiddlewareContractDetails, type MiddlewareOrMiddlewareWithTypedHandler, type NestableRouterBasedHandler, type NumberOnlyObject, type ParamsDictionary, type ParamsObject, type PathBasedHandler, type PathMatch, type PathOrMiddlewareBasedHandler, type PathParamHttpContractDetails, type PathParamMethod, type QueryObject, type RequestContext, type ResponseCompiledSchema, type ResponseShape, type ResponsesObject, type SchemaAuthMethods, type StatusCode, type StringOnlyObject, type TypedHandler, type TypedMiddlewareDefinition, type TypedNestableMiddlewareDefinition, delete_, enrichExpressLikeSend, generateSwaggerDocument, get, getCodeForStatus, head, isClientError, isForklaunchRouter, isInformational, isRedirection, isServerError, isSuccessful, isValidStatusCode, middleware, options, patch, post, put, trace, typedHandler };
950
+ declare function recordMetric(req: Request, res: Response): void;
951
+
952
+ export { type ApiClient, type AuthMethods, type AuthMethodsBase, type Body, type BodyObject, type ConstrainedForklaunchRouter, type ContractDetails, type ContractDetailsExpressLikeSchemaHandler, type ContractDetailsOrMiddlewareOrTypedHandler, type ErrorContainer, type ExpressLikeAuthMapper, type ExpressLikeHandler, type ExpressLikeRouter, type ExpressLikeSchemaAuthMapper, type ExpressLikeSchemaHandler, type ExtractedParamsObject, type ForklaunchBaseRequest, ForklaunchExpressLikeApplication, ForklaunchExpressLikeRouter, type ForklaunchNextFunction, type ForklaunchRequest, type ForklaunchResErrors, type ForklaunchResHeaders, type ForklaunchResponse, type ForklaunchRoute, type ForklaunchRouter, type ForklaunchSendableData, type ForklaunchStatusResponse, HTTPStatuses, type HeadersObject, type HttpContractDetails, type HttpMethod, type LiveTypeFunction, type LiveTypeRouteDefinition, 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 NestableRouterBasedHandler, type NumberOnlyObject, OpenTelemetryCollector, type ParamsDictionary, type ParamsObject, type PathBasedHandler, type PathMatch, type PathOrMiddlewareBasedHandler, type PathParamHttpContractDetails, type PathParamMethod, type QueryObject, type RequestContext, type ResponseCompiledSchema, type ResponseShape, type ResponsesObject, type SchemaAuthMethods, type StatusCode, type StringOnlyObject, type TypedHandler, type TypedMiddlewareDefinition, type TypedNestableMiddlewareDefinition, delete_, emitLoggerError, enrichExpressLikeSend, generateSwaggerDocument, get, getCodeForStatus, head, httpRequestsTotalCounter, isClientError, isForklaunchRouter, isInformational, isRedirection, isServerError, isSuccessful, isValidStatusCode, middleware, options, patch, post, put, recordMetric, trace, typedHandler };
package/lib/http/index.js CHANGED
@@ -33,12 +33,15 @@ __export(http_exports, {
33
33
  ForklaunchExpressLikeApplication: () => ForklaunchExpressLikeApplication,
34
34
  ForklaunchExpressLikeRouter: () => ForklaunchExpressLikeRouter,
35
35
  HTTPStatuses: () => HTTPStatuses,
36
+ OpenTelemetryCollector: () => OpenTelemetryCollector,
36
37
  delete_: () => delete_,
38
+ emitLoggerError: () => emitLoggerError,
37
39
  enrichExpressLikeSend: () => enrichExpressLikeSend,
38
40
  generateSwaggerDocument: () => generateSwaggerDocument,
39
41
  get: () => get,
40
42
  getCodeForStatus: () => getCodeForStatus,
41
43
  head: () => head,
44
+ httpRequestsTotalCounter: () => httpRequestsTotalCounter,
42
45
  isClientError: () => isClientError,
43
46
  isForklaunchRouter: () => isForklaunchRouter,
44
47
  isInformational: () => isInformational,
@@ -51,11 +54,39 @@ __export(http_exports, {
51
54
  patch: () => patch,
52
55
  post: () => post,
53
56
  put: () => put,
57
+ recordMetric: () => recordMetric,
54
58
  trace: () => trace,
55
59
  typedHandler: () => typedHandler
56
60
  });
57
61
  module.exports = __toCommonJS(http_exports);
58
62
 
63
+ // src/http/middleware/request/cors.middleware.ts
64
+ var import_cors = __toESM(require("cors"));
65
+ function cors(req, res, next) {
66
+ if (req.method === "OPTIONS") {
67
+ res.cors = true;
68
+ }
69
+ (0, import_cors.default)()(req, res, next ?? (() => {
70
+ }));
71
+ }
72
+
73
+ // src/http/middleware/request/createContext.middleware.ts
74
+ var import_uuid = require("uuid");
75
+ function createContext(schemaValidator) {
76
+ return function setContext(req, res, next) {
77
+ req.schemaValidator = schemaValidator;
78
+ let correlationId = (0, import_uuid.v4)();
79
+ if (req.headers["x-correlation-id"]) {
80
+ correlationId = req.headers["x-correlation-id"];
81
+ }
82
+ res.setHeader("x-correlation-id", correlationId);
83
+ req.context = {
84
+ correlationId
85
+ };
86
+ next?.();
87
+ };
88
+ }
89
+
59
90
  // src/http/guards/isForklaunchRouter.ts
60
91
  function isForklaunchRouter(maybeForklaunchRouter) {
61
92
  return maybeForklaunchRouter != null && typeof maybeForklaunchRouter === "object" && "basePath" in maybeForklaunchRouter && "routes" in maybeForklaunchRouter && Array.isArray(maybeForklaunchRouter.routes);
@@ -215,55 +246,25 @@ async function checkAuthorizationToken(authorizationMethod, authorizationToken,
215
246
  return [401, "Invalid Authorization method."];
216
247
  }
217
248
  async function parseRequestAuth(req, res, next) {
218
- console.debug("[MIDDLEWARE] parseRequest started");
219
249
  const auth = req.contractDetails.auth;
220
250
  if (auth) {
221
- const errorAndMessage = await checkAuthorizationToken(
251
+ const [error, message] = await checkAuthorizationToken(
222
252
  auth,
223
253
  req.headers[(auth.method === "other" ? auth.headerName : void 0) ?? "Authorization"],
224
254
  req
225
- );
226
- if (Array.isArray(errorAndMessage)) {
227
- res.status(errorAndMessage[0]).send(errorAndMessage[1]);
228
- next?.(new Error(errorAndMessage[1]));
255
+ ) ?? [];
256
+ if (error != null) {
257
+ res.status(error).send(message);
258
+ next?.(new Error(message));
229
259
  }
230
260
  }
231
261
  next?.();
232
262
  }
233
263
 
234
- // src/http/middleware/request/cors.middleware.ts
235
- var import_cors = __toESM(require("cors"));
236
- function cors(req, res, next) {
237
- console.debug("[MIDDLEWARE] cors started");
238
- if (req.method === "OPTIONS") {
239
- res.cors = true;
240
- }
241
- (0, import_cors.default)()(req, res, next ?? (() => {
242
- }));
243
- }
244
-
245
- // src/http/middleware/request/createContext.middleware.ts
246
- var import_uuid = require("uuid");
247
- function createContext(schemaValidator) {
248
- return (req, res, next) => {
249
- console.debug("[MIDDLEWARE] createRequestContext started");
250
- req.schemaValidator = schemaValidator;
251
- let correlationId = (0, import_uuid.v4)();
252
- if (req.headers["x-correlation-id"]) {
253
- correlationId = req.headers["x-correlation-id"];
254
- }
255
- res.setHeader("x-correlation-id", correlationId);
256
- req.context = {
257
- correlationId
258
- };
259
- next?.();
260
- };
261
- }
262
-
263
264
  // src/http/middleware/request/enrichDetails.middleware.ts
264
- function enrichDetails(contractDetails, requestSchema, responseSchemas) {
265
+ function enrichDetails(path, contractDetails, requestSchema, responseSchemas) {
265
266
  return (req, res, next) => {
266
- console.debug("[MIDDLEWARE] enrichRequestDetails started");
267
+ req.originalPath = path;
267
268
  req.contractDetails = contractDetails;
268
269
  req.requestSchema = requestSchema;
269
270
  res.responseSchemas = responseSchemas;
@@ -281,7 +282,6 @@ function isResponseShape(maybeResponseShape) {
281
282
 
282
283
  // src/http/middleware/request/parse.middleware.ts
283
284
  function parse(req, _res, next) {
284
- console.debug("[MIDDLEWARE] parseRequest started");
285
285
  const request = {
286
286
  params: req.params,
287
287
  query: req.query,
@@ -319,8 +319,6 @@ var ForklaunchExpressLikeRouter = class {
319
319
  this.schemaValidator = schemaValidator;
320
320
  this.internal = internal;
321
321
  this.basePath = basePath;
322
- this.internal.use(createContext(this.schemaValidator));
323
- this.internal.use(cors);
324
322
  }
325
323
  requestHandler;
326
324
  routers = [];
@@ -332,9 +330,14 @@ var ForklaunchExpressLikeRouter = class {
332
330
  * @param {PathParamHttpContractDetails<SV> | HttpContractDetails<SV>} contractDetails - The contract details.
333
331
  * @returns {MiddlewareHandler<SV>[]} - The resolved middlewares.
334
332
  */
335
- #resolveMiddlewares(contractDetails, requestSchema, responseSchemas) {
333
+ #resolveMiddlewares(path, contractDetails, requestSchema, responseSchemas) {
336
334
  return [
337
- enrichDetails(contractDetails, requestSchema, responseSchemas),
335
+ enrichDetails(
336
+ `${this.basePath}${path}`,
337
+ contractDetails,
338
+ requestSchema,
339
+ responseSchemas
340
+ ),
338
341
  parse,
339
342
  parseRequestAuth
340
343
  ];
@@ -359,10 +362,10 @@ var ForklaunchExpressLikeRouter = class {
359
362
  try {
360
363
  await requestHandler(req, res, next);
361
364
  } catch (error) {
362
- next?.(error);
363
- console.error(error);
364
- if (!res.headersSent) {
365
- res.status(500).send("Internal Server Error");
365
+ if (next) {
366
+ next(error);
367
+ } else {
368
+ throw error;
366
369
  }
367
370
  }
368
371
  };
@@ -534,7 +537,7 @@ var ForklaunchExpressLikeRouter = class {
534
537
  const controllerHandler = this.#extractControllerHandler(handlers);
535
538
  registrationMethod.bind(this.internal)(
536
539
  path,
537
- ...this.#resolveMiddlewares(contractDetails, requestSchema, responseSchemas).concat(
540
+ ...this.#resolveMiddlewares(path, contractDetails, requestSchema, responseSchemas).concat(
538
541
  handlers
539
542
  ),
540
543
  this.#parseAndRunControllerHandler(controllerHandler)
@@ -895,6 +898,8 @@ var ForklaunchExpressLikeApplication = class extends ForklaunchExpressLikeRouter
895
898
  super("/", schemaValidator, internal);
896
899
  this.schemaValidator = schemaValidator;
897
900
  this.internal = internal;
901
+ this.internal.use(createContext(this.schemaValidator));
902
+ this.internal.use(cors);
898
903
  }
899
904
  };
900
905
 
@@ -1962,7 +1967,7 @@ function transformBasePath(basePath) {
1962
1967
  }
1963
1968
  return `/${basePath}`;
1964
1969
  }
1965
- function swaggerDocument(port, tags, paths) {
1970
+ function generateOpenApiDocument(port, tags, paths) {
1966
1971
  return {
1967
1972
  openapi: "3.1.0",
1968
1973
  info: {
@@ -2034,7 +2039,10 @@ function generateSwaggerDocument(schemaValidator, port, routers) {
2034
2039
  for (const key in route.contractDetails.params) {
2035
2040
  pathItemObject.parameters?.push({
2036
2041
  name: key,
2037
- in: "path"
2042
+ in: "path",
2043
+ schema: schemaValidator.openapi(
2044
+ route.contractDetails.params[key]
2045
+ )
2038
2046
  });
2039
2047
  }
2040
2048
  }
@@ -2049,7 +2057,10 @@ function generateSwaggerDocument(schemaValidator, port, routers) {
2049
2057
  for (const key in requestHeaders) {
2050
2058
  pathItemObject.parameters?.push({
2051
2059
  name: key,
2052
- in: "header"
2060
+ in: "header",
2061
+ schema: schemaValidator.openapi(
2062
+ requestHeaders[key]
2063
+ )
2053
2064
  });
2054
2065
  }
2055
2066
  }
@@ -2057,7 +2068,8 @@ function generateSwaggerDocument(schemaValidator, port, routers) {
2057
2068
  for (const key in query) {
2058
2069
  pathItemObject.parameters?.push({
2059
2070
  name: key,
2060
- in: "query"
2071
+ in: "query",
2072
+ schema: schemaValidator.openapi(query[key])
2061
2073
  });
2062
2074
  }
2063
2075
  }
@@ -2085,13 +2097,164 @@ function generateSwaggerDocument(schemaValidator, port, routers) {
2085
2097
  }
2086
2098
  });
2087
2099
  });
2088
- return swaggerDocument(port, tags, paths);
2100
+ return generateOpenApiDocument(port, tags, paths);
2089
2101
  }
2090
2102
 
2103
+ // src/http/tracing/emitLoggerError.ts
2104
+ var import_api_logs = require("@opentelemetry/api-logs");
2105
+ var import_semantic_conventions = require("@opentelemetry/semantic-conventions");
2106
+
2107
+ // src/http/tracing/pinoLogger.ts
2108
+ var import_pino = __toESM(require("pino"));
2109
+ function pinoLogger(level) {
2110
+ return (0, import_pino.default)({
2111
+ level: level || "info",
2112
+ formatters: {
2113
+ level(label) {
2114
+ return { level: label };
2115
+ }
2116
+ },
2117
+ timestamp: import_pino.default.stdTimeFunctions.isoTime,
2118
+ transport: {
2119
+ target: "pino-pretty",
2120
+ options: { colorize: true }
2121
+ }
2122
+ });
2123
+ }
2124
+
2125
+ // src/http/tracing/emitLoggerError.ts
2126
+ var logger = pinoLogger("error");
2127
+ var emitLoggerError = (req, res, errorString) => {
2128
+ import_api_logs.logs.getLogger(process.env.SERVICE_NAME ?? "unknown").emit({
2129
+ severityText: "ERROR",
2130
+ severityNumber: 17,
2131
+ body: errorString,
2132
+ attributes: {
2133
+ "service.name": process.env.SERVICE_NAME,
2134
+ "api.name": req.contractDetails?.name,
2135
+ [import_semantic_conventions.SEMATTRS_HTTP_TARGET]: req.path,
2136
+ [import_semantic_conventions.SEMATTRS_HTTP_ROUTE]: req.originalPath,
2137
+ [import_semantic_conventions.SEMATTRS_HTTP_METHOD]: req.method,
2138
+ [import_semantic_conventions.SEMATTRS_HTTP_STATUS_CODE]: res.statusCode
2139
+ }
2140
+ });
2141
+ logger.error(errorString);
2142
+ };
2143
+
2144
+ // src/http/tracing/openTelemetryCollector.ts
2145
+ var import_opentelemetry_instrumentation_hyper_express = require("@forklaunch/opentelemetry-instrumentation-hyper-express");
2146
+ var import_api = require("@opentelemetry/api");
2147
+ var import_api_logs2 = require("@opentelemetry/api-logs");
2148
+ var import_exporter_logs_otlp_http = require("@opentelemetry/exporter-logs-otlp-http");
2149
+ var import_exporter_metrics_otlp_http = require("@opentelemetry/exporter-metrics-otlp-http");
2150
+ var import_exporter_trace_otlp_http = require("@opentelemetry/exporter-trace-otlp-http");
2151
+ var import_instrumentation_express = require("@opentelemetry/instrumentation-express");
2152
+ var import_instrumentation_http = require("@opentelemetry/instrumentation-http");
2153
+ var import_resources = require("@opentelemetry/resources");
2154
+ var import_sdk_logs = require("@opentelemetry/sdk-logs");
2155
+ var import_sdk_metrics = require("@opentelemetry/sdk-metrics");
2156
+ var import_sdk_node = require("@opentelemetry/sdk-node");
2157
+ var import_semantic_conventions2 = require("@opentelemetry/semantic-conventions");
2158
+ var OpenTelemetryCollector = class {
2159
+ // scoped creation and create this in middleware when api execute. Also add correlation id
2160
+ constructor(serviceName, level, metricDefinitions) {
2161
+ this.serviceName = serviceName;
2162
+ this.logger = pinoLogger(level ?? "info");
2163
+ this.metrics = {};
2164
+ for (const [metricId, metricType] of Object.entries(
2165
+ metricDefinitions ?? {}
2166
+ )) {
2167
+ switch (metricType) {
2168
+ case "counter":
2169
+ this.metrics[metricId] = import_api.metrics.getMeter(this.serviceName).createCounter(metricId);
2170
+ break;
2171
+ case "gauge":
2172
+ this.metrics[metricId] = import_api.metrics.getMeter(this.serviceName).createGauge(metricId);
2173
+ break;
2174
+ case "histogram":
2175
+ this.metrics[metricId] = import_api.metrics.getMeter(this.serviceName).createHistogram(metricId);
2176
+ break;
2177
+ case "upDownCounter":
2178
+ this.metrics[metricId] = import_api.metrics.getMeter(this.serviceName).createUpDownCounter(metricId);
2179
+ break;
2180
+ case "observableCounter":
2181
+ this.metrics[metricId] = import_api.metrics.getMeter(this.serviceName).createObservableCounter(metricId);
2182
+ break;
2183
+ case "observableGauge":
2184
+ this.metrics[metricId] = import_api.metrics.getMeter(this.serviceName).createObservableGauge(metricId);
2185
+ break;
2186
+ case "observableUpDownCounter":
2187
+ this.metrics[metricId] = import_api.metrics.getMeter(this.serviceName).createObservableUpDownCounter(metricId);
2188
+ break;
2189
+ }
2190
+ }
2191
+ this.log("info", "OpenTelemetry (Traces + Logs + Metrics) started");
2192
+ }
2193
+ logger;
2194
+ metrics;
2195
+ log(level, msg, meta = {}) {
2196
+ const activeSpan = import_api.trace.getActiveSpan();
2197
+ if (activeSpan) {
2198
+ const activeSpanContext = activeSpan.spanContext();
2199
+ meta.trace_id = activeSpanContext.traceId;
2200
+ meta.span_id = activeSpanContext.spanId;
2201
+ meta.trace_flags = activeSpanContext.traceFlags;
2202
+ }
2203
+ if (!meta.api_name) {
2204
+ meta.api_name = activeSpan?.attributes["api.name"] ?? "undefined";
2205
+ }
2206
+ this.logger[level](msg);
2207
+ import_api_logs2.logs.getLogger(this.serviceName).emit({
2208
+ severityText: level,
2209
+ body: msg,
2210
+ attributes: meta
2211
+ });
2212
+ }
2213
+ getMetric(metricId) {
2214
+ return this.metrics[metricId];
2215
+ }
2216
+ };
2217
+ new import_sdk_node.NodeSDK({
2218
+ resource: new import_resources.Resource({
2219
+ [import_semantic_conventions2.ATTR_SERVICE_NAME]: process.env.SERVICE_NAME
2220
+ }),
2221
+ traceExporter: new import_exporter_trace_otlp_http.OTLPTraceExporter({
2222
+ url: `${process.env.OTEL_EXPORTER_OTLP_ENDPOINT ?? "http://localhost:4318"}/v1/traces`
2223
+ }),
2224
+ metricReader: new import_sdk_metrics.PeriodicExportingMetricReader({
2225
+ exporter: new import_exporter_metrics_otlp_http.OTLPMetricExporter({
2226
+ url: `${process.env.OTEL_EXPORTER_OTLP_ENDPOINT ?? "http://localhost:4318"}/v1/metrics`
2227
+ }),
2228
+ exportIntervalMillis: 5e3
2229
+ }),
2230
+ logRecordProcessors: [
2231
+ new import_sdk_logs.BatchLogRecordProcessor(
2232
+ new import_exporter_logs_otlp_http.OTLPLogExporter({
2233
+ url: `${process.env.OTEL_EXPORTER_OTLP_ENDPOINT ?? "http://localhost:4318"}/v1/logs`
2234
+ })
2235
+ )
2236
+ ],
2237
+ instrumentations: [
2238
+ new import_instrumentation_http.HttpInstrumentation(),
2239
+ new import_instrumentation_express.ExpressInstrumentation({
2240
+ requestHook: (span, info) => {
2241
+ span.setAttribute(
2242
+ "service.name",
2243
+ process.env.SERVICE_NAME ?? "unknown"
2244
+ );
2245
+ span.setAttribute("api.name", info.request.contractDetails?.name);
2246
+ }
2247
+ }),
2248
+ new import_opentelemetry_instrumentation_hyper_express.HyperExpressInstrumentation()
2249
+ ]
2250
+ }).start();
2251
+ var httpRequestsTotalCounter = import_api.metrics.getMeter(process.env.SERVICE_NAME || "unknown").createCounter("http_requests_total", {
2252
+ description: "Number of HTTP requests"
2253
+ });
2254
+
2091
2255
  // src/http/middleware/response/parse.middleware.ts
2092
2256
  var import_validator2 = require("@forklaunch/validator");
2093
2257
  function parse2(req, res, next) {
2094
- console.debug("[MIDDLEWARE] parseResponse started");
2095
2258
  const { headers, responses } = res.responseSchemas;
2096
2259
  const parsedResponse = req.schemaValidator.parse(
2097
2260
  responses?.[res.statusCode],
@@ -2141,6 +2304,7 @@ function enrichExpressLikeSend(instance, req, res, originalSend, data, shouldEnr
2141
2304
  if (shouldEnrich) {
2142
2305
  if (res.statusCode === 404) {
2143
2306
  res.status(404);
2307
+ emitLoggerError(req, res, "Not Found");
2144
2308
  originalSend.call(instance, "Not Found");
2145
2309
  }
2146
2310
  parse2(req, res, (err) => {
@@ -2151,6 +2315,7 @@ function enrichExpressLikeSend(instance, req, res, originalSend, data, shouldEnr
2151
2315
  ------------------
2152
2316
  ${res.locals.errorMessage}`;
2153
2317
  }
2318
+ emitLoggerError(req, res, errorString);
2154
2319
  res.status(500);
2155
2320
  originalSend.call(instance, errorString);
2156
2321
  parseErrorSent = true;
@@ -2161,17 +2326,32 @@ ${res.locals.errorMessage}`;
2161
2326
  originalSend.call(instance, data);
2162
2327
  }
2163
2328
  }
2329
+
2330
+ // src/http/utils/recordMetric.ts
2331
+ var import_semantic_conventions3 = require("@opentelemetry/semantic-conventions");
2332
+ function recordMetric(req, res) {
2333
+ httpRequestsTotalCounter.add(1, {
2334
+ "service.name": process.env.SERVICE_NAME ?? "unknown",
2335
+ "api.name": req.contractDetails?.name,
2336
+ [import_semantic_conventions3.SEMATTRS_HTTP_METHOD]: req.method,
2337
+ [import_semantic_conventions3.SEMATTRS_HTTP_ROUTE]: req.originalPath,
2338
+ [import_semantic_conventions3.SEMATTRS_HTTP_STATUS_CODE]: res.statusCode || 0
2339
+ });
2340
+ }
2164
2341
  // Annotate the CommonJS export names for ESM import in node:
2165
2342
  0 && (module.exports = {
2166
2343
  ForklaunchExpressLikeApplication,
2167
2344
  ForklaunchExpressLikeRouter,
2168
2345
  HTTPStatuses,
2346
+ OpenTelemetryCollector,
2169
2347
  delete_,
2348
+ emitLoggerError,
2170
2349
  enrichExpressLikeSend,
2171
2350
  generateSwaggerDocument,
2172
2351
  get,
2173
2352
  getCodeForStatus,
2174
2353
  head,
2354
+ httpRequestsTotalCounter,
2175
2355
  isClientError,
2176
2356
  isForklaunchRouter,
2177
2357
  isInformational,
@@ -2184,6 +2364,7 @@ ${res.locals.errorMessage}`;
2184
2364
  patch,
2185
2365
  post,
2186
2366
  put,
2367
+ recordMetric,
2187
2368
  trace,
2188
2369
  typedHandler
2189
2370
  });