@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/controllers/index.d.mts +6 -1
- package/lib/controllers/index.d.ts +6 -1
- package/lib/http/index.d.mts +206 -163
- package/lib/http/index.d.ts +206 -163
- package/lib/http/index.js +435 -136
- package/lib/http/index.js.map +1 -1
- package/lib/http/index.mjs +441 -138
- package/lib/http/index.mjs.map +1 -1
- package/package.json +32 -13
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
|
-
|
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
|
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 (
|
227
|
-
res.status(
|
228
|
-
next?.(new Error(
|
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
|
-
|
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(
|
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(
|
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
|
363
|
-
|
364
|
-
|
365
|
-
|
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) ? {
|
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(
|
538
|
-
|
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,
|
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
|
1012
|
+
var trace2 = (_schemaValidator, path, contractDetails, ...handlers) => {
|
969
1013
|
return typedHandler(_schemaValidator, path, "trace", contractDetails, ...handlers);
|
970
1014
|
};
|
971
1015
|
|
972
|
-
// src/http/
|
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
|
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
|
2444
|
+
return generateOpenApiDocument(port, tags, paths);
|
2089
2445
|
}
|
2090
2446
|
|
2091
|
-
// src/http/
|
2092
|
-
|
2093
|
-
|
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
|
});
|