@travetto/openapi 7.1.4 → 8.0.0-alpha.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/openapi",
3
- "version": "7.1.4",
3
+ "version": "8.0.0-alpha.0",
4
4
  "type": "module",
5
5
  "description": "OpenAPI integration support for the Travetto framework",
6
6
  "keywords": [
@@ -27,14 +27,14 @@
27
27
  "directory": "module/openapi"
28
28
  },
29
29
  "dependencies": {
30
- "@travetto/config": "^7.1.4",
31
- "@travetto/schema": "^7.1.4",
32
- "@travetto/web": "^7.1.4",
30
+ "@travetto/config": "^8.0.0-alpha.0",
31
+ "@travetto/schema": "^8.0.0-alpha.0",
32
+ "@travetto/web": "^8.0.0-alpha.0",
33
33
  "openapi3-ts": "^4.5.0",
34
34
  "yaml": "^2.8.2"
35
35
  },
36
36
  "peerDependencies": {
37
- "@travetto/cli": "^7.1.4"
37
+ "@travetto/cli": "^8.0.0-alpha.0"
38
38
  },
39
39
  "peerDependenciesMeta": {
40
40
  "@travetto/cli": {
package/src/generate.ts CHANGED
@@ -1,11 +1,10 @@
1
- import { Readable } from 'node:stream';
2
1
  import type {
3
2
  SchemaObject, SchemasObject, ParameterObject, OperationObject,
4
3
  RequestBodyObject, TagObject, PathsObject, PathItemObject
5
4
  } from 'openapi3-ts/oas31';
6
5
 
7
6
  import { type EndpointConfig, type ControllerConfig, type EndpointParameterConfig, type ControllerVisitor, HTTP_METHODS } from '@travetto/web';
8
- import { AppError, type Class, describeFunction } from '@travetto/runtime';
7
+ import { RuntimeError, castTo, type Class, describeFunction } from '@travetto/runtime';
9
8
  import {
10
9
  type SchemaFieldConfig, type SchemaClassConfig, SchemaNameResolver,
11
10
  type SchemaInputConfig, SchemaRegistryIndex, type SchemaBasicType, type SchemaParameterConfig
@@ -48,7 +47,7 @@ export class OpenapiVisitor implements ControllerVisitor<GeneratedSpec> {
48
47
  */
49
48
  #schemaToDotParams(location: 'query' | 'header', input: SchemaInputConfig, prefix: string = '', rootField: SchemaInputConfig = input): ParameterObject[] {
50
49
  if (!SchemaRegistryIndex.has(input.type)) {
51
- throw new AppError(`Unknown class, not registered as a schema: ${input.type.Ⲑid}`);
50
+ throw new RuntimeError(`Unknown class, not registered as a schema: ${input.type.Ⲑid}`);
52
51
  }
53
52
 
54
53
  const fields = SchemaRegistryIndex.get(input.type).getFields(input.view);
@@ -78,7 +77,7 @@ export class OpenapiVisitor implements ControllerVisitor<GeneratedSpec> {
78
77
  * Get the type for a given class
79
78
  */
80
79
  #getType(inputOrClass: SchemaInputConfig | Class): Record<string, unknown> {
81
- let field: { type: Class<unknown>, precision?: [number, number | undefined] };
80
+ let field: { type: Class, precision?: [number, number | undefined] };
82
81
  if (!isInputConfig(inputOrClass)) {
83
82
  field = { type: inputOrClass };
84
83
  } else {
@@ -94,6 +93,10 @@ export class OpenapiVisitor implements ControllerVisitor<GeneratedSpec> {
94
93
  } else {
95
94
  switch (field.type) {
96
95
  case String: out.type = 'string'; break;
96
+ case castTo(BigInt):
97
+ out.type = 'integer';
98
+ out.format = 'int64';
99
+ break;
97
100
  case Number: {
98
101
  if (field.precision) {
99
102
  const [, decimals] = field.precision;
@@ -150,10 +153,10 @@ export class OpenapiVisitor implements ControllerVisitor<GeneratedSpec> {
150
153
  config.minLength = input.minlength.limit;
151
154
  }
152
155
  if (input.min) {
153
- config.minimum = typeof input.min.limit === 'number' ? input.min.limit : input.min.limit.getTime();
156
+ config.minimum = input.min.limit instanceof Date ? input.min.limit.getTime() : castTo(input.min.limit);
154
157
  }
155
158
  if (input.max) {
156
- config.maximum = typeof input.max.limit === 'number' ? input.max.limit : input.max.limit.getTime();
159
+ config.maximum = input.max.limit instanceof Date ? input.max.limit.getTime() : castTo(input.max.limit);
157
160
  }
158
161
  if (input.enum) {
159
162
  config.enum = input.enum.values;
@@ -238,7 +241,7 @@ export class OpenapiVisitor implements ControllerVisitor<GeneratedSpec> {
238
241
  #getEndpointBody(body?: SchemaBasicType, mime?: string | null): RequestBodyObject {
239
242
  if (!body || body.type === undefined) {
240
243
  return { content: {}, description: '' };
241
- } else if (body.type === Readable || body.type === Buffer) {
244
+ } else if (body.binary) {
242
245
  return {
243
246
  content: {
244
247
  [mime ?? 'application/octet-stream']: { schema: { type: 'string', format: 'binary' } },
package/src/service.ts CHANGED
@@ -1,7 +1,9 @@
1
+ import { createReadStream, existsSync } from 'node:fs';
1
2
  import type { OpenAPIObject } from 'openapi3-ts/oas31';
2
3
  import { stringify } from 'yaml';
3
4
 
4
- import { BinaryUtil } from '@travetto/runtime';
5
+ import { ManifestFileUtil } from '@travetto/manifest';
6
+ import { BinaryMetadataUtil, JSONUtil } from '@travetto/runtime';
5
7
  import { Injectable, Inject } from '@travetto/di';
6
8
  import { ControllerVisitUtil, type WebConfig } from '@travetto/web';
7
9
 
@@ -73,10 +75,20 @@ export class OpenApiService {
73
75
  const spec = await this.getSpec();
74
76
 
75
77
  const output = this.apiSpecConfig.output.endsWith('.json') ?
76
- JSON.stringify(spec, undefined, 2) :
78
+ JSONUtil.toUTF8Pretty(spec) :
77
79
  stringify(spec);
78
80
 
79
- await BinaryUtil.bufferedFileWrite(this.apiSpecConfig.output, output, true);
81
+ if (existsSync(this.apiSpecConfig.output)) {
82
+ const existing = await BinaryMetadataUtil.hash(createReadStream(this.apiSpecConfig.output));
83
+ const toWrite = BinaryMetadataUtil.hash(output);
84
+
85
+ if (existing === toWrite) {
86
+ console.debug('OpenAPI spec unchanged, skipping write');
87
+ return;
88
+ }
89
+ }
90
+
91
+ await ManifestFileUtil.bufferedFileWrite(this.apiSpecConfig.output, output);
80
92
  } catch (error) {
81
93
  console.error('Unable to persist openapi spec', error);
82
94
  }
@@ -2,7 +2,7 @@ import fs from 'node:fs/promises';
2
2
  import { spawn } from 'node:child_process';
3
3
  import path from 'node:path';
4
4
 
5
- import { ExecUtil, JSONUtil, Runtime } from '@travetto/runtime';
5
+ import { JSONUtil, ExecUtil, Runtime } from '@travetto/runtime';
6
6
  import { cliTpl } from '@travetto/cli';
7
7
 
8
8
  /**
@@ -22,10 +22,9 @@ export class OpenApiClientHelp {
22
22
  .map(line => line.replace(/^\s+-\s+/, '').trim());
23
23
 
24
24
  await fs.mkdir(path.dirname(formatCache), { recursive: true });
25
- await fs.writeFile(formatCache, JSON.stringify([...lines.toSorted(),]));
25
+ await fs.writeFile(formatCache, JSONUtil.toUTF8([...lines.toSorted(),]));
26
26
  }
27
- const list: string[] = await JSONUtil.readFile(formatCache);
28
- return list;
27
+ return await fs.readFile(formatCache).then(JSONUtil.fromBinaryArray<string[]>);
29
28
  }
30
29
 
31
30
  static async help(dockerImage: string, extendedHelp: boolean): Promise<string[]> {
@@ -2,7 +2,7 @@ import fs from 'node:fs/promises';
2
2
  import path from 'node:path';
3
3
 
4
4
  import { type CliCommandShape, CliCommand } from '@travetto/cli';
5
- import { Env } from '@travetto/runtime';
5
+ import { JSONUtil, Env } from '@travetto/runtime';
6
6
  import { Registry } from '@travetto/registry';
7
7
  import { DependencyRegistryIndex } from '@travetto/di';
8
8
 
@@ -26,12 +26,13 @@ export class OpenApiSpecCommand implements CliCommandShape {
26
26
 
27
27
  const instance = await DependencyRegistryIndex.getInstance(OpenApiService);
28
28
  const result = await instance.getSpec();
29
+ const text = JSONUtil.toUTF8Pretty(result);
29
30
 
30
31
  if (this.output === '-' || !this.output) {
31
- console.log!(JSON.stringify(result, null, 2));
32
+ console.log!(text);
32
33
  } else {
33
34
  await fs.mkdir(path.dirname(this.output), { recursive: true });
34
- await fs.writeFile(this.output, JSON.stringify(result, null, 2), 'utf8');
35
+ await fs.writeFile(this.output, text, 'utf8');
35
36
  }
36
37
  }
37
38
  }