@openapi-typescript-infra/service 4.0.0 → 4.1.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
@@ -17,10 +17,15 @@ export type ServiceLogger = pino.BaseLogger & Pick<pino.Logger, 'isLevelEnabled'
17
17
  export interface ServiceLocals<Config extends ConfigurationSchema = ConfigurationSchema> {
18
18
  service: Service;
19
19
  name: string;
20
+ version: string;
20
21
  logger: ServiceLogger;
21
22
  config: Config;
22
23
  meter: Meter;
23
24
  internalApp: Application<InternalLocals<this>>;
25
+ /**
26
+ * This is the parsed OpenAPI spec we are hosting (if openapi is enabled)
27
+ */
28
+ openApiSpecification?: ReturnType<typeof JSON.parse>;
24
29
  }
25
30
  export interface RequestLocals {
26
31
  rawBody?: Buffer | true;
@@ -49,6 +54,7 @@ export interface Service<SLocals extends AnyServiceLocals = ServiceLocals<Config
49
54
  export type ServiceFactory<SLocals extends AnyServiceLocals = ServiceLocals<ConfigurationSchema>, RLocals extends RequestLocals = RequestLocals> = () => Service<SLocals, RLocals>;
50
55
  export interface ServiceStartOptions<SLocals extends AnyServiceLocals = ServiceLocals<ConfigurationSchema>, RLocals extends RequestLocals = RequestLocals> {
51
56
  name: string;
57
+ version: string;
52
58
  rootDirectory: string;
53
59
  codepath?: 'build' | 'src' | 'dist';
54
60
  locals?: Partial<SLocals>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openapi-typescript-infra/service",
3
- "version": "4.0.0",
3
+ "version": "4.1.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": {
package/src/bootstrap.ts CHANGED
@@ -25,6 +25,8 @@ interface BootstrapArguments {
25
25
  telemetry?: boolean;
26
26
  // Don't bind to http port or expose metrics
27
27
  nobind?: boolean;
28
+ // The version of the app, else discovered via read-pkg-up
29
+ version?: string;
28
30
  }
29
31
 
30
32
  function resolveMain(packageJson: NormalizedPackageJson) {
@@ -39,6 +41,7 @@ async function getServiceDetails(argv: BootstrapArguments = {}) {
39
41
  return {
40
42
  rootDirectory: argv.root,
41
43
  name: argv.name,
44
+ version: argv.version || '0.0.0',
42
45
  main: argv.main || (isDev() && !argv.built ? 'src/index.ts' : 'build/index.js'),
43
46
  };
44
47
  }
@@ -55,6 +58,7 @@ async function getServiceDetails(argv: BootstrapArguments = {}) {
55
58
  main,
56
59
  rootDirectory: path.dirname(pkg.path),
57
60
  name: parts[parts.length - 1],
61
+ version: pkg.packageJson.version,
58
62
  };
59
63
  }
60
64
 
@@ -72,7 +76,7 @@ export async function bootstrap<
72
76
  SLocals extends AnyServiceLocals = ServiceLocals<ConfigurationSchema>,
73
77
  RLocals extends RequestLocals = RequestLocals,
74
78
  >(argv?: BootstrapArguments) {
75
- const { main, rootDirectory, name } = await getServiceDetails(argv);
79
+ const { main, rootDirectory, name, version } = await getServiceDetails(argv);
76
80
 
77
81
  let entrypoint: string;
78
82
  let codepath: 'build' | 'dist' | 'src' = 'build';
@@ -107,6 +111,7 @@ export async function bootstrap<
107
111
  rootDirectory,
108
112
  service: absoluteEntrypoint,
109
113
  codepath,
114
+ version,
110
115
  });
111
116
  }
112
117
 
@@ -115,6 +120,7 @@ export async function bootstrap<
115
120
  const impl = require(absoluteEntrypoint);
116
121
  const opts: ServiceStartOptions<SLocals, RLocals> = {
117
122
  name,
123
+ version,
118
124
  rootDirectory,
119
125
  service: impl.default || impl.service,
120
126
  codepath,
@@ -45,7 +45,7 @@ export async function startApp<
45
45
  SLocals extends AnyServiceLocals = ServiceLocals<ConfigurationSchema>,
46
46
  RLocals extends RequestLocals = RequestLocals,
47
47
  >(startOptions: ServiceStartOptions<SLocals, RLocals>): Promise<ServiceExpress<SLocals>> {
48
- const { service, rootDirectory, codepath = 'build', name } = startOptions;
48
+ const { service, rootDirectory, codepath = 'build', name, version } = startOptions;
49
49
  const shouldPrettyPrint = isDev() && !process.env.NO_PRETTY_LOGS;
50
50
  const destination = pino.destination({
51
51
  sync: isSyncLogging(),
@@ -99,11 +99,12 @@ export async function startApp<
99
99
  app.disable('etag');
100
100
  }
101
101
 
102
- Object.assign(app.locals, { services: {} }, startOptions.locals, {
102
+ Object.assign(app.locals, startOptions.locals, {
103
103
  service: serviceImpl,
104
104
  logger,
105
105
  config,
106
106
  name,
107
+ version,
107
108
  });
108
109
 
109
110
  if (serviceImpl.attach) {
@@ -360,6 +361,12 @@ export async function listen<SLocals extends AnyServiceLocals = ServiceLocals<Co
360
361
  } else {
361
362
  locals.logger.info('No metrics will be exported');
362
363
  }
364
+ if (app.locals.openApiSpecification) {
365
+ locals.internalApp.get('/api-docs/openapi.json', (req, res) => {
366
+ res.json(app.locals.openApiSpecification);
367
+ });
368
+ locals.logger.info('OpenAPI specification available at /api-docs/openapi.json');
369
+ }
363
370
  accept();
364
371
  })
365
372
  .catch((error) => {
package/src/openapi.ts CHANGED
@@ -2,6 +2,7 @@ import path from 'path';
2
2
 
3
3
  import _ from 'lodash';
4
4
  import * as OpenApiValidator from 'express-openapi-validator';
5
+ import { OpenAPIFramework } from 'express-openapi-validator/dist/framework/index';
5
6
  import type { Handler } from 'express';
6
7
 
7
8
  import type { AnyServiceLocals, ServiceExpress, ServiceLocals } from './types';
@@ -64,6 +65,12 @@ export async function openApi<
64
65
  {} as Record<string, Record<string, unknown>>,
65
66
  );
66
67
 
68
+ app.locals.openApiSpecification = await new OpenAPIFramework({ apiDoc: apiSpec })
69
+ .initialize({ visitApi() {} })
70
+ .catch((error) => {
71
+ app.locals.logger.error(error, 'Failed to parse and load OpenAPI spec');
72
+ });
73
+
67
74
  const defaultOptions: OAPIOpts = {
68
75
  apiSpec,
69
76
  ignoreUndocumented: true,
package/src/types.ts CHANGED
@@ -24,10 +24,15 @@ export type ServiceLogger = pino.BaseLogger & Pick<pino.Logger, 'isLevelEnabled'
24
24
  export interface ServiceLocals<Config extends ConfigurationSchema = ConfigurationSchema> {
25
25
  service: Service;
26
26
  name: string;
27
+ version: string;
27
28
  logger: ServiceLogger;
28
29
  config: Config;
29
30
  meter: Meter;
30
31
  internalApp: Application<InternalLocals<this>>;
32
+ /**
33
+ * This is the parsed OpenAPI spec we are hosting (if openapi is enabled)
34
+ */
35
+ openApiSpecification?: ReturnType<typeof JSON.parse>;
31
36
  }
32
37
 
33
38
  export interface RequestLocals {
@@ -111,6 +116,7 @@ export interface ServiceStartOptions<
111
116
  RLocals extends RequestLocals = RequestLocals,
112
117
  > {
113
118
  name: string;
119
+ version: string;
114
120
  rootDirectory: string;
115
121
 
116
122
  // Defaults to "build", but can be set to "src" to run off non-built source