@openapi-typescript-infra/service 2.10.0 → 3.0.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/.eslintrc.js +1 -1
- package/.prettierrc.js +1 -1
- package/CHANGELOG.md +19 -0
- package/README.md +1 -1
- package/build/config/index.d.ts +3 -3
- package/build/config/index.js +5 -9
- package/build/config/index.js.map +1 -1
- package/build/config/schema.d.ts +2 -1
- package/build/config/shortstops.d.ts +2 -29
- package/build/config/shortstops.js +12 -18
- package/build/config/shortstops.js.map +1 -1
- package/build/tsconfig.build.tsbuildinfo +1 -1
- package/build/types.d.ts +4 -3
- package/package.json +9 -12
- package/src/config/index.ts +14 -19
- package/src/config/schema.ts +2 -1
- package/src/config/shortstops.ts +21 -23
- package/src/types.ts +4 -3
- package/vitest.config.ts +1 -1
- package/@types/config.d.ts +0 -64
- package/build/config/types.d.ts +0 -4
- package/build/config/types.js +0 -3
- package/build/config/types.js.map +0 -1
- package/src/config/types.ts +0 -6
package/build/types.d.ts
CHANGED
|
@@ -8,17 +8,18 @@ import type { Request, Response } from 'express';
|
|
|
8
8
|
import type { Application } from 'express-serve-static-core';
|
|
9
9
|
import type { middleware } from 'express-openapi-validator';
|
|
10
10
|
import type { Meter } from '@opentelemetry/api';
|
|
11
|
-
import
|
|
11
|
+
import { Confit } from '@sesamecare-oss/confit';
|
|
12
|
+
import { ConfigurationSchema } from './config/schema';
|
|
12
13
|
export interface InternalLocals extends Record<string, unknown> {
|
|
13
14
|
server?: Server;
|
|
14
15
|
mainApp: ServiceExpress;
|
|
15
16
|
}
|
|
16
17
|
export type ServiceLogger = pino.BaseLogger & Pick<pino.Logger, 'isLevelEnabled'>;
|
|
17
|
-
export interface ServiceLocals {
|
|
18
|
+
export interface ServiceLocals<Config extends ConfigurationSchema = ConfigurationSchema> {
|
|
18
19
|
service: Service;
|
|
19
20
|
name: string;
|
|
20
21
|
logger: ServiceLogger;
|
|
21
|
-
config:
|
|
22
|
+
config: Confit<Config>;
|
|
22
23
|
meter: Meter;
|
|
23
24
|
internalApp: Application<InternalLocals>;
|
|
24
25
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openapi-typescript-infra/service",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.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": {
|
|
@@ -59,7 +59,6 @@
|
|
|
59
59
|
},
|
|
60
60
|
"homepage": "https://github.com/openapi-typescript-infra/service#readme",
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"@gasbuddy/confit": "^3.0.0",
|
|
63
62
|
"@godaddy/terminus": "^4.12.1",
|
|
64
63
|
"@opentelemetry/api": "^1.6.0",
|
|
65
64
|
"@opentelemetry/exporter-prometheus": "^0.43.0",
|
|
@@ -81,6 +80,7 @@
|
|
|
81
80
|
"@opentelemetry/sdk-node": "^0.43.0",
|
|
82
81
|
"@opentelemetry/sdk-trace-base": "^1.17.1",
|
|
83
82
|
"@opentelemetry/semantic-conventions": "^1.17.1",
|
|
83
|
+
"@sesamecare-oss/confit": "^1.0.6",
|
|
84
84
|
"@sesamecare-oss/opentelemetry-node-metrics": "^1.0.1",
|
|
85
85
|
"cookie-parser": "^1.4.6",
|
|
86
86
|
"dotenv": "^16.3.1",
|
|
@@ -93,14 +93,11 @@
|
|
|
93
93
|
"opentelemetry-instrumentation-fetch-node": "^1.1.0",
|
|
94
94
|
"pino": "^8.16.0",
|
|
95
95
|
"read-pkg-up": "^7.0.1",
|
|
96
|
-
"rest-api-support": "^1.16.3"
|
|
97
|
-
"shortstop-dns": "^1.1.0",
|
|
98
|
-
"shortstop-handlers": "^1.1.1",
|
|
99
|
-
"shortstop-yaml": "^1.0.0"
|
|
96
|
+
"rest-api-support": "^1.16.3"
|
|
100
97
|
},
|
|
101
98
|
"devDependencies": {
|
|
102
|
-
"@commitlint/cli": "^17.
|
|
103
|
-
"@commitlint/config-conventional": "^17.
|
|
99
|
+
"@commitlint/cli": "^17.8.0",
|
|
100
|
+
"@commitlint/config-conventional": "^17.8.0",
|
|
104
101
|
"@openapi-typescript-infra/coconfig": "^4.2.1",
|
|
105
102
|
"@semantic-release/changelog": "^6.0.3",
|
|
106
103
|
"@semantic-release/commit-analyzer": "^11.0.0",
|
|
@@ -113,11 +110,11 @@
|
|
|
113
110
|
"@types/glob": "^8.1.0",
|
|
114
111
|
"@types/lodash": "^4.14.199",
|
|
115
112
|
"@types/minimist": "^1.2.3",
|
|
116
|
-
"@types/node": "^20.8.
|
|
113
|
+
"@types/node": "^20.8.6",
|
|
117
114
|
"@types/supertest": "^2.0.14",
|
|
118
|
-
"@typescript-eslint/eslint-plugin": "^6.
|
|
119
|
-
"@typescript-eslint/parser": "^6.
|
|
120
|
-
"coconfig": "^0.
|
|
115
|
+
"@typescript-eslint/eslint-plugin": "^6.8.0",
|
|
116
|
+
"@typescript-eslint/parser": "^6.8.0",
|
|
117
|
+
"coconfig": "^1.0.0",
|
|
121
118
|
"eslint": "^8.51.0",
|
|
122
119
|
"eslint-config-prettier": "^9.0.0",
|
|
123
120
|
"eslint-plugin-import": "^2.28.1",
|
package/src/config/index.ts
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
|
|
4
|
-
import confit from '@
|
|
4
|
+
import { BaseConfitType, Confit, Factory, confit } from '@sesamecare-oss/confit';
|
|
5
5
|
|
|
6
6
|
import { findPort } from '../development/port-finder';
|
|
7
7
|
|
|
8
8
|
import { shortstops } from './shortstops';
|
|
9
|
-
import type { ConfigStore } from './types';
|
|
10
9
|
import type { ConfigurationSchema } from './schema';
|
|
11
10
|
|
|
12
11
|
// Order matters here.
|
|
13
|
-
const ENVIRONMENTS = ['production', 'staging', 'test', 'development'];
|
|
12
|
+
const ENVIRONMENTS = ['production', 'staging', 'test', 'development'] as const;
|
|
14
13
|
|
|
15
14
|
async function pathExists(f: string) {
|
|
16
15
|
return new Promise((accept, reject) => {
|
|
@@ -26,12 +25,12 @@ async function pathExists(f: string) {
|
|
|
26
25
|
});
|
|
27
26
|
}
|
|
28
27
|
|
|
29
|
-
async function addDefaultConfiguration(
|
|
30
|
-
configFactory:
|
|
28
|
+
async function addDefaultConfiguration<Config extends ConfigurationSchema = ConfigurationSchema>(
|
|
29
|
+
configFactory: Factory<Config>,
|
|
31
30
|
directory: string,
|
|
32
|
-
envConfit:
|
|
31
|
+
envConfit: Confit<BaseConfitType>,
|
|
33
32
|
) {
|
|
34
|
-
const addIfEnv = async (e:
|
|
33
|
+
const addIfEnv = async (e: (typeof ENVIRONMENTS)[number]) => {
|
|
35
34
|
const c = path.join(directory, `${e}.json`);
|
|
36
35
|
if (envConfit.get(`env:${e}`) && (await pathExists(c))) {
|
|
37
36
|
configFactory.addDefault(c);
|
|
@@ -60,19 +59,17 @@ export interface ServiceConfigurationSpec {
|
|
|
60
59
|
name: string;
|
|
61
60
|
}
|
|
62
61
|
|
|
63
|
-
export async function loadConfiguration({
|
|
62
|
+
export async function loadConfiguration<Config extends ConfigurationSchema>({
|
|
64
63
|
name,
|
|
65
64
|
configurationDirectories: dirs,
|
|
66
65
|
sourceDirectory,
|
|
67
|
-
}: ServiceConfigurationSpec): Promise<
|
|
66
|
+
}: ServiceConfigurationSpec): Promise<Confit<Config>> {
|
|
68
67
|
const defaultProtocols = shortstops({ name }, sourceDirectory);
|
|
69
68
|
const specificConfig = dirs[dirs.length - 1];
|
|
70
69
|
|
|
71
70
|
// This confit version just gets us environment info
|
|
72
|
-
const envConfit
|
|
73
|
-
|
|
74
|
-
});
|
|
75
|
-
const configFactory = confit({
|
|
71
|
+
const envConfit = await confit({ basedir: specificConfig }).create();
|
|
72
|
+
const configFactory = confit<ConfigurationSchema>({
|
|
76
73
|
basedir: specificConfig,
|
|
77
74
|
protocols: defaultProtocols,
|
|
78
75
|
});
|
|
@@ -89,13 +86,11 @@ export async function loadConfiguration({
|
|
|
89
86
|
Promise.resolve(),
|
|
90
87
|
);
|
|
91
88
|
|
|
92
|
-
const loaded
|
|
93
|
-
configFactory.create((err, config) => (err ? reject(err) : accept(config)));
|
|
94
|
-
});
|
|
89
|
+
const loaded = await configFactory.create();
|
|
95
90
|
|
|
96
91
|
// Because other things need to know the port we choose, we pick it here if it's
|
|
97
92
|
// configured to auto-select
|
|
98
|
-
const serverConfig = loaded.get('server')
|
|
93
|
+
const serverConfig = loaded.get('server');
|
|
99
94
|
if (serverConfig.port === 0) {
|
|
100
95
|
const port = (await findPort(8001)) as number;
|
|
101
96
|
loaded.set('server:port', port);
|
|
@@ -104,7 +99,8 @@ export async function loadConfiguration({
|
|
|
104
99
|
// TODO init other stuff based on config here, such as key management or
|
|
105
100
|
// other cloud-aware shortstop handlers
|
|
106
101
|
|
|
107
|
-
|
|
102
|
+
// Not sure why this is necessary, but it is
|
|
103
|
+
return loaded as unknown as Confit<Config>;
|
|
108
104
|
}
|
|
109
105
|
|
|
110
106
|
export function insertConfigurationBefore(
|
|
@@ -123,4 +119,3 @@ export function insertConfigurationBefore(
|
|
|
123
119
|
}
|
|
124
120
|
|
|
125
121
|
export * from './schema';
|
|
126
|
-
export * from './types';
|
package/src/config/schema.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { BaseConfitType } from '@sesamecare-oss/confit';
|
|
1
2
|
import type { Level } from 'pino';
|
|
2
3
|
|
|
3
4
|
export interface ServiceConfiguration {
|
|
@@ -12,7 +13,7 @@ export interface ConfigurationItemEnabled {
|
|
|
12
13
|
enabled?: boolean;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
export interface ConfigurationSchema extends
|
|
16
|
+
export interface ConfigurationSchema extends BaseConfitType {
|
|
16
17
|
trustProxy?: string[];
|
|
17
18
|
logging?: {
|
|
18
19
|
level?: Level;
|
package/src/config/shortstops.ts
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import os from 'os';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
|
|
4
|
-
import
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
import {
|
|
5
|
+
base64Handler,
|
|
6
|
+
envHandler,
|
|
7
|
+
fileHandler,
|
|
8
|
+
pathHandler,
|
|
9
|
+
requireHandler,
|
|
10
|
+
yamlHandler,
|
|
11
|
+
type ShortstopHandler,
|
|
12
|
+
} from '@sesamecare-oss/confit';
|
|
8
13
|
|
|
9
14
|
/**
|
|
10
15
|
* Default shortstop handlers for GasBuddy service configuration
|
|
@@ -15,7 +20,7 @@ import { ProtocolFn } from '@gasbuddy/confit';
|
|
|
15
20
|
* with a url-like hash pattern
|
|
16
21
|
*/
|
|
17
22
|
function betterRequire(basepath: string) {
|
|
18
|
-
const baseRequire =
|
|
23
|
+
const baseRequire = requireHandler(basepath);
|
|
19
24
|
return function hashRequire(v: string) {
|
|
20
25
|
const [moduleName, func] = v.split('#');
|
|
21
26
|
const module = baseRequire(moduleName);
|
|
@@ -33,7 +38,7 @@ function betterRequire(basepath: string) {
|
|
|
33
38
|
* Just like path, but resolve ~/ to the home directory
|
|
34
39
|
*/
|
|
35
40
|
function betterPath(basepath: string) {
|
|
36
|
-
const basePath =
|
|
41
|
+
const basePath = pathHandler(basepath);
|
|
37
42
|
return function pathWithHomeDir(v: string) {
|
|
38
43
|
if (v.startsWith('~/')) {
|
|
39
44
|
return basePath(path.join(os.homedir(), v.slice(2)));
|
|
@@ -46,18 +51,12 @@ function betterPath(basepath: string) {
|
|
|
46
51
|
* Just like file, but resolve ~/ to the home directory
|
|
47
52
|
*/
|
|
48
53
|
function betterFile(basepath: string) {
|
|
49
|
-
const baseFile =
|
|
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
|
-
}
|
|
54
|
+
const baseFile = fileHandler(basepath);
|
|
55
|
+
return function fileWithHomeDir(v: string) {
|
|
57
56
|
if (v.startsWith('~/')) {
|
|
58
|
-
return baseFile(path.join(os.homedir(), v.slice(2))
|
|
57
|
+
return baseFile(path.join(os.homedir(), v.slice(2)));
|
|
59
58
|
}
|
|
60
|
-
return baseFile(v
|
|
59
|
+
return baseFile(v);
|
|
61
60
|
};
|
|
62
61
|
}
|
|
63
62
|
|
|
@@ -109,7 +108,7 @@ export function shortstops(service: { name: string }, sourcedir: string) {
|
|
|
109
108
|
*/
|
|
110
109
|
const basedir = path.join(sourcedir, '..');
|
|
111
110
|
|
|
112
|
-
const env =
|
|
111
|
+
const env = envHandler();
|
|
113
112
|
|
|
114
113
|
return {
|
|
115
114
|
env,
|
|
@@ -121,7 +120,7 @@ export function shortstops(service: { name: string }, sourcedir: string) {
|
|
|
121
120
|
}
|
|
122
121
|
return !!env(v);
|
|
123
122
|
},
|
|
124
|
-
base64:
|
|
123
|
+
base64: base64Handler(),
|
|
125
124
|
regex(v: string) {
|
|
126
125
|
const [, pattern, flags] = v.match(/^\/(.*)\/([a-z]*)/) || [];
|
|
127
126
|
return new RegExp(pattern, flags);
|
|
@@ -129,14 +128,14 @@ export function shortstops(service: { name: string }, sourcedir: string) {
|
|
|
129
128
|
|
|
130
129
|
// handle source and base directory intelligently
|
|
131
130
|
path: betterPath(basedir),
|
|
132
|
-
sourcepath:
|
|
131
|
+
sourcepath: pathHandler(sourcedir),
|
|
133
132
|
file: betterFile(basedir),
|
|
134
|
-
sourcefile:
|
|
133
|
+
sourcefile: fileHandler(sourcedir),
|
|
135
134
|
require: betterRequire(basedir),
|
|
136
135
|
sourcerequire: betterRequire(sourcedir),
|
|
137
136
|
|
|
138
137
|
// Sometimes yaml is more pleasant for configuration
|
|
139
|
-
yaml:
|
|
138
|
+
yaml: yamlHandler(basedir),
|
|
140
139
|
|
|
141
140
|
// Switch on service type
|
|
142
141
|
servicetype: serviceTypeFactory(service.name),
|
|
@@ -145,10 +144,9 @@ export function shortstops(service: { name: string }, sourcedir: string) {
|
|
|
145
144
|
os(p: keyof typeof osMethods) {
|
|
146
145
|
return osMethods[p]();
|
|
147
146
|
},
|
|
148
|
-
dns: shortstopDns() as ProtocolFn<string>,
|
|
149
147
|
// No-op in case you have values that start with a shortstop handler name (and colon)
|
|
150
148
|
literal(v: string) {
|
|
151
149
|
return v;
|
|
152
150
|
},
|
|
153
|
-
}
|
|
151
|
+
} as Record<string, ShortstopHandler<string, unknown>>;
|
|
154
152
|
}
|
package/src/types.ts
CHANGED
|
@@ -6,8 +6,9 @@ import type { Request, Response } from 'express';
|
|
|
6
6
|
import type { Application } from 'express-serve-static-core';
|
|
7
7
|
import type { middleware } from 'express-openapi-validator';
|
|
8
8
|
import type { Meter } from '@opentelemetry/api';
|
|
9
|
+
import { Confit } from '@sesamecare-oss/confit';
|
|
9
10
|
|
|
10
|
-
import
|
|
11
|
+
import { ConfigurationSchema } from './config/schema';
|
|
11
12
|
|
|
12
13
|
export interface InternalLocals extends Record<string, unknown> {
|
|
13
14
|
server?: Server;
|
|
@@ -19,11 +20,11 @@ export type ServiceLogger = pino.BaseLogger & Pick<pino.Logger, 'isLevelEnabled'
|
|
|
19
20
|
// Vanilla express wants this to extend Record<string, any> but this is a mistake
|
|
20
21
|
// because you lose type checking on it, even though I get that underneath it truly
|
|
21
22
|
// is Record<string, any>
|
|
22
|
-
export interface ServiceLocals {
|
|
23
|
+
export interface ServiceLocals<Config extends ConfigurationSchema = ConfigurationSchema> {
|
|
23
24
|
service: Service;
|
|
24
25
|
name: string;
|
|
25
26
|
logger: ServiceLogger;
|
|
26
|
-
config:
|
|
27
|
+
config: Confit<Config>;
|
|
27
28
|
meter: Meter;
|
|
28
29
|
internalApp: Application<InternalLocals>;
|
|
29
30
|
}
|
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@0.
|
|
6
|
+
* @version coconfig@1.0.0
|
|
7
7
|
*/
|
|
8
8
|
import cjs from '@openapi-typescript-infra/coconfig';
|
|
9
9
|
import * as esmToCjs from '@openapi-typescript-infra/coconfig';
|
package/@types/config.d.ts
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
/* eslint-disable import/no-default-export */
|
|
2
|
-
declare module 'shortstop-handlers' {
|
|
3
|
-
export function require(module: string): ReturnType<NodeRequire>;
|
|
4
|
-
export function env(): (envVarName: string) => string | undefined;
|
|
5
|
-
export function base64(): (blob: string) => Buffer;
|
|
6
|
-
export function path(baseDir?: string): (relativePath: string) => string;
|
|
7
|
-
export function file(
|
|
8
|
-
baseDir?: string,
|
|
9
|
-
): (
|
|
10
|
-
relativePath: string,
|
|
11
|
-
cb: (err: Error | null, result?: Buffer | string | undefined) => void,
|
|
12
|
-
) => void;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
declare module 'shortstop-yaml' {
|
|
16
|
-
export default function yaml<T>(
|
|
17
|
-
basepath: string,
|
|
18
|
-
): (path: string, callback: (error?: Error, result?: T) => void) => void;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
declare module 'shortstop-dns' {
|
|
22
|
-
export default function dns(opts?: {
|
|
23
|
-
family?: number;
|
|
24
|
-
all?: boolean;
|
|
25
|
-
}): (address: string, callback: (error?: Error, result?: string[]) => void) => void;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
declare module '@gasbuddy/confit' {
|
|
29
|
-
type Json = ReturnType<typeof JSON.parse>;
|
|
30
|
-
|
|
31
|
-
export type ProtocolFn<CallbackType> = (
|
|
32
|
-
value: Json,
|
|
33
|
-
callback?: (error: Error | null, result?: CallbackType) => void,
|
|
34
|
-
) => void;
|
|
35
|
-
|
|
36
|
-
interface ProtocolsSetPrivate<C> {
|
|
37
|
-
[protocol: string]: ProtocolFn<C> | ProtocolFn<C>[];
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
interface ConfigStore {
|
|
41
|
-
get<T>(name: string): T | undefined;
|
|
42
|
-
set<T>(name: string, newValue: T): T;
|
|
43
|
-
use(newSettings: unknown): void;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
type Options<C> = {
|
|
47
|
-
basedir: string;
|
|
48
|
-
protocols: ProtocolsSetPrivate<C>;
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
interface ConfigFactory {
|
|
52
|
-
create(callback: (err: Error, config: ConfigStore) => unknown): void;
|
|
53
|
-
addOverride(filepathOrSettingsObj: string | object): this;
|
|
54
|
-
addDefault(filepathOrSettingsObj: string | object): this;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function confit<C>(optionsOrBaseDir: Options<C> | string): ConfigFactory;
|
|
58
|
-
|
|
59
|
-
namespace confit {
|
|
60
|
-
export type ProtocolsSet<C> = ProtocolsSetPrivate<C>;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
export default confit;
|
|
64
|
-
}
|
package/build/config/types.d.ts
DELETED
package/build/config/types.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/config/types.ts"],"names":[],"mappings":""}
|