@travetto/openapi 8.0.0-alpha.9 → 8.0.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/README.md CHANGED
@@ -26,12 +26,12 @@ All of the high level configurations can be found in the following structure:
26
26
  ```typescript
27
27
  import path from 'node:path';
28
28
 
29
- import type { ServerObject, ContactObject, LicenseObject } from 'openapi3-ts/oas31';
29
+ import type { ContactObject, LicenseObject, ServerObject } from 'openapi3-ts/oas31';
30
30
 
31
31
  import { Config } from '@travetto/config';
32
+ import { PostConstruct } from '@travetto/di';
32
33
  import { Runtime } from '@travetto/runtime';
33
34
  import { Required } from '@travetto/schema';
34
- import { PostConstruct } from '@travetto/di';
35
35
 
36
36
  /**
37
37
  * API Information, infers as much as possible from the package.json
@@ -105,7 +105,8 @@ export class ApiSpecConfig {
105
105
  this.persist ??= Runtime.localDevelopment;
106
106
  }
107
107
  if (this.persist) {
108
- if (!/[.](json|ya?ml) $/.test(this.output)) { // Assume a folder
108
+ if (!/[.](json|ya?ml) $/.test(this.output)) {
109
+ // Assume a folder
109
110
  this.output = path.resolve(this.output, 'openapi.yml');
110
111
  }
111
112
  }
@@ -114,43 +115,53 @@ export class ApiSpecConfig {
114
115
  ```
115
116
 
116
117
  ## Spec Generation
117
- The framework, when in watch mode, will generate the [OpenAPI](https://github.com/OAI/OpenAPI-Specification) specification in either [JSON](https://www.json.org) or [YAML](https://en.wikipedia.org/wiki/YAML). This module integrates with the file watching paradigm and can regenerate the openapi spec as changes to endpoints and models are made during development. The output format is defined by the suffix of the output file, `.yaml` or `.json`.
118
+ The framework, when in watch mode, will generate the [OpenAPI](https://github.com/OAI/OpenAPI-Specification) specification in either [JSON](https://www.json.org) or [YAML](https://en.wikipedia.org/wiki/YAML). This module integrates with the file watching paradigm and can regenerate the openapi spec as changes to endpoints and models are made during development. The output format is defined by the suffix of the output file, `.yaml` or `.json`.
118
119
 
119
120
  ## CLI - openapi:spec
120
- The module provides a command for the [Command Line Interface](https://github.com/travetto/travetto/tree/main/module/cli#readme "CLI infrastructure for Travetto framework") to allow scripting file generation.
121
+ The command will load your application, in non-listening mode, to collect all the endpoints and model information, to produce the `openapi.yml`. Once produced, the code will store the output in the specified location.
121
122
 
122
- **Terminal: OpenAPI usage**
123
+ **Terminal: Help for openapi:spec**
123
124
  ```bash
124
125
  $ trv openapi:spec --help
125
126
 
126
127
  Usage: openapi:spec [options]
127
128
 
129
+ Description:
130
+ Generate the OpenAPI specification for the selected module.
131
+
132
+ The resulting JSON can be written to stdout or to a file path for use in
133
+ downstream tooling, CI publishing, and client generation pipelines.
134
+
128
135
  Options:
129
136
  -o, --output <string> Output files
130
137
  -m, --module <module> Module to run for
131
138
  --help display help for command
132
139
  ```
133
140
 
134
- The command will run your application, in non-server mode, to collect all the endpoints and model information, to produce the `openapi.yml`. Once produced, the code will store the output in the specified location.
135
-
136
141
  **Note**: The module supports generating the OpenAPI spec in real-time while listening for changes to endpoints and models.
137
142
 
138
143
  ## CLI - openapi:client
139
- The module provides a command for the [Command Line Interface](https://github.com/travetto/travetto/tree/main/module/cli#readme "CLI infrastructure for Travetto framework") to allow client generation from the API structure.
144
+ Generate API clients from an OpenAPI specification using the generator image.
140
145
 
141
- **Terminal: OpenAPI usage**
146
+ **Terminal: Help for openapi:client**
142
147
  ```bash
143
148
  $ trv openapi:client --help
144
149
 
145
150
  Usage: openapi:client [options] <format:string>
146
151
 
152
+ Description:
153
+ Generate API clients from an OpenAPI specification using the generator image.
154
+
155
+ This command wraps OpenAPI Generator in Docker and writes generated client code
156
+ into the configured output folder.
157
+
147
158
  Options:
148
- -x, --extended-help Show Extended Help (default: false)
149
- -a, --additional-properties <string> Additional Properties (default: [])
150
- -i, --input <string> Input file (default: "./openapi.yml")
151
- -o, --output <string> Output folder (default: "./api-client")
152
- -d, --docker-image <string> Docker Image to user (default: "openapitools/openapi-generator-cli:latest")
159
+ -x, --extended-help Show expanded generator help for all available formats/options. (default: false)
160
+ -a, --additional-properties <string> Additional generator properties passed as comma-separated key/value pairs. (default: [])
161
+ -i, --input <string> Input OpenAPI document path. (default: "./openapi.yml")
162
+ -o, --output <string> Output directory for generated client sources. (default: "./api-client")
163
+ -d, --docker-image <string> Docker image used to run OpenAPI Generator. (default: "openapitools/openapi-generator-cli:latest")
153
164
  --help display help for command
154
165
  ```
155
166
 
156
- This tool relies upon a custom build of [OpenAPI client generation tools](https://github.com/OpenAPITools/openapi-generator), which supports watching. This allows for fast responsive client generation as the shape of the API changes.
167
+ This tool relies upon a custom build of [OpenAPI client generation tools](https://github.com/OpenAPITools/openapi-generator), which supports watching. This allows for fast responsive client generation as the shape of the API changes.
package/__index__.ts CHANGED
@@ -1,3 +1,3 @@
1
+ export * from './src/config.ts';
1
2
  export * from './src/generate.ts';
2
3
  export * from './src/service.ts';
3
- export * from './src/config.ts';
package/package.json CHANGED
@@ -1,40 +1,43 @@
1
1
  {
2
2
  "name": "@travetto/openapi",
3
- "version": "8.0.0-alpha.9",
4
- "type": "module",
3
+ "version": "8.0.0",
5
4
  "description": "OpenAPI integration support for the Travetto framework",
6
5
  "keywords": [
7
- "web",
8
- "travetto",
9
6
  "decorators",
10
7
  "schema",
11
- "typescript"
8
+ "travetto",
9
+ "typescript",
10
+ "web"
12
11
  ],
13
12
  "homepage": "https://travetto.io",
14
13
  "license": "MIT",
15
14
  "author": {
16
- "email": "travetto.framework@gmail.com",
17
- "name": "Travetto Framework"
15
+ "name": "Travetto Framework",
16
+ "email": "travetto.framework@gmail.com"
17
+ },
18
+ "repository": {
19
+ "url": "git+https://github.com/travetto/travetto.git",
20
+ "directory": "module/openapi"
18
21
  },
19
22
  "files": [
20
23
  "__index__.ts",
21
24
  "src",
22
25
  "support"
23
26
  ],
27
+ "type": "module",
24
28
  "main": "__index__.ts",
25
- "repository": {
26
- "url": "git+https://github.com/travetto/travetto.git",
27
- "directory": "module/openapi"
29
+ "publishConfig": {
30
+ "access": "public"
28
31
  },
29
32
  "dependencies": {
30
- "@travetto/config": "^8.0.0-alpha.8",
31
- "@travetto/schema": "^8.0.0-alpha.8",
32
- "@travetto/web": "^8.0.0-alpha.9",
33
- "openapi3-ts": "^4.5.0",
34
- "yaml": "^2.8.2"
33
+ "@travetto/config": "^8.0.0",
34
+ "@travetto/schema": "^8.0.0",
35
+ "@travetto/web": "^8.0.0",
36
+ "openapi3-ts": "^4.6.1",
37
+ "yaml": "^2.9.0"
35
38
  },
36
39
  "peerDependencies": {
37
- "@travetto/cli": "^8.0.0-alpha.13"
40
+ "@travetto/cli": "^8.0.0"
38
41
  },
39
42
  "peerDependenciesMeta": {
40
43
  "@travetto/cli": {
@@ -43,8 +46,5 @@
43
46
  },
44
47
  "travetto": {
45
48
  "displayName": "OpenAPI Specification"
46
- },
47
- "publishConfig": {
48
- "access": "public"
49
49
  }
50
50
  }
package/src/config.ts CHANGED
@@ -1,11 +1,11 @@
1
1
  import path from 'node:path';
2
2
 
3
- import type { ServerObject, ContactObject, LicenseObject } from 'openapi3-ts/oas31';
3
+ import type { ContactObject, LicenseObject, ServerObject } from 'openapi3-ts/oas31';
4
4
 
5
5
  import { Config } from '@travetto/config';
6
+ import { PostConstruct } from '@travetto/di';
6
7
  import { Runtime } from '@travetto/runtime';
7
8
  import { Required } from '@travetto/schema';
8
- import { PostConstruct } from '@travetto/di';
9
9
 
10
10
  /**
11
11
  * API Information, infers as much as possible from the package.json
@@ -79,9 +79,10 @@ export class ApiSpecConfig {
79
79
  this.persist ??= Runtime.localDevelopment;
80
80
  }
81
81
  if (this.persist) {
82
- if (!/[.](json|ya?ml)$/.test(this.output)) { // Assume a folder
82
+ if (!/[.](json|ya?ml)$/.test(this.output)) {
83
+ // Assume a folder
83
84
  this.output = path.resolve(this.output, 'openapi.yml');
84
85
  }
85
86
  }
86
87
  }
87
- }
88
+ }
package/src/controller.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  import { stringify } from 'yaml';
2
2
 
3
- import { ConfigureInterceptor, Controller, CorsInterceptor, Get, SetHeaders } from '@travetto/web';
4
3
  import { Inject } from '@travetto/di';
5
4
  import { IsPrivate } from '@travetto/schema';
5
+ import { ConfigureInterceptor, Controller, CorsInterceptor, Get, SetHeaders } from '@travetto/web';
6
6
 
7
7
  import type { OpenApiService } from './service.ts';
8
8
 
@@ -13,7 +13,6 @@ import type { OpenApiService } from './service.ts';
13
13
  @Controller('/')
14
14
  @ConfigureInterceptor(CorsInterceptor, { origins: ['*'] })
15
15
  export class OpenApiController {
16
-
17
16
  @Inject()
18
17
  service: OpenApiService;
19
18
 
@@ -27,4 +26,4 @@ export class OpenApiController {
27
26
  async getYmlSpec(): Promise<string> {
28
27
  return stringify(await this.service.getSpec()); // Force output to be simple
29
28
  }
30
- }
29
+ }
package/src/generate.ts CHANGED
@@ -1,14 +1,31 @@
1
1
  import type {
2
- SchemaObject, SchemasObject, ParameterObject, OperationObject,
3
- RequestBodyObject, TagObject, PathsObject, PathItemObject
2
+ OperationObject,
3
+ ParameterObject,
4
+ PathItemObject,
5
+ PathsObject,
6
+ RequestBodyObject,
7
+ SchemaObject,
8
+ SchemasObject,
9
+ TagObject
4
10
  } from 'openapi3-ts/oas31';
5
11
 
6
- import { type EndpointConfig, type ControllerConfig, type EndpointParameterConfig, type ControllerVisitor, HTTP_METHODS } from '@travetto/web';
7
- import { RuntimeError, castTo, type Class, describeFunction } from '@travetto/runtime';
12
+ import { type Class, castTo, describeFunction, RuntimeError } from '@travetto/runtime';
8
13
  import {
9
- type SchemaFieldConfig, type SchemaClassConfig, SchemaNameResolver,
10
- type SchemaInputConfig, SchemaRegistryIndex, type SchemaBasicType, type SchemaParameterConfig
14
+ type SchemaBasicType,
15
+ type SchemaClassConfig,
16
+ type SchemaFieldConfig,
17
+ type SchemaInputConfig,
18
+ SchemaNameResolver,
19
+ type SchemaParameterConfig,
20
+ SchemaRegistryIndex
11
21
  } from '@travetto/schema';
22
+ import {
23
+ type ControllerConfig,
24
+ type ControllerVisitor,
25
+ type EndpointConfig,
26
+ type EndpointParameterConfig,
27
+ HTTP_METHODS
28
+ } from '@travetto/web';
12
29
 
13
30
  import type { ApiSpecConfig } from './config.ts';
14
31
 
@@ -45,7 +62,12 @@ export class OpenapiVisitor implements ControllerVisitor<GeneratedSpec> {
45
62
  /**
46
63
  * Convert schema to a set of dotted parameters
47
64
  */
48
- #schemaToDotParams(location: 'query' | 'header', input: SchemaInputConfig, prefix: string = '', rootField: SchemaInputConfig = input): ParameterObject[] {
65
+ #schemaToDotParams(
66
+ location: 'query' | 'header',
67
+ input: SchemaInputConfig,
68
+ prefix: string = '',
69
+ rootField: SchemaInputConfig = input
70
+ ): ParameterObject[] {
49
71
  if (!SchemaRegistryIndex.has(input.type)) {
50
72
  throw new RuntimeError(`Unknown class, not registered as a schema: ${input.type.Ⲑid}`);
51
73
  }
@@ -55,17 +77,19 @@ export class OpenapiVisitor implements ControllerVisitor<GeneratedSpec> {
55
77
  for (const sub of Object.values(fields)) {
56
78
  const name = sub.name;
57
79
  if (SchemaRegistryIndex.has(sub.type)) {
58
- const suffix = (sub.array) ? '[]' : '';
80
+ const suffix = sub.array ? '[]' : '';
59
81
  params.push(...this.#schemaToDotParams(location, sub, prefix ? `${prefix}.${name}${suffix}` : `${name}${suffix}.`, rootField));
60
82
  } else {
61
83
  params.push({
62
84
  name: `${prefix}${name}`,
63
85
  description: sub.description,
64
- schema: sub.array ? {
65
- type: 'array',
66
- ...this.#getType(sub)
67
- } : this.#getType(sub),
68
- required: (rootField?.required?.active !== false && sub.required?.active !== false),
86
+ schema: sub.array
87
+ ? {
88
+ type: 'array',
89
+ ...this.#getType(sub)
90
+ }
91
+ : this.#getType(sub),
92
+ required: rootField?.required?.active !== false && sub.required?.active !== false,
69
93
  in: location
70
94
  });
71
95
  }
@@ -77,7 +101,7 @@ export class OpenapiVisitor implements ControllerVisitor<GeneratedSpec> {
77
101
  * Get the type for a given class
78
102
  */
79
103
  #getType(inputOrClass: SchemaInputConfig | Class): Record<string, unknown> {
80
- let field: { type: Class, precision?: [number, number | undefined] };
104
+ let field: { type: Class; precision?: [number, number | undefined] };
81
105
  if (!isInputConfig(inputOrClass)) {
82
106
  field = { type: inputOrClass };
83
107
  } else {
@@ -92,7 +116,9 @@ export class OpenapiVisitor implements ControllerVisitor<GeneratedSpec> {
92
116
  out.$ref = `${DEFINITION}/${id}`;
93
117
  } else {
94
118
  switch (field.type) {
95
- case String: out.type = 'string'; break;
119
+ case String:
120
+ out.type = 'string';
121
+ break;
96
122
  case castTo(BigInt):
97
123
  out.type = 'integer';
98
124
  out.format = 'int64';
@@ -110,7 +136,9 @@ export class OpenapiVisitor implements ControllerVisitor<GeneratedSpec> {
110
136
  out.format = 'date-time';
111
137
  out.type = 'string';
112
138
  break;
113
- case Boolean: out.type = 'boolean'; break;
139
+ case Boolean:
140
+ out.type = 'boolean';
141
+ break;
114
142
  default:
115
143
  out.type = 'object';
116
144
  break;
@@ -244,7 +272,7 @@ export class OpenapiVisitor implements ControllerVisitor<GeneratedSpec> {
244
272
  } else if (body.binary) {
245
273
  return {
246
274
  content: {
247
- [mime ?? 'application/octet-stream']: { schema: { type: 'string', format: 'binary' } },
275
+ [mime ?? 'application/octet-stream']: { schema: { type: 'string', format: 'binary' } }
248
276
  },
249
277
  description: 'Raw binary data'
250
278
  };
@@ -266,11 +294,11 @@ export class OpenapiVisitor implements ControllerVisitor<GeneratedSpec> {
266
294
  /**
267
295
  * Process endpoint parameter
268
296
  */
269
- #processEndpointParam(endpoint: EndpointConfig, param: EndpointParameterConfig, input: SchemaParameterConfig): (
270
- { requestBody: RequestBodyObject } |
271
- { parameters: ParameterObject[] } |
272
- undefined
273
- ) {
297
+ #processEndpointParam(
298
+ endpoint: EndpointConfig,
299
+ param: EndpointParameterConfig,
300
+ input: SchemaParameterConfig
301
+ ): { requestBody: RequestBodyObject } | { parameters: ParameterObject[] } | undefined {
274
302
  const complex = input.type && SchemaRegistryIndex.has(input.type);
275
303
 
276
304
  if (param.location) {
@@ -360,17 +388,11 @@ export class OpenapiVisitor implements ControllerVisitor<GeneratedSpec> {
360
388
  paths: Object.fromEntries(
361
389
  Object.entries(this.#paths)
362
390
  .toSorted(([a], [b]) => a.localeCompare(b))
363
- .map(([key, value]) => [key, Object.fromEntries(
364
- Object.entries(value)
365
- .toSorted(([a], [b]) => a.localeCompare(b))
366
- )])
391
+ .map(([key, value]) => [key, Object.fromEntries(Object.entries(value).toSorted(([a], [b]) => a.localeCompare(b)))])
367
392
  ),
368
393
  components: {
369
- schemas: Object.fromEntries(
370
- Object.entries(this.#schemas)
371
- .toSorted(([a], [b]) => a.localeCompare(b))
372
- )
394
+ schemas: Object.fromEntries(Object.entries(this.#schemas).toSorted(([a], [b]) => a.localeCompare(b)))
373
395
  }
374
396
  };
375
397
  }
376
- }
398
+ }
package/src/service.ts CHANGED
@@ -1,10 +1,11 @@
1
1
  import { createReadStream, existsSync } from 'node:fs';
2
+
2
3
  import type { OpenAPIObject } from 'openapi3-ts/oas31';
3
4
  import { stringify } from 'yaml';
4
5
 
6
+ import { Inject, Injectable, PostConstruct } from '@travetto/di';
5
7
  import { ManifestFileUtil } from '@travetto/manifest';
6
8
  import { BinaryMetadataUtil, JSONUtil } from '@travetto/runtime';
7
- import { Injectable, Inject, PostConstruct } from '@travetto/di';
8
9
  import { ControllerVisitUtil, type WebConfig } from '@travetto/web';
9
10
 
10
11
  import type { ApiHostConfig, ApiInfoConfig, ApiSpecConfig } from './config.ts';
@@ -15,7 +16,6 @@ import { OpenapiVisitor } from './generate.ts';
15
16
  */
16
17
  @Injectable()
17
18
  export class OpenApiService {
18
-
19
19
  @Inject()
20
20
  apiHostConfig: ApiHostConfig;
21
21
 
@@ -60,7 +60,7 @@ export class OpenApiService {
60
60
  this.#spec = {
61
61
  ...this.apiHostConfig,
62
62
  info: { ...this.apiInfoConfig },
63
- ...await ControllerVisitUtil.visit(new OpenapiVisitor(this.apiSpecConfig))
63
+ ...(await ControllerVisitUtil.visit(new OpenapiVisitor(this.apiSpecConfig)))
64
64
  };
65
65
  }
66
66
  return this.#spec!;
@@ -75,9 +75,7 @@ export class OpenApiService {
75
75
 
76
76
  const spec = await this.getSpec();
77
77
 
78
- const output = this.apiSpecConfig.output.endsWith('.json') ?
79
- JSONUtil.toUTF8Pretty(spec) :
80
- stringify(spec);
78
+ const output = this.apiSpecConfig.output.endsWith('.json') ? JSONUtil.toUTF8Pretty(spec) : stringify(spec);
81
79
 
82
80
  if (existsSync(this.apiSpecConfig.output)) {
83
81
  const existing = await BinaryMetadataUtil.hash(createReadStream(this.apiSpecConfig.output));
@@ -94,4 +92,4 @@ export class OpenApiService {
94
92
  console.error('Unable to persist openapi spec', error);
95
93
  }
96
94
  }
97
- }
95
+ }
@@ -1,18 +1,17 @@
1
- import fs from 'node:fs/promises';
2
1
  import { spawn } from 'node:child_process';
2
+ import fs from 'node:fs/promises';
3
3
  import path from 'node:path';
4
4
 
5
- import { JSONUtil, ExecUtil, Runtime } from '@travetto/runtime';
6
5
  import { cliTpl } from '@travetto/cli';
6
+ import { ExecUtil, JSONUtil, Runtime } from '@travetto/runtime';
7
7
 
8
8
  /**
9
9
  * Help utility for openapi client command
10
10
  */
11
11
  export class OpenApiClientHelp {
12
-
13
12
  static async getListOfFormats(dockerImage: string): Promise<string[]> {
14
13
  const formatCache = Runtime.toolPath('openapi-formats.json');
15
- if (!await fs.stat(formatCache, { throwIfNoEntry: false })) {
14
+ if (!(await fs.stat(formatCache, { throwIfNoEntry: false }))) {
16
15
  const { stdout } = await ExecUtil.getResult(spawn('docker', ['run', '--rm', dockerImage, 'list']));
17
16
  const lines = stdout
18
17
  .split('DOCUMENTATION')[0]
@@ -22,7 +21,7 @@ export class OpenApiClientHelp {
22
21
  .map(line => line.replace(/^\s+-\s+/, '').trim());
23
22
 
24
23
  await fs.mkdir(path.dirname(formatCache), { recursive: true });
25
- await fs.writeFile(formatCache, JSONUtil.toUTF8([...lines.toSorted(),]));
24
+ await fs.writeFile(formatCache, JSONUtil.toUTF8([...lines.toSorted()]));
26
25
  }
27
26
  return await fs.readFile(formatCache).then(JSONUtil.fromBinaryArray<string[]>);
28
27
  }
@@ -40,4 +39,4 @@ export class OpenApiClientHelp {
40
39
  }
41
40
  return help;
42
41
  }
43
- }
42
+ }
@@ -1,27 +1,30 @@
1
- import path from 'node:path';
2
1
  import cp from 'node:child_process';
2
+ import path from 'node:path';
3
3
 
4
- import { type CliCommandShape, CliCommand, CliFlag } from '@travetto/cli';
4
+ import { CliCommand, type CliCommandShape, CliFlag } from '@travetto/cli';
5
5
  import { ExecUtil } from '@travetto/runtime';
6
6
 
7
7
  import { OpenApiClientHelp } from './bin/help.ts';
8
8
 
9
9
  /**
10
- * CLI for generating the cli client
10
+ * Generate API clients from an OpenAPI specification using the generator image.
11
+ *
12
+ * This command wraps OpenAPI Generator in Docker and writes generated client code
13
+ * into the configured output folder.
11
14
  */
12
15
  @CliCommand()
13
16
  export class OpenApiClientCommand implements CliCommandShape {
14
- /** Show Extended Help */
17
+ /** Show expanded generator help for all available formats/options. */
15
18
  @CliFlag({ short: '-x' })
16
19
  extendedHelp: boolean = false;
17
- /** Additional Properties */
20
+ /** Additional generator properties passed as comma-separated key/value pairs. */
18
21
  @CliFlag({ short: '-a', full: '--additional-properties' })
19
22
  properties: string[] = [];
20
- /** Input file */
23
+ /** Input OpenAPI document path. */
21
24
  input = './openapi.yml';
22
- /** Output folder */
25
+ /** Output directory for generated client sources. */
23
26
  output = './api-client';
24
- /** Docker Image to user */
27
+ /** Docker image used to run OpenAPI Generator. */
25
28
  dockerImage = 'openapitools/openapi-generator-cli:latest';
26
29
 
27
30
  async help(): Promise<string[]> {
@@ -32,25 +35,35 @@ export class OpenApiClientCommand implements CliCommandShape {
32
35
  this.output = path.resolve(this.output);
33
36
  this.input = path.resolve(this.input);
34
37
 
35
- const subProcess = cp.spawn('docker', [
36
- 'run',
37
- '--rm',
38
- '-i',
39
- '-v', `${this.output}:/workspace`,
40
- '-v', `${path.dirname(this.input)}:/input`,
41
- '--user', `${process.geteuid?.() ?? 0}:${process.getgid?.() ?? 0}`,
42
- this.dockerImage,
43
- // Parameters
44
- 'generate',
45
- '--skip-validate-spec',
46
- '--remove-operation-id-prefix',
47
- '-g', format,
48
- '-o', '/workspace',
49
- '-i', `/input/${path.basename(this.input)}`,
50
- ...(this.properties.length ? ['--additional-properties', this.properties.join(',')] : [])
51
- ], {
52
- stdio: 'inherit'
53
- });
38
+ const subProcess = cp.spawn(
39
+ 'docker',
40
+ [
41
+ 'run',
42
+ '--rm',
43
+ '-i',
44
+ '-v',
45
+ `${this.output}:/workspace`,
46
+ '-v',
47
+ `${path.dirname(this.input)}:/input`,
48
+ '--user',
49
+ `${process.geteuid?.() ?? 0}:${process.getgid?.() ?? 0}`,
50
+ this.dockerImage,
51
+ // Parameters
52
+ 'generate',
53
+ '--skip-validate-spec',
54
+ '--remove-operation-id-prefix',
55
+ '-g',
56
+ format,
57
+ '-o',
58
+ '/workspace',
59
+ '-i',
60
+ `/input/${path.basename(this.input)}`,
61
+ ...(this.properties.length ? ['--additional-properties', this.properties.join(',')] : [])
62
+ ],
63
+ {
64
+ stdio: 'inherit'
65
+ }
66
+ );
54
67
 
55
68
  const result = await ExecUtil.getResult(subProcess);
56
69
 
@@ -58,4 +71,4 @@ export class OpenApiClientCommand implements CliCommandShape {
58
71
  process.exitCode = 1;
59
72
  }
60
73
  }
61
- }
74
+ }
@@ -1,17 +1,19 @@
1
1
  import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
 
4
- import { type CliCommandShape, CliCommand, CliModuleFlag } from '@travetto/cli';
5
- import { JSONUtil, Env } from '@travetto/runtime';
6
- import { Registry } from '@travetto/registry';
4
+ import { CliCommand, type CliCommandShape, CliModuleFlag } from '@travetto/cli';
7
5
  import { DependencyRegistryIndex } from '@travetto/di';
6
+ import { Registry } from '@travetto/registry';
7
+ import { Env, JSONUtil } from '@travetto/runtime';
8
8
 
9
9
  /**
10
- * CLI for outputting the open api spec to a local file
10
+ * Generate the OpenAPI specification for the selected module.
11
+ *
12
+ * The resulting JSON can be written to stdout or to a file path for use in
13
+ * downstream tooling, CI publishing, and client generation pipelines.
11
14
  */
12
15
  @CliCommand()
13
16
  export class OpenApiSpecCommand implements CliCommandShape {
14
-
15
17
  /** Output files */
16
18
  output?: string;
17
19
 
@@ -38,4 +40,4 @@ export class OpenApiSpecCommand implements CliCommandShape {
38
40
  await fs.writeFile(this.output, text, 'utf8');
39
41
  }
40
42
  }
41
- }
43
+ }