@openapi-typescript-infra/service 5.10.0 → 5.11.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openapi-typescript-infra/service",
3
- "version": "5.10.0",
3
+ "version": "5.11.1",
4
4
  "description": "An opinionated framework for building configuration driven services - web, api, or ob. Uses OpenAPI, pino logging, express, confit, Typescript and vitest.",
5
5
  "exports": {
6
6
  ".": {
@@ -70,66 +70,67 @@
70
70
  "dependencies": {
71
71
  "@godaddy/terminus": "^4.12.1",
72
72
  "@opentelemetry/api": "^1.9.0",
73
- "@opentelemetry/auto-instrumentations-node": "^0.56.1",
74
- "@opentelemetry/exporter-prometheus": "^0.57.2",
75
- "@opentelemetry/instrumentation-dns": "^0.43.1",
76
- "@opentelemetry/instrumentation-express": "^0.47.1",
77
- "@opentelemetry/instrumentation-generic-pool": "^0.43.1",
78
- "@opentelemetry/instrumentation-graphql": "^0.47.1",
79
- "@opentelemetry/instrumentation-http": "^0.57.2",
80
- "@opentelemetry/instrumentation-ioredis": "^0.47.1",
81
- "@opentelemetry/instrumentation-net": "^0.43.1",
82
- "@opentelemetry/instrumentation-pg": "^0.51.1",
83
- "@opentelemetry/instrumentation-pino": "^0.46.1",
84
- "@opentelemetry/instrumentation-undici": "^0.10.1",
85
- "@opentelemetry/resource-detector-container": "^0.6.1",
86
- "@opentelemetry/resource-detector-gcp": "^0.33.1",
87
- "@opentelemetry/sdk-node": "^0.57.2",
88
- "@opentelemetry/semantic-conventions": "^1.30.0",
73
+ "@opentelemetry/auto-instrumentations-node": "^0.59.0",
74
+ "@opentelemetry/exporter-prometheus": "^0.201.1",
75
+ "@opentelemetry/instrumentation-dns": "^0.45.0",
76
+ "@opentelemetry/instrumentation-express": "^0.50.0",
77
+ "@opentelemetry/instrumentation-generic-pool": "^0.45.0",
78
+ "@opentelemetry/instrumentation-graphql": "^0.49.0",
79
+ "@opentelemetry/instrumentation-http": "^0.201.1",
80
+ "@opentelemetry/instrumentation-ioredis": "^0.49.0",
81
+ "@opentelemetry/instrumentation-net": "^0.45.0",
82
+ "@opentelemetry/instrumentation-pg": "^0.53.0",
83
+ "@opentelemetry/instrumentation-pino": "^0.48.0",
84
+ "@opentelemetry/instrumentation-undici": "^0.12.0",
85
+ "@opentelemetry/resource-detector-container": "^0.7.1",
86
+ "@opentelemetry/resource-detector-gcp": "^0.35.0",
87
+ "@opentelemetry/sdk-node": "^0.201.1",
88
+ "@opentelemetry/semantic-conventions": "^1.33.0",
89
89
  "@sesamecare-oss/confit": "^2.2.1",
90
90
  "@sesamecare-oss/opentelemetry-node-metrics": "^1.1.0",
91
91
  "ajv": "^8.17.1",
92
92
  "clean-stack": "^5.2.0",
93
93
  "cookie-parser": "^1.4.7",
94
- "dotenv": "^16.4.7",
94
+ "dotenv": "^16.5.0",
95
95
  "express": "^5.1.0",
96
- "express-openapi-validator": "^5.4.7",
97
- "glob": "^11.0.1",
98
- "import-in-the-middle": "^1.13.1",
96
+ "express-openapi-validator": "^5.5.2",
97
+ "glob": "^11.0.2",
98
+ "import-in-the-middle": "^1.13.2",
99
99
  "minimist": "^1.2.8",
100
100
  "moderndash": "^4.0.0",
101
- "pino": "^9.6.0",
101
+ "opentelemetry-resource-detector-sync-api": "^0.30.0",
102
+ "pino": "^9.7.0",
102
103
  "read-package-up": "^11.0.0",
103
104
  "request-ip": "^3.3.0"
104
105
  },
105
106
  "devDependencies": {
106
- "@commitlint/cli": "^19.8.0",
107
- "@commitlint/config-conventional": "^19.8.0",
108
- "@openapi-typescript-infra/coconfig": "^4.6.0",
107
+ "@commitlint/cli": "^19.8.1",
108
+ "@commitlint/config-conventional": "^19.8.1",
109
+ "@openapi-typescript-infra/coconfig": "^4.7.1",
109
110
  "@semantic-release/commit-analyzer": "^13.0.1",
110
- "@semantic-release/exec": "^7.0.3",
111
- "@semantic-release/github": "^11.0.1",
111
+ "@semantic-release/exec": "^7.1.0",
112
+ "@semantic-release/github": "^11.0.2",
112
113
  "@semantic-release/release-notes-generator": "^14.0.3",
113
114
  "@types/cookie-parser": "^1.4.8",
114
- "@types/express": "^5.0.1",
115
+ "@types/express": "^5.0.2",
115
116
  "@types/minimist": "^1.2.5",
116
- "@types/node": "^22.13.17",
117
+ "@types/node": "^22.15.20",
117
118
  "@types/request-ip": "^0.0.41",
118
119
  "@types/supertest": "^6.0.3",
119
120
  "@typescript-eslint/eslint-plugin": "^7.18.0",
120
121
  "@typescript-eslint/parser": "^7.18.0",
121
- "coconfig": "^1.6.1",
122
+ "coconfig": "^1.6.2",
122
123
  "eslint": "^8.57.1",
123
124
  "eslint-config-prettier": "^9.1.0",
124
- "eslint-import-resolver-typescript": "^4.3.1",
125
+ "eslint-import-resolver-typescript": "^4.3.5",
125
126
  "eslint-plugin-import": "^2.31.0",
126
127
  "pino-pretty": "^13.0.0",
127
128
  "pinst": "^3.0.0",
128
- "supertest": "^7.1.0",
129
+ "supertest": "^7.1.1",
129
130
  "tsconfig-paths": "^4.2.0",
130
- "tsx": "^4.19.3",
131
- "typescript": "^5.8.2",
132
- "vitest": "^3.1.1"
131
+ "tsx": "^4.19.4",
132
+ "typescript": "^5.8.3",
133
+ "vitest": "^3.1.4"
133
134
  },
134
135
  "resolutions": {
135
136
  "qs": "^6.11.0"
@@ -232,7 +232,7 @@ export async function startApp<
232
232
  }
233
233
 
234
234
  if (routing?.freezeQuery) {
235
- app.use((req, res, next) => {
235
+ app.use(function freezeQuery(req, res, next) {
236
236
  // Express 5 re-parses the query string every time. This causes problems with
237
237
  // various libraries, namely the express OpenAPI parser. So we "freeze it" in place
238
238
  // here, which runs right before the routing validation logic does. Note that this
@@ -259,7 +259,8 @@ export async function startApp<
259
259
  );
260
260
  }
261
261
  if (routing?.openapi) {
262
- app.use(await openApi(app, rootDirectory, codepath, codePattern, options.openApiOptions));
262
+ const openApiMiddleware = await openApi(app, rootDirectory, codepath, codePattern, options.openApiOptions);
263
+ app.use(openApiMiddleware);
263
264
  }
264
265
 
265
266
  // Putting this here allows more flexible middleware insertion
@@ -1,14 +1,11 @@
1
1
  import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-proto';
2
2
  import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';
3
3
  import {
4
- Detector,
5
- DetectorSync,
6
- envDetectorSync,
7
- hostDetectorSync,
8
- IResource,
9
- osDetectorSync,
10
- processDetectorSync,
11
- ResourceDetectionConfig,
4
+ detectResources,
5
+ envDetector,
6
+ hostDetector,
7
+ osDetector,
8
+ processDetector,
12
9
  } from '@opentelemetry/resources';
13
10
  import { containerDetector } from '@opentelemetry/resource-detector-container';
14
11
  import { gcpDetector } from '@opentelemetry/resource-detector-gcp';
@@ -67,17 +64,6 @@ function getLogExporter() {
67
64
  let prometheusExporter: PrometheusExporter | undefined;
68
65
  let telemetrySdk: opentelemetry.NodeSDK | undefined;
69
66
 
70
- function awaitAttributes(detector: DetectorSync): Detector {
71
- return {
72
- async detect(config?: ResourceDetectionConfig): Promise<IResource> {
73
- const resource = detector.detect(config)
74
- await resource.waitForAsyncAttributes?.()
75
-
76
- return resource
77
- },
78
- }
79
- }
80
-
81
67
  /**
82
68
  * OpenTelemetry is not friendly to the idea of stopping
83
69
  * and starting itself, it seems. So we can only keep a global
@@ -90,33 +76,40 @@ export async function startGlobalTelemetry(serviceName: string) {
90
76
  if (!prometheusExporter) {
91
77
  const { metrics, logs, NodeSDK } = opentelemetry;
92
78
 
79
+ const resource = await detectResources({
80
+ detectors: [
81
+ envDetector,
82
+ hostDetector,
83
+ osDetector,
84
+ processDetector,
85
+ containerDetector,
86
+ gcpDetector,
87
+ ],
88
+ });
89
+
93
90
  prometheusExporter = new PrometheusExporter({ preventServerStart: true });
94
91
  const instrumentations = getAutoInstrumentations();
95
92
  const logExporter = getLogExporter();
96
93
  telemetrySdk = new NodeSDK({
97
94
  serviceName,
98
95
  autoDetectResources: false,
99
- resourceDetectors: [
100
- awaitAttributes(envDetectorSync),
101
- awaitAttributes(hostDetectorSync),
102
- awaitAttributes(osDetectorSync),
103
- awaitAttributes(processDetectorSync),
104
- awaitAttributes(containerDetector),
105
- awaitAttributes(gcpDetector),
106
- ],
96
+ resource,
107
97
  traceExporter: getSpanExporter(),
108
98
  metricReader: prometheusExporter,
109
99
  instrumentations,
110
100
  logRecordProcessors: logExporter ? [new logs.BatchLogRecordProcessor(logExporter)] : [],
111
101
  views: [
112
- new metrics.View({
102
+ {
113
103
  instrumentName: 'http_request_duration_seconds',
114
104
  instrumentType: metrics.InstrumentType.HISTOGRAM,
115
- aggregation: new metrics.ExplicitBucketHistogramAggregation(
116
- [0.003, 0.03, 0.1, 0.3, 1.5, 10],
117
- true,
118
- ),
119
- }),
105
+ aggregation: {
106
+ type: metrics.AggregationType.EXPLICIT_BUCKET_HISTOGRAM,
107
+ options: {
108
+ boundaries: [0.003, 0.03, 0.1, 0.3, 1.5, 10],
109
+ recordMinMax: true,
110
+ },
111
+ },
112
+ },
120
113
  ],
121
114
  });
122
115
  telemetrySdk.start();
@@ -160,3 +153,5 @@ export async function startWithTelemetry<
160
153
  });
161
154
  return { app, codepath: options.codepath, server };
162
155
  }
156
+
157
+ export { setTelemetryHooks } from './instrumentations.js';
@@ -1,11 +1,12 @@
1
1
  import type { Instrumentation } from '@opentelemetry/instrumentation';
2
2
  import { DnsInstrumentation } from '@opentelemetry/instrumentation-dns';
3
- import { ExpressInstrumentation } from '@opentelemetry/instrumentation-express';
3
+ import { ExpressInstrumentation, SpanNameHook } from '@opentelemetry/instrumentation-express';
4
4
  import { UndiciInstrumentation } from '@opentelemetry/instrumentation-undici';
5
5
  import { GenericPoolInstrumentation } from '@opentelemetry/instrumentation-generic-pool';
6
- import { HttpInstrumentation } from '@opentelemetry/instrumentation-http';
6
+ import { HttpInstrumentation, IgnoreIncomingRequestFunction } from '@opentelemetry/instrumentation-http';
7
7
  import { IORedisInstrumentation } from '@opentelemetry/instrumentation-ioredis';
8
8
  import { NetInstrumentation } from '@opentelemetry/instrumentation-net';
9
+ import { GraphQLInstrumentation } from '@opentelemetry/instrumentation-graphql';
9
10
  import { PgInstrumentation } from '@opentelemetry/instrumentation-pg';
10
11
  import { PinoInstrumentation } from '@opentelemetry/instrumentation-pino';
11
12
 
@@ -13,6 +14,7 @@ const InstrumentationMap = {
13
14
  '@opentelemetry/instrumentation-http': HttpInstrumentation,
14
15
  '@opentelemetry/instrumentation-dns': DnsInstrumentation,
15
16
  '@opentelemetry/instrumentation-express': ExpressInstrumentation,
17
+ '@opentelemetry/instrumentation-graphql': GraphQLInstrumentation,
16
18
  '@opentelemetry/instrumentation-undici': UndiciInstrumentation,
17
19
  '@opentelemetry/instrumentation-generic-pool': GenericPoolInstrumentation,
18
20
  '@opentelemetry/instrumentation-ioredis': IORedisInstrumentation,
@@ -27,8 +29,45 @@ export type InstrumentationConfigMap = {
27
29
  [Name in keyof typeof InstrumentationMap]?: ConfigArg<(typeof InstrumentationMap)[Name]>;
28
30
  };
29
31
 
32
+ let ignoreIncomingRequestHook: IgnoreIncomingRequestFunction | undefined = (req) => {
33
+ return req.url === '/health' || req.url === '/metrics';
34
+ };
35
+
36
+ let spanNameHook: SpanNameHook | undefined;
37
+
38
+ export function setTelemetryHooks(hooks: {
39
+ ignoreIncomingRequestHook?: IgnoreIncomingRequestFunction;
40
+ spanNameHook?: SpanNameHook;
41
+ }) {
42
+ if ('ignoreIncomingRequestHook' in hooks) {
43
+ ignoreIncomingRequestHook = hooks.ignoreIncomingRequestHook;
44
+ }
45
+ if ('spanNameHook' in hooks) {
46
+ spanNameHook = hooks.spanNameHook;
47
+ }
48
+ }
49
+
50
+ const defaultConfigs: InstrumentationConfigMap = {
51
+ '@opentelemetry/instrumentation-http': {
52
+ ignoreIncomingRequestHook(req) {
53
+ if (ignoreIncomingRequestHook) {
54
+ return ignoreIncomingRequestHook(req);
55
+ }
56
+ return false;
57
+ },
58
+ },
59
+ '@opentelemetry/instrumentation-express': {
60
+ spanNameHook(info, defaultName) {
61
+ if (spanNameHook) {
62
+ return spanNameHook(info, defaultName);
63
+ }
64
+ return defaultName;
65
+ },
66
+ },
67
+ };
68
+
30
69
  export function getAutoInstrumentations(
31
- inputConfigs: InstrumentationConfigMap = {},
70
+ inputConfigs: InstrumentationConfigMap = defaultConfigs,
32
71
  ): Instrumentation[] {
33
72
  const keys = Object.keys(InstrumentationMap) as Array<keyof typeof InstrumentationMap>;
34
73
  return keys
package/vitest.config.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  * Instead, edit the coconfig.js or coconfig.ts file in your project root.
4
4
  *
5
5
  * See https://github.com/gas-buddy/coconfig for more information.
6
- * @version coconfig@1.6.1
6
+ * @version coconfig@1.6.2
7
7
  */
8
8
  import cjs from '@openapi-typescript-infra/coconfig';
9
9
  import * as esmToCjs from '@openapi-typescript-infra/coconfig';