@forklaunch/core 0.3.6 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/http/index.js CHANGED
@@ -30,32 +30,84 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/http/index.ts
31
31
  var http_exports = {};
32
32
  __export(http_exports, {
33
+ ATTR_API_NAME: () => ATTR_API_NAME,
34
+ ATTR_CORRELATION_ID: () => ATTR_CORRELATION_ID,
35
+ ATTR_HTTP_REQUEST_METHOD: () => import_semantic_conventions.ATTR_HTTP_REQUEST_METHOD,
36
+ ATTR_HTTP_RESPONSE_STATUS_CODE: () => import_semantic_conventions.ATTR_HTTP_RESPONSE_STATUS_CODE,
37
+ ATTR_HTTP_ROUTE: () => import_semantic_conventions.ATTR_HTTP_ROUTE,
38
+ ATTR_SERVICE_NAME: () => import_semantic_conventions.ATTR_SERVICE_NAME,
33
39
  ForklaunchExpressLikeApplication: () => ForklaunchExpressLikeApplication,
34
40
  ForklaunchExpressLikeRouter: () => ForklaunchExpressLikeRouter,
35
41
  HTTPStatuses: () => HTTPStatuses,
42
+ OpenTelemetryCollector: () => OpenTelemetryCollector,
36
43
  delete_: () => delete_,
37
44
  enrichExpressLikeSend: () => enrichExpressLikeSend,
38
45
  generateSwaggerDocument: () => generateSwaggerDocument,
39
46
  get: () => get,
40
47
  getCodeForStatus: () => getCodeForStatus,
41
48
  head: () => head,
49
+ httpRequestsTotalCounter: () => httpRequestsTotalCounter,
42
50
  isClientError: () => isClientError,
51
+ isForklaunchRequest: () => isForklaunchRequest,
43
52
  isForklaunchRouter: () => isForklaunchRouter,
44
53
  isInformational: () => isInformational,
45
54
  isRedirection: () => isRedirection,
46
55
  isServerError: () => isServerError,
47
56
  isSuccessful: () => isSuccessful,
48
57
  isValidStatusCode: () => isValidStatusCode,
58
+ logger: () => logger,
59
+ metricsDefinitions: () => metricsDefinitions,
49
60
  middleware: () => middleware,
50
61
  options: () => options,
51
62
  patch: () => patch,
52
63
  post: () => post,
53
64
  put: () => put,
54
- trace: () => trace,
65
+ recordMetric: () => recordMetric,
66
+ trace: () => trace2,
55
67
  typedHandler: () => typedHandler
56
68
  });
57
69
  module.exports = __toCommonJS(http_exports);
58
70
 
71
+ // src/http/middleware/request/cors.middleware.ts
72
+ var import_cors = __toESM(require("cors"));
73
+ function cors(req, res, next) {
74
+ if (req.method === "OPTIONS") {
75
+ res.cors = true;
76
+ }
77
+ (0, import_cors.default)()(req, res, next ?? (() => {
78
+ }));
79
+ }
80
+
81
+ // src/http/middleware/request/createContext.middleware.ts
82
+ var import_api = require("@opentelemetry/api");
83
+ var import_uuid = require("uuid");
84
+
85
+ // src/http/telemetry/constants.ts
86
+ var import_semantic_conventions = require("@opentelemetry/semantic-conventions");
87
+ var ATTR_API_NAME = "api.name";
88
+ var ATTR_CORRELATION_ID = "correlation.id";
89
+
90
+ // src/http/middleware/request/createContext.middleware.ts
91
+ function createContext(schemaValidator) {
92
+ return function setContext(req, res, next) {
93
+ req.schemaValidator = schemaValidator;
94
+ let correlationId = (0, import_uuid.v4)();
95
+ if (req.headers["x-correlation-id"]) {
96
+ correlationId = req.headers["x-correlation-id"];
97
+ }
98
+ res.setHeader("x-correlation-id", correlationId);
99
+ req.context = {
100
+ correlationId
101
+ };
102
+ const span = import_api.trace.getActiveSpan();
103
+ if (span != null) {
104
+ req.context.span = span;
105
+ req.context.span?.setAttribute(ATTR_CORRELATION_ID, correlationId);
106
+ }
107
+ next?.();
108
+ };
109
+ }
110
+
59
111
  // src/http/guards/isForklaunchRouter.ts
60
112
  function isForklaunchRouter(maybeForklaunchRouter) {
61
113
  return maybeForklaunchRouter != null && typeof maybeForklaunchRouter === "object" && "basePath" in maybeForklaunchRouter && "routes" in maybeForklaunchRouter && Array.isArray(maybeForklaunchRouter.routes);
@@ -215,58 +267,29 @@ async function checkAuthorizationToken(authorizationMethod, authorizationToken,
215
267
  return [401, "Invalid Authorization method."];
216
268
  }
217
269
  async function parseRequestAuth(req, res, next) {
218
- console.debug("[MIDDLEWARE] parseRequest started");
219
270
  const auth = req.contractDetails.auth;
220
271
  if (auth) {
221
- const errorAndMessage = await checkAuthorizationToken(
272
+ const [error, message] = await checkAuthorizationToken(
222
273
  auth,
223
274
  req.headers[(auth.method === "other" ? auth.headerName : void 0) ?? "Authorization"],
224
275
  req
225
- );
226
- if (Array.isArray(errorAndMessage)) {
227
- res.status(errorAndMessage[0]).send(errorAndMessage[1]);
228
- next?.(new Error(errorAndMessage[1]));
276
+ ) ?? [];
277
+ if (error != null) {
278
+ res.status(error).send(message);
279
+ next?.(new Error(message));
229
280
  }
230
281
  }
231
282
  next?.();
232
283
  }
233
284
 
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
285
  // src/http/middleware/request/enrichDetails.middleware.ts
264
- function enrichDetails(contractDetails, requestSchema, responseSchemas) {
286
+ function enrichDetails(path, contractDetails, requestSchema, responseSchemas) {
265
287
  return (req, res, next) => {
266
- console.debug("[MIDDLEWARE] enrichRequestDetails started");
288
+ req.originalPath = path;
267
289
  req.contractDetails = contractDetails;
268
290
  req.requestSchema = requestSchema;
269
291
  res.responseSchemas = responseSchemas;
292
+ req.context.span?.setAttribute(ATTR_API_NAME, req.contractDetails?.name);
270
293
  next?.();
271
294
  };
272
295
  }
@@ -281,14 +304,16 @@ function isResponseShape(maybeResponseShape) {
281
304
 
282
305
  // src/http/middleware/request/parse.middleware.ts
283
306
  function parse(req, _res, next) {
284
- console.debug("[MIDDLEWARE] parseRequest started");
285
307
  const request = {
286
308
  params: req.params,
287
309
  query: req.query,
288
310
  headers: req.headers,
289
311
  body: req.body
290
312
  };
291
- const parsedRequest = req.schemaValidator.parse(req.requestSchema, request);
313
+ const parsedRequest = req.schemaValidator.parse(
314
+ req.requestSchema,
315
+ request
316
+ );
292
317
  if (parsedRequest.ok && isResponseShape(parsedRequest.value)) {
293
318
  req.body = parsedRequest.value.body;
294
319
  req.params = parsedRequest.value.params;
@@ -319,8 +344,6 @@ var ForklaunchExpressLikeRouter = class {
319
344
  this.schemaValidator = schemaValidator;
320
345
  this.internal = internal;
321
346
  this.basePath = basePath;
322
- this.internal.use(createContext(this.schemaValidator));
323
- this.internal.use(cors);
324
347
  }
325
348
  requestHandler;
326
349
  routers = [];
@@ -332,9 +355,14 @@ var ForklaunchExpressLikeRouter = class {
332
355
  * @param {PathParamHttpContractDetails<SV> | HttpContractDetails<SV>} contractDetails - The contract details.
333
356
  * @returns {MiddlewareHandler<SV>[]} - The resolved middlewares.
334
357
  */
335
- #resolveMiddlewares(contractDetails, requestSchema, responseSchemas) {
358
+ #resolveMiddlewares(path, contractDetails, requestSchema, responseSchemas) {
336
359
  return [
337
- enrichDetails(contractDetails, requestSchema, responseSchemas),
360
+ enrichDetails(
361
+ `${this.basePath}${path}`,
362
+ contractDetails,
363
+ requestSchema,
364
+ responseSchemas
365
+ ),
338
366
  parse,
339
367
  parseRequestAuth
340
368
  ];
@@ -359,10 +387,10 @@ var ForklaunchExpressLikeRouter = class {
359
387
  try {
360
388
  await requestHandler(req, res, next);
361
389
  } catch (error) {
362
- next?.(error);
363
- console.error(error);
364
- if (!res.headersSent) {
365
- res.status(500).send("Internal Server Error");
390
+ if (next && typeof next === "function") {
391
+ next(error);
392
+ } else {
393
+ throw error;
366
394
  }
367
395
  }
368
396
  };
@@ -395,7 +423,7 @@ var ForklaunchExpressLikeRouter = class {
395
423
  ...contractDetails.params ? { params: contractDetails.params } : {},
396
424
  ...contractDetails.requestHeaders ? { headers: contractDetails.requestHeaders } : {},
397
425
  ...contractDetails.query ? { query: contractDetails.query } : {},
398
- ...isHttpContractDetails(contractDetails) && contractDetails.body ? { body: contractDetails.body } : {}
426
+ ...isHttpContractDetails(contractDetails) && contractDetails.body != null ? { body: contractDetails.body } : {}
399
427
  })
400
428
  );
401
429
  const responseEntries = {
@@ -404,7 +432,9 @@ var ForklaunchExpressLikeRouter = class {
404
432
  403: schemaValidator.string,
405
433
  404: schemaValidator.string,
406
434
  500: schemaValidator.string,
407
- ...isPathParamHttpContractDetails(contractDetails) || isHttpContractDetails(contractDetails) ? { ...contractDetails.responses } : {}
435
+ ...isPathParamHttpContractDetails(contractDetails) || isHttpContractDetails(contractDetails) ? {
436
+ ...contractDetails.responses
437
+ } : {}
408
438
  };
409
439
  const responseSchemas = {
410
440
  responses: {},
@@ -534,9 +564,12 @@ var ForklaunchExpressLikeRouter = class {
534
564
  const controllerHandler = this.#extractControllerHandler(handlers);
535
565
  registrationMethod.bind(this.internal)(
536
566
  path,
537
- ...this.#resolveMiddlewares(contractDetails, requestSchema, responseSchemas).concat(
538
- handlers
539
- ),
567
+ ...this.#resolveMiddlewares(
568
+ path,
569
+ contractDetails,
570
+ requestSchema,
571
+ responseSchemas
572
+ ).concat(handlers),
540
573
  this.#parseAndRunControllerHandler(controllerHandler)
541
574
  );
542
575
  return this.#localParamRequest(
@@ -592,9 +625,7 @@ var ForklaunchExpressLikeRouter = class {
592
625
  contractDetailsOrMiddlewareOrTypedHandler,
593
626
  middleware2
594
627
  );
595
- if (isForklaunchExpressLikeRouter(
596
- contractDetailsOrMiddlewareOrTypedHandler
597
- )) {
628
+ if (isForklaunchExpressLikeRouter(contractDetailsOrMiddlewareOrTypedHandler)) {
598
629
  middleware2.push(contractDetailsOrMiddlewareOrTypedHandler.internal);
599
630
  }
600
631
  middleware2.push(
@@ -895,11 +926,23 @@ var ForklaunchExpressLikeApplication = class extends ForklaunchExpressLikeRouter
895
926
  super("/", schemaValidator, internal);
896
927
  this.schemaValidator = schemaValidator;
897
928
  this.internal = internal;
929
+ this.internal.use(createContext(this.schemaValidator));
930
+ this.internal.use(cors);
898
931
  }
899
932
  };
900
933
 
934
+ // src/http/guards/isForklaunchRequest.ts
935
+ function isForklaunchRequest(request) {
936
+ return request != null && typeof request === "object" && "contractDetails" in request;
937
+ }
938
+
939
+ // src/http/guards/isPath.ts
940
+ function isPath(path) {
941
+ return path.startsWith("/");
942
+ }
943
+
901
944
  // src/http/handlers/typedHandler.ts
902
- function typedHandler(_schemaValidator, _pathOrContractDetails, contractMethodOrContractDetails, contractDetailsOrHandler, ...handlerArray) {
945
+ function typedHandler(_schemaValidator, pathOrContractMethod, contractMethodOrContractDetails, contractDetailsOrHandler, ...handlerArray) {
903
946
  let contractDetails;
904
947
  let handlers;
905
948
  if (typeof contractMethodOrContractDetails === "string") {
@@ -919,6 +962,7 @@ function typedHandler(_schemaValidator, _pathOrContractDetails, contractMethodOr
919
962
  }
920
963
  return {
921
964
  _typedHandler: true,
965
+ _path: isPath(pathOrContractMethod) ? pathOrContractMethod : void 0,
922
966
  contractDetails,
923
967
  handlers
924
968
  };
@@ -965,11 +1009,11 @@ var put = (_schemaValidator, path, contractDetails, ...handlers) => {
965
1009
  };
966
1010
 
967
1011
  // src/http/handlers/trace.ts
968
- var trace = (_schemaValidator, path, contractDetails, ...handlers) => {
1012
+ var trace2 = (_schemaValidator, path, contractDetails, ...handlers) => {
969
1013
  return typedHandler(_schemaValidator, path, "trace", contractDetails, ...handlers);
970
1014
  };
971
1015
 
972
- // src/http/utils/httpStatusCodes.ts
1016
+ // src/http/httpStatusCodes.ts
973
1017
  var HTTPStatuses = {
974
1018
  /**
975
1019
  * The initial part of a request has been received and has not yet been
@@ -1952,6 +1996,311 @@ var getCodeForStatus = (status) => {
1952
1996
  };
1953
1997
  var httpStatusCodes_default = HTTPStatuses;
1954
1998
 
1999
+ // src/http/middleware/response/parse.middleware.ts
2000
+ var import_validator2 = require("@forklaunch/validator");
2001
+ function parse2(req, res, next) {
2002
+ const { headers, responses } = res.responseSchemas;
2003
+ const parsedResponse = req.schemaValidator.parse(
2004
+ responses?.[res.statusCode],
2005
+ res.bodyData
2006
+ );
2007
+ const parsedHeaders = req.schemaValidator.parse(
2008
+ headers ?? req.schemaValidator.unknown,
2009
+ res.getHeaders()
2010
+ );
2011
+ const parseErrors = [];
2012
+ if (!parsedHeaders.ok) {
2013
+ const headerErrors = (0, import_validator2.prettyPrintParseErrors)(parsedHeaders.errors, "Header");
2014
+ if (headerErrors) {
2015
+ parseErrors.push(headerErrors);
2016
+ }
2017
+ }
2018
+ if (!parsedResponse.ok) {
2019
+ const responseErrors = (0, import_validator2.prettyPrintParseErrors)(
2020
+ parsedResponse.errors,
2021
+ "Response"
2022
+ );
2023
+ if (responseErrors) {
2024
+ parseErrors.push(responseErrors);
2025
+ }
2026
+ }
2027
+ if (parseErrors.length > 0) {
2028
+ switch (req.contractDetails.options?.responseValidation) {
2029
+ default:
2030
+ case "error":
2031
+ next?.(new Error(`Invalid response:
2032
+ ${parseErrors.join("\n\n")}`));
2033
+ break;
2034
+ case "warning":
2035
+ console.warn(`Invalid response:
2036
+ ${parseErrors.join("\n\n")}`);
2037
+ break;
2038
+ case "none":
2039
+ break;
2040
+ }
2041
+ }
2042
+ next?.();
2043
+ }
2044
+
2045
+ // src/http/telemetry/pinoLogger.ts
2046
+ var import_common2 = require("@forklaunch/common");
2047
+ var import_api2 = require("@opentelemetry/api");
2048
+ var import_api_logs = require("@opentelemetry/api-logs");
2049
+ var import_pino = __toESM(require("pino"));
2050
+ function mapSeverity(level) {
2051
+ switch (level) {
2052
+ case "silent":
2053
+ return 0;
2054
+ case "trace":
2055
+ return 1;
2056
+ case "debug":
2057
+ return 5;
2058
+ case "info":
2059
+ return 9;
2060
+ case "warn":
2061
+ return 13;
2062
+ case "error":
2063
+ return 17;
2064
+ case "fatal":
2065
+ return 21;
2066
+ default:
2067
+ (0, import_common2.isNever)(level);
2068
+ return 0;
2069
+ }
2070
+ }
2071
+ var PinoLogger = class _PinoLogger {
2072
+ pinoLogger;
2073
+ meta;
2074
+ constructor(level, meta = {}) {
2075
+ this.pinoLogger = (0, import_pino.default)({
2076
+ level: level || "info",
2077
+ formatters: {
2078
+ level(label) {
2079
+ return { level: label };
2080
+ }
2081
+ },
2082
+ timestamp: import_pino.default.stdTimeFunctions.isoTime,
2083
+ transport: {
2084
+ target: "pino-pretty",
2085
+ options: { colorize: true }
2086
+ }
2087
+ });
2088
+ this.meta = meta;
2089
+ }
2090
+ log(level, msg, meta = {}) {
2091
+ const activeSpan = import_api2.trace.getActiveSpan();
2092
+ if (activeSpan) {
2093
+ const activeSpanContext = activeSpan.spanContext();
2094
+ meta.trace_id = activeSpanContext.traceId;
2095
+ meta.span_id = activeSpanContext.spanId;
2096
+ meta.trace_flags = activeSpanContext.traceFlags;
2097
+ if (!meta.api_name) {
2098
+ meta = { ...meta, ...activeSpan?.attributes };
2099
+ }
2100
+ }
2101
+ this.pinoLogger[level](msg);
2102
+ import_api_logs.logs.getLogger(process.env.OTEL_SERVICE_NAME ?? "unknown").emit({
2103
+ severityText: level,
2104
+ severityNumber: mapSeverity(level),
2105
+ body: msg,
2106
+ attributes: { ...this.meta, ...meta }
2107
+ });
2108
+ }
2109
+ error(msg, meta = {}) {
2110
+ this.log("error", msg, meta);
2111
+ }
2112
+ info(msg, meta = {}) {
2113
+ this.log("info", msg, meta);
2114
+ }
2115
+ debug(msg, meta = {}) {
2116
+ this.log("debug", msg, meta);
2117
+ }
2118
+ warn(msg, meta = {}) {
2119
+ this.log("warn", msg, meta);
2120
+ }
2121
+ trace(msg, meta = {}) {
2122
+ this.log("trace", msg, meta);
2123
+ }
2124
+ child(meta = {}) {
2125
+ return new _PinoLogger(this.pinoLogger.level, { ...this.meta, ...meta });
2126
+ }
2127
+ getBaseLogger() {
2128
+ return this.pinoLogger;
2129
+ }
2130
+ };
2131
+ function logger(level, meta = {}) {
2132
+ return new PinoLogger(level, meta);
2133
+ }
2134
+
2135
+ // src/http/telemetry/recordMetric.ts
2136
+ var import_semantic_conventions3 = require("@opentelemetry/semantic-conventions");
2137
+
2138
+ // src/services/configInjector.ts
2139
+ var import_common3 = require("@forklaunch/common");
2140
+ var import_validator3 = require("@forklaunch/validator");
2141
+
2142
+ // src/services/getEnvVar.ts
2143
+ function getEnvVar(name) {
2144
+ const value = process.env[name];
2145
+ return value;
2146
+ }
2147
+
2148
+ // src/http/telemetry/openTelemetryCollector.ts
2149
+ var import_opentelemetry_instrumentation_hyper_express = require("@forklaunch/opentelemetry-instrumentation-hyper-express");
2150
+ var import_api3 = require("@opentelemetry/api");
2151
+ var import_exporter_logs_otlp_http = require("@opentelemetry/exporter-logs-otlp-http");
2152
+ var import_exporter_metrics_otlp_http = require("@opentelemetry/exporter-metrics-otlp-http");
2153
+ var import_exporter_trace_otlp_http = require("@opentelemetry/exporter-trace-otlp-http");
2154
+ var import_instrumentation_express = require("@opentelemetry/instrumentation-express");
2155
+ var import_instrumentation_http = require("@opentelemetry/instrumentation-http");
2156
+ var import_resources = require("@opentelemetry/resources");
2157
+ var import_sdk_logs = require("@opentelemetry/sdk-logs");
2158
+ var import_sdk_metrics = require("@opentelemetry/sdk-metrics");
2159
+ var import_sdk_node = require("@opentelemetry/sdk-node");
2160
+ var import_semantic_conventions2 = require("@opentelemetry/semantic-conventions");
2161
+ var import_dotenv = __toESM(require("dotenv"));
2162
+ var OpenTelemetryCollector = class {
2163
+ // scoped creation and create this in middleware when api execute. Also add correlation id
2164
+ constructor(serviceName, level, metricDefinitions) {
2165
+ this.serviceName = serviceName;
2166
+ this.logger = logger(level ?? "info");
2167
+ this.metrics = {};
2168
+ for (const [metricId, metricType] of Object.entries(
2169
+ metricDefinitions ?? {}
2170
+ )) {
2171
+ switch (metricType) {
2172
+ case "counter":
2173
+ this.metrics[metricId] = import_api3.metrics.getMeter(this.serviceName).createCounter(metricId);
2174
+ break;
2175
+ case "gauge":
2176
+ this.metrics[metricId] = import_api3.metrics.getMeter(this.serviceName).createGauge(metricId);
2177
+ break;
2178
+ case "histogram":
2179
+ this.metrics[metricId] = import_api3.metrics.getMeter(this.serviceName).createHistogram(metricId);
2180
+ break;
2181
+ case "upDownCounter":
2182
+ this.metrics[metricId] = import_api3.metrics.getMeter(this.serviceName).createUpDownCounter(metricId);
2183
+ break;
2184
+ case "observableCounter":
2185
+ this.metrics[metricId] = import_api3.metrics.getMeter(this.serviceName).createObservableCounter(metricId);
2186
+ break;
2187
+ case "observableGauge":
2188
+ this.metrics[metricId] = import_api3.metrics.getMeter(this.serviceName).createObservableGauge(metricId);
2189
+ break;
2190
+ case "observableUpDownCounter":
2191
+ this.metrics[metricId] = import_api3.metrics.getMeter(this.serviceName).createObservableUpDownCounter(metricId);
2192
+ break;
2193
+ }
2194
+ }
2195
+ this.log("info", "OpenTelemetry (Traces + Logs + Metrics) started");
2196
+ }
2197
+ logger;
2198
+ metrics;
2199
+ log(level, msg, meta = {}) {
2200
+ this.logger.log(level, msg, meta);
2201
+ }
2202
+ info(msg, meta = {}) {
2203
+ this.logger.info(msg, meta);
2204
+ }
2205
+ error(msg, meta = {}) {
2206
+ this.logger.error(msg, meta);
2207
+ }
2208
+ warn(msg, meta = {}) {
2209
+ this.logger.warn(msg, meta);
2210
+ }
2211
+ debug(msg, meta = {}) {
2212
+ this.logger.debug(msg, meta);
2213
+ }
2214
+ trace(msg, meta = {}) {
2215
+ this.logger.trace(msg, meta);
2216
+ }
2217
+ getMetric(metricId) {
2218
+ return this.metrics[metricId];
2219
+ }
2220
+ };
2221
+ import_dotenv.default.config({ path: getEnvVar("ENV_FILE_PATH") });
2222
+ new import_sdk_node.NodeSDK({
2223
+ resource: new import_resources.Resource({
2224
+ [import_semantic_conventions2.ATTR_SERVICE_NAME]: getEnvVar("OTEL_SERVICE_NAME")
2225
+ }),
2226
+ traceExporter: new import_exporter_trace_otlp_http.OTLPTraceExporter({
2227
+ url: `${getEnvVar("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/traces`
2228
+ }),
2229
+ metricReader: new import_sdk_metrics.PeriodicExportingMetricReader({
2230
+ exporter: new import_exporter_metrics_otlp_http.OTLPMetricExporter({
2231
+ url: `${getEnvVar("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/metrics`
2232
+ }),
2233
+ exportIntervalMillis: 5e3
2234
+ }),
2235
+ logRecordProcessors: [
2236
+ new import_sdk_logs.BatchLogRecordProcessor(
2237
+ new import_exporter_logs_otlp_http.OTLPLogExporter({
2238
+ url: `${getEnvVar("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/logs`
2239
+ })
2240
+ )
2241
+ ],
2242
+ instrumentations: [
2243
+ new import_instrumentation_http.HttpInstrumentation({
2244
+ applyCustomAttributesOnSpan: (span, request) => {
2245
+ span.setAttribute(
2246
+ "service.name",
2247
+ getEnvVar("OTEL_SERVICE_NAME") ?? "unknown"
2248
+ );
2249
+ if (isForklaunchRequest(request)) {
2250
+ span.setAttribute("api.name", request.contractDetails?.name);
2251
+ }
2252
+ }
2253
+ }),
2254
+ new import_instrumentation_express.ExpressInstrumentation(),
2255
+ new import_opentelemetry_instrumentation_hyper_express.HyperExpressInstrumentation()
2256
+ ]
2257
+ }).start();
2258
+ var httpRequestsTotalCounter = import_api3.metrics.getMeter(getEnvVar("OTEL_SERVICE_NAME") || "unknown").createCounter("http_requests_total", {
2259
+ description: "Number of HTTP requests"
2260
+ });
2261
+
2262
+ // src/http/telemetry/recordMetric.ts
2263
+ function recordMetric(req, res) {
2264
+ httpRequestsTotalCounter.add(1, {
2265
+ [import_semantic_conventions3.ATTR_SERVICE_NAME]: getEnvVar("OTEL_SERVICE_NAME"),
2266
+ [ATTR_API_NAME]: req.contractDetails?.name,
2267
+ [ATTR_CORRELATION_ID]: req.context.correlationId,
2268
+ [import_semantic_conventions3.ATTR_HTTP_REQUEST_METHOD]: req.method,
2269
+ [import_semantic_conventions3.ATTR_HTTP_ROUTE]: req.originalPath,
2270
+ [import_semantic_conventions3.ATTR_HTTP_RESPONSE_STATUS_CODE]: res.statusCode || 0
2271
+ });
2272
+ }
2273
+
2274
+ // src/http/middleware/response/enrichExpressLikeSend.middleware.ts
2275
+ function enrichExpressLikeSend(instance, req, res, originalSend, data, shouldEnrich) {
2276
+ let parseErrorSent;
2277
+ if (shouldEnrich) {
2278
+ recordMetric(req, res);
2279
+ if (res.statusCode === 404) {
2280
+ res.status(404);
2281
+ logger("error").error("Not Found");
2282
+ originalSend.call(instance, "Not Found");
2283
+ }
2284
+ parse2(req, res, (err) => {
2285
+ if (err) {
2286
+ let errorString = err.message;
2287
+ if (res.locals.errorMessage) {
2288
+ errorString += `
2289
+ ------------------
2290
+ ${res.locals.errorMessage}`;
2291
+ }
2292
+ logger("error").error(errorString);
2293
+ res.status(500);
2294
+ originalSend.call(instance, errorString);
2295
+ parseErrorSent = true;
2296
+ }
2297
+ });
2298
+ }
2299
+ if (!parseErrorSent) {
2300
+ originalSend.call(instance, data);
2301
+ }
2302
+ }
2303
+
1955
2304
  // src/http/openApiV3Generator/openApiV3Generator.ts
1956
2305
  function toUpperCase(str) {
1957
2306
  return str.charAt(0).toUpperCase() + str.slice(1);
@@ -1962,7 +2311,7 @@ function transformBasePath(basePath) {
1962
2311
  }
1963
2312
  return `/${basePath}`;
1964
2313
  }
1965
- function swaggerDocument(port, tags, paths) {
2314
+ function generateOpenApiDocument(port, tags, paths) {
1966
2315
  return {
1967
2316
  openapi: "3.1.0",
1968
2317
  info: {
@@ -2034,7 +2383,10 @@ function generateSwaggerDocument(schemaValidator, port, routers) {
2034
2383
  for (const key in route.contractDetails.params) {
2035
2384
  pathItemObject.parameters?.push({
2036
2385
  name: key,
2037
- in: "path"
2386
+ in: "path",
2387
+ schema: schemaValidator.openapi(
2388
+ route.contractDetails.params[key]
2389
+ )
2038
2390
  });
2039
2391
  }
2040
2392
  }
@@ -2049,7 +2401,10 @@ function generateSwaggerDocument(schemaValidator, port, routers) {
2049
2401
  for (const key in requestHeaders) {
2050
2402
  pathItemObject.parameters?.push({
2051
2403
  name: key,
2052
- in: "header"
2404
+ in: "header",
2405
+ schema: schemaValidator.openapi(
2406
+ requestHeaders[key]
2407
+ )
2053
2408
  });
2054
2409
  }
2055
2410
  }
@@ -2057,7 +2412,8 @@ function generateSwaggerDocument(schemaValidator, port, routers) {
2057
2412
  for (const key in query) {
2058
2413
  pathItemObject.parameters?.push({
2059
2414
  name: key,
2060
- in: "query"
2415
+ in: "query",
2416
+ schema: schemaValidator.openapi(query[key])
2061
2417
  });
2062
2418
  }
2063
2419
  }
@@ -2085,105 +2441,48 @@ function generateSwaggerDocument(schemaValidator, port, routers) {
2085
2441
  }
2086
2442
  });
2087
2443
  });
2088
- return swaggerDocument(port, tags, paths);
2444
+ return generateOpenApiDocument(port, tags, paths);
2089
2445
  }
2090
2446
 
2091
- // src/http/middleware/response/parse.middleware.ts
2092
- var import_validator2 = require("@forklaunch/validator");
2093
- function parse2(req, res, next) {
2094
- console.debug("[MIDDLEWARE] parseResponse started");
2095
- const { headers, responses } = res.responseSchemas;
2096
- const parsedResponse = req.schemaValidator.parse(
2097
- responses?.[res.statusCode],
2098
- res.bodyData
2099
- );
2100
- const parsedHeaders = req.schemaValidator.parse(
2101
- headers ?? req.schemaValidator.unknown,
2102
- res.getHeaders()
2103
- );
2104
- const parseErrors = [];
2105
- if (!parsedHeaders.ok) {
2106
- const headerErrors = (0, import_validator2.prettyPrintParseErrors)(parsedHeaders.errors, "Header");
2107
- if (headerErrors) {
2108
- parseErrors.push(headerErrors);
2109
- }
2110
- }
2111
- if (!parsedResponse.ok) {
2112
- const responseErrors = (0, import_validator2.prettyPrintParseErrors)(
2113
- parsedResponse.errors,
2114
- "Response"
2115
- );
2116
- if (responseErrors) {
2117
- parseErrors.push(responseErrors);
2118
- }
2119
- }
2120
- if (parseErrors.length > 0) {
2121
- switch (req.contractDetails.options?.responseValidation) {
2122
- default:
2123
- case "error":
2124
- next?.(new Error(`Invalid response:
2125
- ${parseErrors.join("\n\n")}`));
2126
- break;
2127
- case "warning":
2128
- console.warn(`Invalid response:
2129
- ${parseErrors.join("\n\n")}`);
2130
- break;
2131
- case "none":
2132
- break;
2133
- }
2134
- }
2135
- next?.();
2136
- }
2137
-
2138
- // src/http/utils/enrichExpressLikeSend.ts
2139
- function enrichExpressLikeSend(instance, req, res, originalSend, data, shouldEnrich) {
2140
- let parseErrorSent;
2141
- if (shouldEnrich) {
2142
- if (res.statusCode === 404) {
2143
- res.status(404);
2144
- originalSend.call(instance, "Not Found");
2145
- }
2146
- parse2(req, res, (err) => {
2147
- if (err) {
2148
- let errorString = err.message;
2149
- if (res.locals.errorMessage) {
2150
- errorString += `
2151
- ------------------
2152
- ${res.locals.errorMessage}`;
2153
- }
2154
- res.status(500);
2155
- originalSend.call(instance, errorString);
2156
- parseErrorSent = true;
2157
- }
2158
- });
2159
- }
2160
- if (!parseErrorSent) {
2161
- originalSend.call(instance, data);
2162
- }
2447
+ // src/http/telemetry/metricsDefinition.ts
2448
+ function metricsDefinitions(metrics2) {
2449
+ return metrics2;
2163
2450
  }
2164
2451
  // Annotate the CommonJS export names for ESM import in node:
2165
2452
  0 && (module.exports = {
2453
+ ATTR_API_NAME,
2454
+ ATTR_CORRELATION_ID,
2455
+ ATTR_HTTP_REQUEST_METHOD,
2456
+ ATTR_HTTP_RESPONSE_STATUS_CODE,
2457
+ ATTR_HTTP_ROUTE,
2458
+ ATTR_SERVICE_NAME,
2166
2459
  ForklaunchExpressLikeApplication,
2167
2460
  ForklaunchExpressLikeRouter,
2168
2461
  HTTPStatuses,
2462
+ OpenTelemetryCollector,
2169
2463
  delete_,
2170
2464
  enrichExpressLikeSend,
2171
2465
  generateSwaggerDocument,
2172
2466
  get,
2173
2467
  getCodeForStatus,
2174
2468
  head,
2469
+ httpRequestsTotalCounter,
2175
2470
  isClientError,
2471
+ isForklaunchRequest,
2176
2472
  isForklaunchRouter,
2177
2473
  isInformational,
2178
2474
  isRedirection,
2179
2475
  isServerError,
2180
2476
  isSuccessful,
2181
2477
  isValidStatusCode,
2478
+ logger,
2479
+ metricsDefinitions,
2182
2480
  middleware,
2183
2481
  options,
2184
2482
  patch,
2185
2483
  post,
2186
2484
  put,
2485
+ recordMetric,
2187
2486
  trace,
2188
2487
  typedHandler
2189
2488
  });