@openapi-typescript-infra/service 4.19.1 → 4.20.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/config/test.json CHANGED
@@ -4,6 +4,7 @@
4
4
  "level": "warn"
5
5
  },
6
6
  "server": {
7
- "port": 0
7
+ "port": 0,
8
+ "internalPort": 0
8
9
  }
9
10
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openapi-typescript-infra/service",
3
- "version": "4.19.1",
3
+ "version": "4.20.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
  "main": "build/index.js",
6
6
  "scripts": {
@@ -85,7 +85,7 @@
85
85
  "glob": "^8.1.0",
86
86
  "lodash": "^4.17.21",
87
87
  "minimist": "^1.2.8",
88
- "pino": "^8.21.0",
88
+ "pino": "^9.0.0",
89
89
  "read-pkg-up": "^7.0.1",
90
90
  "request-ip": "^3.3.0"
91
91
  },
@@ -1,5 +1,4 @@
1
1
  import fs from 'fs';
2
- import net from 'net';
3
2
  import path from 'path';
4
3
 
5
4
  import {
@@ -10,8 +9,7 @@ import {
10
9
  confit,
11
10
  } from '@sesamecare-oss/confit';
12
11
 
13
- import { findPort } from '../development/port-finder';
14
- import { isTest } from '../env';
12
+ import { getAvailablePort } from '../development/port-finder';
15
13
 
16
14
  import type { ConfigurationSchema } from './schema';
17
15
 
@@ -57,28 +55,6 @@ async function addDefaultConfiguration<Config extends ConfigurationSchema = Conf
57
55
  }
58
56
  }
59
57
 
60
- async function getEphemeralPort(): Promise<number> {
61
- return new Promise((resolve, reject) => {
62
- const server = net.createServer();
63
-
64
- server.listen(0, () => {
65
- const address = server.address();
66
- if (typeof address === 'string' || !address) {
67
- reject(new Error('Invalid address'));
68
- return;
69
- }
70
- const port = address.port; // Retrieve the ephemeral port
71
- server.close((err) => {
72
- if (err) {
73
- reject(err);
74
- } else {
75
- resolve(port);
76
- }
77
- });
78
- });
79
- });
80
- }
81
-
82
58
  export interface ServiceConfigurationSpec {
83
59
  // The LAST configuration is the most "specific" - if a configuration value
84
60
  // exists in all directories, the last one wins
@@ -117,8 +93,7 @@ export async function loadConfiguration<Config extends ConfigurationSchema>({
117
93
  // configured to auto-select
118
94
  const serverConfig = loaded.get().server;
119
95
  if (serverConfig.port === 0) {
120
- const portPromise: Promise<number> = (isTest() || process.env.TEST_RUNNER) ? getEphemeralPort() : findPort(8001);
121
- const port = await portPromise;
96
+ const port = await getAvailablePort(8001);
122
97
  const store = loaded.get();
123
98
  store.server = store.server || {};
124
99
  store.server.port = port;
@@ -1,5 +1,7 @@
1
1
  import net from 'net';
2
2
 
3
+ import { isTest } from '../env';
4
+
3
5
  // Inspired by https://github.com/kessler/find-port/blob/master/lib/findPort.js
4
6
  async function isAvailable(port: number) {
5
7
  return new Promise((accept, reject) => {
@@ -29,7 +31,7 @@ async function isAvailable(port: number) {
29
31
  });
30
32
  }
31
33
 
32
- export async function findPort(start: number) {
34
+ async function findPort(start: number) {
33
35
  for (let p = start; p < start + 1000; p += 1) {
34
36
  // eslint-disable-next-line no-await-in-loop
35
37
  if (await isAvailable(p)) {
@@ -38,3 +40,29 @@ export async function findPort(start: number) {
38
40
  }
39
41
  return 0;
40
42
  }
43
+
44
+ async function getEphemeralPort(): Promise<number> {
45
+ return new Promise((resolve, reject) => {
46
+ const server = net.createServer();
47
+
48
+ server.listen(0, () => {
49
+ const address = server.address();
50
+ if (typeof address === 'string' || !address) {
51
+ reject(new Error('Invalid address'));
52
+ return;
53
+ }
54
+ const port = address.port; // Retrieve the ephemeral port
55
+ server.close((err) => {
56
+ if (err) {
57
+ reject(err);
58
+ } else {
59
+ resolve(port);
60
+ }
61
+ });
62
+ });
63
+ });
64
+ }
65
+
66
+ export async function getAvailablePort(basePort: number): Promise<number> {
67
+ return (isTest() || process.env.TEST_RUNNER) ? getEphemeralPort() : findPort(basePort);
68
+ }
@@ -2,7 +2,7 @@ import express from 'express';
2
2
  import type { Application } from 'express-serve-static-core';
3
3
 
4
4
  import { AnyServiceLocals, InternalLocals, ServiceExpress, ServiceLocals } from '../types';
5
- import { findPort } from '../development/port-finder';
5
+ import { getAvailablePort } from '../development/port-finder';
6
6
  import { ConfigurationSchema } from '../config/schema';
7
7
 
8
8
  export async function startInternalApp<
@@ -11,7 +11,7 @@ export async function startInternalApp<
11
11
  const app = express() as unknown as Application<InternalLocals<SLocals>>;
12
12
  app.locals.mainApp = mainApp;
13
13
 
14
- const finalPort = port === 0 ? await findPort(3001) : port;
14
+ const finalPort = port === 0 ? await getAvailablePort(3001) : port;
15
15
 
16
16
  app.get('/health', async (req, res) => {
17
17
  if (mainApp.locals.service?.healthy) {
@@ -29,6 +29,7 @@ interface WithIdentifiedSession {
29
29
 
30
30
  interface ErrorWithStatus extends Error {
31
31
  status?: number;
32
+ expected_error?: boolean;
32
33
  }
33
34
 
34
35
  function getBasicInfo(req: Request): [string, Record<string, string | number>] {
@@ -84,11 +85,15 @@ function finishLog<SLocals extends AnyServiceLocals = ServiceLocals<Configuratio
84
85
  endLog.u = res.locals.user.id;
85
86
  }
86
87
 
88
+ let unexpectedError = false;
87
89
  if (error) {
88
90
  endLog.e = error.message;
89
91
  if (!(error instanceof ServiceError) || error.log_stack) {
90
92
  endLog.st = error.stack;
91
93
  }
94
+ if (!(error as ErrorWithStatus).expected_error) {
95
+ unexpectedError = true;
96
+ }
92
97
  }
93
98
 
94
99
  if (prefs.logRequests) {
@@ -110,7 +115,11 @@ function finishLog<SLocals extends AnyServiceLocals = ServiceLocals<Configuratio
110
115
  }
111
116
 
112
117
  const msg = service.getLogFields?.(req as RequestWithApp<SLocals>, endLog) || url;
113
- logger.info(endLog, msg);
118
+ if (unexpectedError) {
119
+ logger.error(endLog, msg);
120
+ } else {
121
+ logger.info(endLog, msg);
122
+ }
114
123
  }
115
124
 
116
125
  export function loggerMiddleware<
@@ -122,7 +131,7 @@ export function loggerMiddleware<
122
131
  ): RequestHandler {
123
132
  const nonProd = getNodeEnv() !== 'production';
124
133
  const { logger, service } = app.locals;
125
- return function gblogger(req, res, next) {
134
+ return function serviceLogger(req, res, next) {
126
135
  const logResponse =
127
136
  config?.logResponseBody || (nonProd && req.headers['x-log']?.includes('res'));
128
137
  const logRequest = config?.logRequestBody || (nonProd && req.headers['x-log']?.includes('req'));
@@ -176,7 +185,7 @@ export function loggerMiddleware<
176
185
  export function errorHandlerMiddleware<
177
186
  SLocals extends AnyServiceLocals = ServiceLocals<ConfigurationSchema>,
178
187
  >(app: ServiceExpress<SLocals>, histogram: Histogram, unnest?: boolean, returnError?: boolean) {
179
- const gbErrorHandler: ErrorRequestHandler = (error, req, res, next) => {
188
+ const svcErrorHandler: ErrorRequestHandler = (error, req, res, next) => {
180
189
  let loggable: Partial<ServiceError> = error;
181
190
  const body = error.response?.body || error.body;
182
191
  if (unnest && body?.domain && body?.code && body?.message) {
@@ -204,11 +213,11 @@ export function errorHandlerMiddleware<
204
213
  next(error);
205
214
  }
206
215
  };
207
- return gbErrorHandler;
216
+ return svcErrorHandler;
208
217
  }
209
218
 
210
219
  export function notFoundMiddleware() {
211
- const gbNotFoundHandler: ServiceHandler = (req, res, next) => {
220
+ const serviceNotFoundHandler: ServiceHandler = (req, res, next) => {
212
221
  const error = new ServiceError(req.app, `Cannot ${req.method} ${req.path}`, {
213
222
  status: 404,
214
223
  code: 'NotFound',
@@ -216,5 +225,5 @@ export function notFoundMiddleware() {
216
225
  });
217
226
  next(error);
218
227
  };
219
- return gbNotFoundHandler as RequestHandler;
228
+ return serviceNotFoundHandler as RequestHandler;
220
229
  }