@openapi-typescript-infra/service 2.0.2 → 2.2.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/.trunk/trunk.yaml +6 -6
- package/@types/config.d.ts +12 -4
- package/CHANGELOG.md +14 -0
- package/build/bootstrap.d.ts +2 -1
- package/build/config/schema.d.ts +3 -1
- package/build/config/shortstops.d.ts +3 -3
- package/build/config/shortstops.js +29 -2
- package/build/config/shortstops.js.map +1 -1
- package/build/express-app/app.d.ts +3 -1
- package/build/express-app/app.js +14 -3
- package/build/express-app/app.js.map +1 -1
- package/build/express-app/internal-server.js +6 -1
- package/build/express-app/internal-server.js.map +1 -1
- package/build/telemetry/index.d.ts +2 -1
- package/build/tsconfig.build.tsbuildinfo +1 -1
- package/config/development.json +6 -2
- package/config/test.json +2 -1
- package/package.json +13 -11
- package/src/config/schema.ts +16 -10
- package/src/config/shortstops.ts +36 -7
- package/src/express-app/app.ts +22 -3
- package/src/express-app/internal-server.ts +7 -1
- package/tsconfig.json +1 -1
package/config/development.json
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
{
|
|
2
|
+
"$schema": "tsschema://../src/config/schema#ConfigurationSchema",
|
|
2
3
|
"logging": {
|
|
3
4
|
"level": "debug"
|
|
4
5
|
},
|
|
5
|
-
"
|
|
6
|
+
"server": {
|
|
7
|
+
"port": 0,
|
|
8
|
+
"internalPort": 0
|
|
9
|
+
},
|
|
6
10
|
"connections": {
|
|
7
11
|
"default": {
|
|
8
12
|
"proxy": "http://localhost:9990"
|
|
9
13
|
}
|
|
10
14
|
}
|
|
11
|
-
}
|
|
15
|
+
}
|
package/config/test.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openapi-typescript-infra/service",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.2.0",
|
|
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": {
|
|
@@ -67,7 +67,7 @@
|
|
|
67
67
|
"@opentelemetry/exporter-trace-otlp-proto": "^0.41.2",
|
|
68
68
|
"@opentelemetry/instrumentation": "^0.41.2",
|
|
69
69
|
"@opentelemetry/instrumentation-aws-sdk": "^0.36.0",
|
|
70
|
-
"@opentelemetry/instrumentation-dns": "^0.32.
|
|
70
|
+
"@opentelemetry/instrumentation-dns": "^0.32.2",
|
|
71
71
|
"@opentelemetry/instrumentation-express": "^0.33.1",
|
|
72
72
|
"@opentelemetry/instrumentation-generic-pool": "^0.32.2",
|
|
73
73
|
"@opentelemetry/instrumentation-graphql": "^0.35.1",
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"dotenv": "^16.3.1",
|
|
84
84
|
"eventsource": "^1.1.2",
|
|
85
85
|
"express": "next",
|
|
86
|
-
"express-openapi-validator": "^5.0.
|
|
86
|
+
"express-openapi-validator": "^5.0.6",
|
|
87
87
|
"glob": "^8.1.0",
|
|
88
88
|
"lodash": "^4.17.21",
|
|
89
89
|
"minimist": "^1.2.8",
|
|
@@ -97,31 +97,33 @@
|
|
|
97
97
|
"devDependencies": {
|
|
98
98
|
"@commitlint/cli": "^17.7.1",
|
|
99
99
|
"@commitlint/config-conventional": "^17.7.0",
|
|
100
|
-
"@openapi-typescript-infra/coconfig": "^4.
|
|
100
|
+
"@openapi-typescript-infra/coconfig": "^4.1.0",
|
|
101
101
|
"@semantic-release/changelog": "^6.0.3",
|
|
102
|
-
"@semantic-release/commit-analyzer": "^10.0.
|
|
102
|
+
"@semantic-release/commit-analyzer": "^10.0.4",
|
|
103
103
|
"@semantic-release/exec": "^6.0.3",
|
|
104
104
|
"@semantic-release/git": "^10.0.1",
|
|
105
|
-
"@semantic-release/release-notes-generator": "^11.0.
|
|
105
|
+
"@semantic-release/release-notes-generator": "^11.0.7",
|
|
106
106
|
"@types/cookie-parser": "^1.4.3",
|
|
107
107
|
"@types/eventsource": "1.1.11",
|
|
108
108
|
"@types/express": "^4.17.17",
|
|
109
109
|
"@types/glob": "^8.1.0",
|
|
110
110
|
"@types/lodash": "^4.14.197",
|
|
111
111
|
"@types/minimist": "^1.2.2",
|
|
112
|
-
"@types/node": "^18.17.
|
|
112
|
+
"@types/node": "^18.17.12",
|
|
113
113
|
"@types/supertest": "^2.0.12",
|
|
114
|
+
"@typescript-eslint/eslint-plugin": "^6.5.0",
|
|
115
|
+
"@typescript-eslint/parser": "^6.5.0",
|
|
114
116
|
"coconfig": "^0.13.3",
|
|
115
|
-
"eslint": "^8.
|
|
116
|
-
"eslint-config-gasbuddy": "^7.2.0",
|
|
117
|
+
"eslint": "^8.48.0",
|
|
117
118
|
"eslint-config-prettier": "^9.0.0",
|
|
119
|
+
"eslint-plugin-import": "^2.28.1",
|
|
118
120
|
"pino-pretty": "^10.2.0",
|
|
119
121
|
"pinst": "^3.0.0",
|
|
120
122
|
"supertest": "^6.3.3",
|
|
121
123
|
"ts-node": "^10.9.1",
|
|
122
124
|
"tsconfig-paths": "^4.2.0",
|
|
123
|
-
"typescript": "^5.
|
|
124
|
-
"vitest": "^0.34.
|
|
125
|
+
"typescript": "^5.2.2",
|
|
126
|
+
"vitest": "^0.34.3"
|
|
125
127
|
},
|
|
126
128
|
"packageManager": "yarn@3.2.3"
|
|
127
129
|
}
|
package/src/config/schema.ts
CHANGED
|
@@ -19,7 +19,7 @@ export interface ConfigurationSchema extends Record<string, unknown> {
|
|
|
19
19
|
logHttpRequests?: boolean;
|
|
20
20
|
logRequestBody?: boolean;
|
|
21
21
|
logResponseBody?: boolean;
|
|
22
|
-
}
|
|
22
|
+
};
|
|
23
23
|
routing?: {
|
|
24
24
|
openapi?: boolean;
|
|
25
25
|
// Relative to the *root directory* of the app
|
|
@@ -34,14 +34,14 @@ export interface ConfigurationSchema extends Record<string, unknown> {
|
|
|
34
34
|
bodyParsers?: {
|
|
35
35
|
json?: boolean;
|
|
36
36
|
form?: boolean;
|
|
37
|
-
}
|
|
37
|
+
};
|
|
38
38
|
// Set static.enabled to true to enable static assets to be served
|
|
39
39
|
static?: ConfigurationItemEnabled & {
|
|
40
40
|
// The path relative to the root directory of the app
|
|
41
41
|
path?: string;
|
|
42
42
|
// The path on which to mount the static assets (defaults to /)
|
|
43
43
|
mountPath?: string;
|
|
44
|
-
}
|
|
44
|
+
};
|
|
45
45
|
finalHandlers: {
|
|
46
46
|
// Whether to create and return errors for unhandled routes
|
|
47
47
|
notFound?: boolean;
|
|
@@ -55,12 +55,18 @@ export interface ConfigurationSchema extends Record<string, unknown> {
|
|
|
55
55
|
// information.
|
|
56
56
|
unnest: boolean;
|
|
57
57
|
};
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
server
|
|
61
|
-
internalPort?: number
|
|
62
|
-
port?: number
|
|
63
|
-
metrics: ConfigurationItemEnabled
|
|
64
|
-
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
server: {
|
|
61
|
+
internalPort?: number;
|
|
62
|
+
port?: number;
|
|
63
|
+
metrics: ConfigurationItemEnabled;
|
|
64
|
+
// To enable HTTPS on the main service, set the key and cert to the
|
|
65
|
+
// actual key material (not the path). Use shortstop file: handler.
|
|
66
|
+
// Note that generally it's better to offload tls termination,
|
|
67
|
+
// but this is useful for dev.
|
|
68
|
+
key?: string;
|
|
69
|
+
certificate?: string;
|
|
70
|
+
};
|
|
65
71
|
connections: Record<string, ServiceConfiguration>;
|
|
66
72
|
}
|
package/src/config/shortstops.ts
CHANGED
|
@@ -29,6 +29,38 @@ function betterRequire(basepath: string) {
|
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Just like path, but resolve ~/ to the home directory
|
|
34
|
+
*/
|
|
35
|
+
function betterPath(basepath: string) {
|
|
36
|
+
const basePath = shortstop.path(basepath);
|
|
37
|
+
return function pathWithHomeDir(v: string) {
|
|
38
|
+
if (v.startsWith('~/')) {
|
|
39
|
+
return basePath(path.join(os.homedir(), v.slice(2)));
|
|
40
|
+
}
|
|
41
|
+
return basePath(v);
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Just like file, but resolve ~/ to the home directory
|
|
47
|
+
*/
|
|
48
|
+
function betterFile(basepath: string) {
|
|
49
|
+
const baseFile = shortstop.file(basepath);
|
|
50
|
+
return function fileWithHomeDir(
|
|
51
|
+
v: string,
|
|
52
|
+
callback: ((error: Error | null, result?: Buffer | string | undefined) => void) | undefined,
|
|
53
|
+
) {
|
|
54
|
+
if (!callback) {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
if (v.startsWith('~/')) {
|
|
58
|
+
return baseFile(path.join(os.homedir(), v.slice(2)), callback);
|
|
59
|
+
}
|
|
60
|
+
return baseFile(v, callback);
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
32
64
|
function canonicalizeServiceSuffix(suffix?: string) {
|
|
33
65
|
if (!suffix) {
|
|
34
66
|
return 'internal';
|
|
@@ -70,10 +102,7 @@ const osMethods = {
|
|
|
70
102
|
version: os.version,
|
|
71
103
|
};
|
|
72
104
|
|
|
73
|
-
export function shortstops(
|
|
74
|
-
service: { name: string; },
|
|
75
|
-
sourcedir: string,
|
|
76
|
-
) {
|
|
105
|
+
export function shortstops(service: { name: string }, sourcedir: string) {
|
|
77
106
|
/**
|
|
78
107
|
* Since we use transpiled sources a lot,
|
|
79
108
|
* basedir and sourcedir are meaningfully different reference points.
|
|
@@ -99,10 +128,10 @@ export function shortstops(
|
|
|
99
128
|
},
|
|
100
129
|
|
|
101
130
|
// handle source and base directory intelligently
|
|
102
|
-
path:
|
|
131
|
+
path: betterPath(basedir),
|
|
103
132
|
sourcepath: shortstop.path(sourcedir),
|
|
104
|
-
file:
|
|
105
|
-
sourcefile: shortstop.file(sourcedir)
|
|
133
|
+
file: betterFile(basedir),
|
|
134
|
+
sourcefile: shortstop.file(sourcedir) as ProtocolFn<Buffer | string | undefined>,
|
|
106
135
|
require: betterRequire(basedir),
|
|
107
136
|
sourcerequire: betterRequire(sourcedir),
|
|
108
137
|
|
package/src/express-app/app.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import assert from 'assert';
|
|
2
2
|
import http from 'http';
|
|
3
|
+
import https from 'https';
|
|
3
4
|
import path from 'path';
|
|
4
5
|
|
|
5
6
|
import express from 'express';
|
|
@@ -303,18 +304,32 @@ export async function shutdownApp(app: ServiceExpress) {
|
|
|
303
304
|
(logger as pino.Logger).flush?.();
|
|
304
305
|
}
|
|
305
306
|
|
|
307
|
+
function tlsServer<SLocals extends ServiceLocals = ServiceLocals>(
|
|
308
|
+
app: ServiceExpress<SLocals>,
|
|
309
|
+
config: ConfigurationSchema['server'],
|
|
310
|
+
) {
|
|
311
|
+
return https.createServer(
|
|
312
|
+
{
|
|
313
|
+
key: config.key,
|
|
314
|
+
cert: config.certificate,
|
|
315
|
+
},
|
|
316
|
+
app,
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
|
|
306
320
|
export async function listen<SLocals extends ServiceLocals = ServiceLocals>(
|
|
307
321
|
app: ServiceExpress<SLocals>,
|
|
308
322
|
shutdownHandler?: () => Promise<void>,
|
|
309
323
|
) {
|
|
310
|
-
|
|
324
|
+
const config = app.locals.config.get('server') as Required<ConfigurationSchema['server']>;
|
|
325
|
+
let { port } = config;
|
|
311
326
|
|
|
312
327
|
if (port === 0) {
|
|
313
|
-
port = await findPort(8001);
|
|
328
|
+
port = (await findPort(8001)) as number;
|
|
314
329
|
}
|
|
315
330
|
|
|
316
331
|
const { service, logger } = app.locals;
|
|
317
|
-
const server = http.createServer(app);
|
|
332
|
+
const server = config.certificate ? tlsServer(app, config) : http.createServer(app);
|
|
318
333
|
let shutdownInProgress = false;
|
|
319
334
|
createTerminus(server, {
|
|
320
335
|
timeout: 15000,
|
|
@@ -358,6 +373,10 @@ export async function listen<SLocals extends ServiceLocals = ServiceLocals>(
|
|
|
358
373
|
}
|
|
359
374
|
});
|
|
360
375
|
|
|
376
|
+
server.on('error', (error) => {
|
|
377
|
+
logger.error(error, 'Main service listener error');
|
|
378
|
+
});
|
|
379
|
+
|
|
361
380
|
const metricInfo = (app.locals as AppWithMetrics)[METRICS_KEY] as InternalMetricsInfo;
|
|
362
381
|
delete (app.locals as AppWithMetrics)[METRICS_KEY];
|
|
363
382
|
|
|
@@ -2,11 +2,14 @@ import express from 'express';
|
|
|
2
2
|
import type { Application } from 'express-serve-static-core';
|
|
3
3
|
|
|
4
4
|
import { InternalLocals, ServiceExpress } from '../types';
|
|
5
|
+
import { findPort } from '../development/port-finder';
|
|
5
6
|
|
|
6
7
|
export async function startInternalApp(mainApp: ServiceExpress, port: number) {
|
|
7
8
|
const app = express() as unknown as Application<InternalLocals>;
|
|
8
9
|
app.locals.mainApp = mainApp;
|
|
9
10
|
|
|
11
|
+
const finalPort = port === 0 ? await findPort(3001) : port;
|
|
12
|
+
|
|
10
13
|
app.get('/health', async (req, res) => {
|
|
11
14
|
if (mainApp.locals.service?.healthy) {
|
|
12
15
|
try {
|
|
@@ -21,9 +24,12 @@ export async function startInternalApp(mainApp: ServiceExpress, port: number) {
|
|
|
21
24
|
});
|
|
22
25
|
|
|
23
26
|
const listenPromise = new Promise<void>((accept) => {
|
|
24
|
-
app.locals.server = app.listen(
|
|
27
|
+
app.locals.server = app.listen(finalPort, () => {
|
|
25
28
|
accept();
|
|
26
29
|
});
|
|
30
|
+
app.locals.server.on('error', (error) => {
|
|
31
|
+
mainApp.locals.logger.error(error, 'Internal app server error');
|
|
32
|
+
});
|
|
27
33
|
});
|
|
28
34
|
|
|
29
35
|
await listenPromise;
|