@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/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 type { ConfigStore } from './config/types';
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: ConfigStore;
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": "2.10.0",
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.7.2",
103
- "@commitlint/config-conventional": "^17.7.0",
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.5",
113
+ "@types/node": "^20.8.6",
117
114
  "@types/supertest": "^2.0.14",
118
- "@typescript-eslint/eslint-plugin": "^6.7.5",
119
- "@typescript-eslint/parser": "^6.7.5",
120
- "coconfig": "^0.13.3",
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",
@@ -1,16 +1,15 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
 
4
- import confit from '@gasbuddy/confit';
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: ReturnType<typeof confit>,
28
+ async function addDefaultConfiguration<Config extends ConfigurationSchema = ConfigurationSchema>(
29
+ configFactory: Factory<Config>,
31
30
  directory: string,
32
- envConfit: ConfigStore,
31
+ envConfit: Confit<BaseConfitType>,
33
32
  ) {
34
- const addIfEnv = async (e: string) => {
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<ConfigStore> {
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: ConfigStore = await new Promise((accept, reject) => {
73
- confit(specificConfig).create((err, config) => (err ? reject(err) : accept(config)));
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: ConfigStore = await new Promise((accept, reject) => {
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') as Required<ConfigurationSchema['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
- return loaded;
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';
@@ -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 Record<string, unknown> {
16
+ export interface ConfigurationSchema extends BaseConfitType {
16
17
  trustProxy?: string[];
17
18
  logging?: {
18
19
  level?: Level;
@@ -1,10 +1,15 @@
1
1
  import os from 'os';
2
2
  import path from 'path';
3
3
 
4
- import shortstop from 'shortstop-handlers';
5
- import shortstopYaml from 'shortstop-yaml';
6
- import shortstopDns from 'shortstop-dns';
7
- import { ProtocolFn } from '@gasbuddy/confit';
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 = shortstop.require(basepath);
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 = shortstop.path(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 = 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
- }
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)), callback);
57
+ return baseFile(path.join(os.homedir(), v.slice(2)));
59
58
  }
60
- return baseFile(v, callback);
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 = shortstop.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: shortstop.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: shortstop.path(sourcedir),
131
+ sourcepath: pathHandler(sourcedir),
133
132
  file: betterFile(basedir),
134
- sourcefile: shortstop.file(sourcedir) as ProtocolFn<Buffer | string | undefined>,
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: shortstopYaml(basedir) as ProtocolFn<ReturnType<typeof JSON.parse>>,
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 type { ConfigStore } from './config/types';
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: ConfigStore;
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.13.3
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';
@@ -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
- }
@@ -1,4 +0,0 @@
1
- export interface ConfigStore {
2
- get<T>(name: string): T | undefined;
3
- set<T>(name: string, value: T): void;
4
- }
@@ -1,3 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- //# sourceMappingURL=types.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/config/types.ts"],"names":[],"mappings":""}
@@ -1,6 +0,0 @@
1
- export interface ConfigStore {
2
- // Confit supports more things (e.g. use), but that's not how we
3
- // intend it to be used.
4
- get<T>(name: string): T | undefined;
5
- set<T>(name: string, value: T): void;
6
- }