@forklaunch/core 0.8.7 → 0.8.9

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.
@@ -1,4 +1,5 @@
1
1
  import { AnySchemaValidator, UnboxedObjectSchema, IdiomaticSchema, Schema } from '@forklaunch/validator';
2
+ import { CorsOptions } from 'cors';
2
3
  import { Counter, Gauge, Histogram, UpDownCounter, ObservableCounter, ObservableGauge, ObservableUpDownCounter, Span } from '@opentelemetry/api';
3
4
  import { LevelWithSilentOrString, LevelWithSilent, Logger } from 'pino';
4
5
  import { UnionToIntersection, MimeType, Prettify, ExclusiveRecord, RemoveTrailingSlash, MakePropertyOptionalIfChildrenOptional } from '@forklaunch/common';
@@ -491,6 +492,12 @@ interface ForklaunchResponse<BaseResponse, ResBodyMap extends Record<number, unk
491
492
  * @returns {Omit<ResHeaders, keyof ForklaunchResHeaders> & ForklaunchResHeaders} - The headers of the response.
492
493
  */
493
494
  getHeaders: () => Omit<ResHeaders, keyof ForklaunchResHeaders> & ForklaunchResHeaders;
495
+ /**
496
+ * Gets a header for the response.
497
+ * @param {string} key - The header key.
498
+ * @returns {string | string[] | undefined} The header value.
499
+ */
500
+ getHeader: (key: string) => string | string[] | undefined;
494
501
  /**
495
502
  * Sets a header for the response.
496
503
  * @param {string} key - The header key.
@@ -946,12 +953,17 @@ declare abstract class ForklaunchExpressLikeApplication<SV extends AnySchemaVali
946
953
  readonly internal: Server;
947
954
  readonly postEnrichMiddleware: RouterHandler[];
948
955
  readonly openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>;
956
+ readonly appOptions?: {
957
+ cors?: CorsOptions;
958
+ } | undefined;
949
959
  /**
950
960
  * Creates an instance of the Application class.
951
961
  *
952
962
  * @param {SV} schemaValidator - The schema validator.
953
963
  */
954
- constructor(schemaValidator: SV, internal: Server, postEnrichMiddleware: RouterHandler[], openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>);
964
+ constructor(schemaValidator: SV, internal: Server, postEnrichMiddleware: RouterHandler[], openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>, appOptions?: {
965
+ cors?: CorsOptions;
966
+ } | undefined);
955
967
  abstract listen(...args: unknown[]): void;
956
968
  }
957
969
 
@@ -1,4 +1,5 @@
1
1
  import { AnySchemaValidator, UnboxedObjectSchema, IdiomaticSchema, Schema } from '@forklaunch/validator';
2
+ import { CorsOptions } from 'cors';
2
3
  import { Counter, Gauge, Histogram, UpDownCounter, ObservableCounter, ObservableGauge, ObservableUpDownCounter, Span } from '@opentelemetry/api';
3
4
  import { LevelWithSilentOrString, LevelWithSilent, Logger } from 'pino';
4
5
  import { UnionToIntersection, MimeType, Prettify, ExclusiveRecord, RemoveTrailingSlash, MakePropertyOptionalIfChildrenOptional } from '@forklaunch/common';
@@ -491,6 +492,12 @@ interface ForklaunchResponse<BaseResponse, ResBodyMap extends Record<number, unk
491
492
  * @returns {Omit<ResHeaders, keyof ForklaunchResHeaders> & ForklaunchResHeaders} - The headers of the response.
492
493
  */
493
494
  getHeaders: () => Omit<ResHeaders, keyof ForklaunchResHeaders> & ForklaunchResHeaders;
495
+ /**
496
+ * Gets a header for the response.
497
+ * @param {string} key - The header key.
498
+ * @returns {string | string[] | undefined} The header value.
499
+ */
500
+ getHeader: (key: string) => string | string[] | undefined;
494
501
  /**
495
502
  * Sets a header for the response.
496
503
  * @param {string} key - The header key.
@@ -946,12 +953,17 @@ declare abstract class ForklaunchExpressLikeApplication<SV extends AnySchemaVali
946
953
  readonly internal: Server;
947
954
  readonly postEnrichMiddleware: RouterHandler[];
948
955
  readonly openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>;
956
+ readonly appOptions?: {
957
+ cors?: CorsOptions;
958
+ } | undefined;
949
959
  /**
950
960
  * Creates an instance of the Application class.
951
961
  *
952
962
  * @param {SV} schemaValidator - The schema validator.
953
963
  */
954
- constructor(schemaValidator: SV, internal: Server, postEnrichMiddleware: RouterHandler[], openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>);
964
+ constructor(schemaValidator: SV, internal: Server, postEnrichMiddleware: RouterHandler[], openTelemetryCollector: OpenTelemetryCollector<MetricsDefinition>, appOptions?: {
965
+ cors?: CorsOptions;
966
+ } | undefined);
955
967
  abstract listen(...args: unknown[]): void;
956
968
  }
957
969
 
@@ -75,12 +75,19 @@ module.exports = __toCommonJS(http_exports);
75
75
 
76
76
  // src/http/middleware/request/cors.middleware.ts
77
77
  var import_cors = __toESM(require("cors"));
78
- function cors(req, res, next) {
79
- if (req.method === "OPTIONS") {
80
- res.cors = true;
81
- }
82
- (0, import_cors.default)()(req, res, next ?? (() => {
83
- }));
78
+ function cors(corsOptions) {
79
+ return (req, res, next) => {
80
+ if (req.method === "OPTIONS") {
81
+ res.cors = true;
82
+ }
83
+ if (!res.getHeader) {
84
+ res.getHeader = (key) => {
85
+ return res.getHeaders()[key];
86
+ };
87
+ }
88
+ (0, import_cors.default)(corsOptions)(req, res, next ?? (() => {
89
+ }));
90
+ };
84
91
  }
85
92
 
86
93
  // src/http/middleware/request/createContext.middleware.ts
@@ -288,13 +295,11 @@ async function parseRequestAuth(req, res, next) {
288
295
  next?.();
289
296
  }
290
297
 
291
- // src/services/getEnvVar.ts
292
- function getEnvVar(name) {
293
- const value = process.env[name];
294
- return value;
295
- }
298
+ // src/http/middleware/request/enrichDetails.middleware.ts
299
+ var import_common4 = require("@forklaunch/common");
296
300
 
297
301
  // src/http/telemetry/openTelemetryCollector.ts
302
+ var import_common3 = require("@forklaunch/common");
298
303
  var import_opentelemetry_instrumentation_hyper_express = require("@forklaunch/opentelemetry-instrumentation-hyper-express");
299
304
  var import_api3 = require("@opentelemetry/api");
300
305
  var import_exporter_logs_otlp_http = require("@opentelemetry/exporter-logs-otlp-http");
@@ -488,24 +493,24 @@ var OpenTelemetryCollector = class {
488
493
  return this.metrics[metricId];
489
494
  }
490
495
  };
491
- import_dotenv.default.config({ path: getEnvVar("ENV_FILE_PATH") });
496
+ import_dotenv.default.config({ path: (0, import_common3.getEnvVar)("ENV_FILE_PATH") });
492
497
  new import_sdk_node.NodeSDK({
493
498
  resource: (0, import_resources.resourceFromAttributes)({
494
- [import_semantic_conventions2.ATTR_SERVICE_NAME]: getEnvVar("OTEL_SERVICE_NAME")
499
+ [import_semantic_conventions2.ATTR_SERVICE_NAME]: (0, import_common3.getEnvVar)("OTEL_SERVICE_NAME")
495
500
  }),
496
501
  traceExporter: new import_exporter_trace_otlp_http.OTLPTraceExporter({
497
- url: `${getEnvVar("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/traces`
502
+ url: `${(0, import_common3.getEnvVar)("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/traces`
498
503
  }),
499
504
  metricReader: new import_sdk_metrics.PeriodicExportingMetricReader({
500
505
  exporter: new import_exporter_metrics_otlp_http.OTLPMetricExporter({
501
- url: `${getEnvVar("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/metrics`
506
+ url: `${(0, import_common3.getEnvVar)("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/metrics`
502
507
  }),
503
508
  exportIntervalMillis: 5e3
504
509
  }),
505
510
  logRecordProcessors: [
506
511
  new import_sdk_logs.BatchLogRecordProcessor(
507
512
  new import_exporter_logs_otlp_http.OTLPLogExporter({
508
- url: `${getEnvVar("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/logs`
513
+ url: `${(0, import_common3.getEnvVar)("OTEL_EXPORTER_OTLP_ENDPOINT") ?? "http://localhost:4318"}/v1/logs`
509
514
  })
510
515
  )
511
516
  ],
@@ -514,7 +519,7 @@ new import_sdk_node.NodeSDK({
514
519
  applyCustomAttributesOnSpan: (span, request) => {
515
520
  span.setAttribute(
516
521
  "service.name",
517
- getEnvVar("OTEL_SERVICE_NAME") ?? "unknown"
522
+ (0, import_common3.getEnvVar)("OTEL_SERVICE_NAME") ?? "unknown"
518
523
  );
519
524
  if (isForklaunchRequest(request)) {
520
525
  span.setAttribute("api.name", request.contractDetails?.name);
@@ -525,10 +530,10 @@ new import_sdk_node.NodeSDK({
525
530
  new import_opentelemetry_instrumentation_hyper_express.HyperExpressInstrumentation()
526
531
  ]
527
532
  }).start();
528
- var httpRequestsTotalCounter = import_api3.metrics.getMeter(getEnvVar("OTEL_SERVICE_NAME") || "unknown").createCounter("http_requests_total", {
533
+ var httpRequestsTotalCounter = import_api3.metrics.getMeter((0, import_common3.getEnvVar)("OTEL_SERVICE_NAME") || "unknown").createCounter("http_requests_total", {
529
534
  description: "Number of HTTP requests"
530
535
  });
531
- var httpServerDurationHistogram = import_api3.metrics.getMeter(getEnvVar("OTEL_SERVICE_NAME") || "unknown").createHistogram("http_server_duration", {
536
+ var httpServerDurationHistogram = import_api3.metrics.getMeter((0, import_common3.getEnvVar)("OTEL_SERVICE_NAME") || "unknown").createHistogram("http_server_duration", {
532
537
  description: "Duration of HTTP server requests",
533
538
  unit: "s"
534
539
  });
@@ -547,7 +552,7 @@ function enrichDetails(path, contractDetails, requestSchema, responseSchemas, op
547
552
  const [seconds, nanoseconds] = process.hrtime(startTime);
548
553
  const durationMs = seconds + nanoseconds / 1e9;
549
554
  httpServerDurationHistogram.record(durationMs, {
550
- [import_semantic_conventions.ATTR_SERVICE_NAME]: getEnvVar("OTEL_SERVICE_NAME") || "unknown",
555
+ [import_semantic_conventions.ATTR_SERVICE_NAME]: (0, import_common4.getEnvVar)("OTEL_SERVICE_NAME") || "unknown",
551
556
  [ATTR_API_NAME]: req.contractDetails?.name || "unknown",
552
557
  [import_semantic_conventions.ATTR_HTTP_REQUEST_METHOD]: req.method,
553
558
  [import_semantic_conventions.ATTR_HTTP_ROUTE]: req.originalPath || "unknown",
@@ -1365,7 +1370,7 @@ var ForklaunchExpressLikeApplication = class extends ForklaunchExpressLikeRouter
1365
1370
  *
1366
1371
  * @param {SV} schemaValidator - The schema validator.
1367
1372
  */
1368
- constructor(schemaValidator, internal, postEnrichMiddleware, openTelemetryCollector) {
1373
+ constructor(schemaValidator, internal, postEnrichMiddleware, openTelemetryCollector, appOptions) {
1369
1374
  super(
1370
1375
  "/",
1371
1376
  schemaValidator,
@@ -1377,8 +1382,26 @@ var ForklaunchExpressLikeApplication = class extends ForklaunchExpressLikeRouter
1377
1382
  this.internal = internal;
1378
1383
  this.postEnrichMiddleware = postEnrichMiddleware;
1379
1384
  this.openTelemetryCollector = openTelemetryCollector;
1385
+ this.appOptions = appOptions;
1386
+ process.on("uncaughtException", (err) => {
1387
+ this.openTelemetryCollector.log("error", "Uncaught exception", { err });
1388
+ process.exit(1);
1389
+ });
1390
+ process.on("unhandledRejection", (reason) => {
1391
+ this.openTelemetryCollector.log("error", "Unhandled rejection", {
1392
+ reason
1393
+ });
1394
+ process.exit(1);
1395
+ });
1396
+ process.on("exit", () => {
1397
+ this.openTelemetryCollector.log("info", "Shutting down application");
1398
+ });
1399
+ process.on("SIGINT", () => {
1400
+ this.openTelemetryCollector.log("info", "Shutting down application");
1401
+ process.exit(0);
1402
+ });
1380
1403
  this.internal.use(createContext(this.schemaValidator));
1381
- this.internal.use(cors);
1404
+ this.internal.use(cors(this.appOptions?.cors ?? {}));
1382
1405
  }
1383
1406
  };
1384
1407
 
@@ -2504,23 +2527,18 @@ ${parseErrors.join("\n\n")}`
2504
2527
  }
2505
2528
 
2506
2529
  // src/http/middleware/response/enrichExpressLikeSend.middleware.ts
2507
- var import_common4 = require("@forklaunch/common");
2530
+ var import_common6 = require("@forklaunch/common");
2508
2531
  var import_stream = require("stream");
2509
2532
 
2510
2533
  // src/http/telemetry/recordMetric.ts
2534
+ var import_common5 = require("@forklaunch/common");
2511
2535
  var import_semantic_conventions3 = require("@opentelemetry/semantic-conventions");
2512
-
2513
- // src/services/configInjector.ts
2514
- var import_common3 = require("@forklaunch/common");
2515
- var import_validator3 = require("@forklaunch/validator");
2516
-
2517
- // src/http/telemetry/recordMetric.ts
2518
2536
  function recordMetric(req, res) {
2519
2537
  if (res.metricRecorded) {
2520
2538
  return;
2521
2539
  }
2522
2540
  httpRequestsTotalCounter.add(1, {
2523
- [import_semantic_conventions3.ATTR_SERVICE_NAME]: getEnvVar("OTEL_SERVICE_NAME"),
2541
+ [import_semantic_conventions3.ATTR_SERVICE_NAME]: (0, import_common5.getEnvVar)("OTEL_SERVICE_NAME"),
2524
2542
  [ATTR_API_NAME]: req.contractDetails?.name,
2525
2543
  [ATTR_CORRELATION_ID]: req.context.correlationId,
2526
2544
  [import_semantic_conventions3.ATTR_HTTP_REQUEST_METHOD]: req.method,
@@ -2558,8 +2576,8 @@ function enrichExpressLikeSend(instance, req, res, originalOperation, originalSe
2558
2576
  `attachment; filename="${data.name}"`
2559
2577
  );
2560
2578
  }
2561
- if ((0, import_common4.isNodeJsWriteableStream)(res)) {
2562
- import_stream.Readable.from((0, import_common4.readableStreamToAsyncIterable)(data.stream())).pipe(
2579
+ if ((0, import_common6.isNodeJsWriteableStream)(res)) {
2580
+ import_stream.Readable.from((0, import_common6.readableStreamToAsyncIterable)(data.stream())).pipe(
2563
2581
  res
2564
2582
  );
2565
2583
  } else {
@@ -2568,7 +2586,7 @@ function enrichExpressLikeSend(instance, req, res, originalOperation, originalSe
2568
2586
  originalSend.call(instance, "Not a NodeJS WritableStream");
2569
2587
  errorSent = true;
2570
2588
  }
2571
- } else if ((0, import_common4.isAsyncGenerator)(data)) {
2589
+ } else if ((0, import_common6.isAsyncGenerator)(data)) {
2572
2590
  let firstPass = true;
2573
2591
  const transformer = new import_stream.Transform({
2574
2592
  objectMode: true,
@@ -2596,7 +2614,7 @@ ${res.locals.errorMessage}`;
2596
2614
  if (!errorSent) {
2597
2615
  let data2 = "";
2598
2616
  for (const [key, value] of Object.entries(chunk)) {
2599
- data2 += `${key}: ${typeof value === "string" ? value : (0, import_common4.safeStringify)(value)}
2617
+ data2 += `${key}: ${typeof value === "string" ? value : (0, import_common6.safeStringify)(value)}
2600
2618
  `;
2601
2619
  }
2602
2620
  data2 += "\n";
@@ -2604,7 +2622,7 @@ ${res.locals.errorMessage}`;
2604
2622
  }
2605
2623
  }
2606
2624
  });
2607
- if ((0, import_common4.isNodeJsWriteableStream)(res)) {
2625
+ if ((0, import_common6.isNodeJsWriteableStream)(res)) {
2608
2626
  import_stream.Readable.from(data).pipe(transformer).pipe(res);
2609
2627
  } else {
2610
2628
  res.type("text/plain");
@@ -2615,7 +2633,7 @@ ${res.locals.errorMessage}`;
2615
2633
  } else {
2616
2634
  const parserType = responseBodies?.[Number(res.statusCode)]?.parserType;
2617
2635
  res.bodyData = data;
2618
- if ((0, import_common4.isRecord)(data)) {
2636
+ if ((0, import_common6.isRecord)(data)) {
2619
2637
  switch (parserType) {
2620
2638
  case "json":
2621
2639
  res.bodyData = "json" in data ? data.json : data;
@@ -2636,7 +2654,7 @@ ${res.locals.errorMessage}`;
2636
2654
  res.bodyData = data;
2637
2655
  break;
2638
2656
  default:
2639
- (0, import_common4.isNever)(parserType);
2657
+ (0, import_common6.isNever)(parserType);
2640
2658
  res.bodyData = data;
2641
2659
  break;
2642
2660
  }