@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.
- package/lib/http/index.d.mts +44 -2
- package/lib/http/index.d.ts +44 -2
- package/lib/http/index.js +234 -53
- package/lib/http/index.js.map +1 -1
- package/lib/http/index.mjs +244 -53
- package/lib/http/index.mjs.map +1 -1
- package/package.json +31 -13
package/lib/http/index.d.mts
CHANGED
@@ -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
|
-
|
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.d.ts
CHANGED
@@ -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
|
-
|
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
|
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 (
|
227
|
-
res.status(
|
228
|
-
next?.(new Error(
|
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
|
-
|
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(
|
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
|
363
|
-
|
364
|
-
|
365
|
-
|
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
|
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
|
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
|
});
|