@goast/core 0.5.2 → 0.6.0-beta.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.
Files changed (52) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +67 -67
  3. package/esm/src/parse/parser.js +2 -1
  4. package/package.json +1 -1
  5. package/script/src/parse/parser.js +2 -1
  6. package/src/_dnt.shims.ts +0 -69
  7. package/src/mod.ts +0 -35
  8. package/src/src/ast/node.ts +0 -46
  9. package/src/src/codegen/config.ts +0 -41
  10. package/src/src/codegen/endpoints-generator.ts +0 -72
  11. package/src/src/codegen/generator.ts +0 -192
  12. package/src/src/codegen/internal-utils.ts +0 -6
  13. package/src/src/codegen/schemas-generator.ts +0 -87
  14. package/src/src/codegen/services-generator.ts +0 -77
  15. package/src/src/codegen/types.ts +0 -37
  16. package/src/src/collect/collector.ts +0 -142
  17. package/src/src/collect/helpers.ts +0 -36
  18. package/src/src/collect/types.ts +0 -21
  19. package/src/src/internal-utils.ts +0 -5
  20. package/src/src/parse/deref-proxy.ts +0 -70
  21. package/src/src/parse/openapi-types.ts +0 -949
  22. package/src/src/parse/parser.ts +0 -206
  23. package/src/src/parse/types.ts +0 -28
  24. package/src/src/transform/api-types.ts +0 -245
  25. package/src/src/transform/helpers.ts +0 -218
  26. package/src/src/transform/transform-document.ts +0 -35
  27. package/src/src/transform/transform-endpoint.ts +0 -331
  28. package/src/src/transform/transform-schema.ts +0 -217
  29. package/src/src/transform/transformer.ts +0 -53
  30. package/src/src/transform/types.ts +0 -52
  31. package/src/src/utils/action-provider.ts +0 -58
  32. package/src/src/utils/api-data.utils.ts +0 -48
  33. package/src/src/utils/array.utils.ts +0 -7
  34. package/src/src/utils/asset-manager.ts +0 -51
  35. package/src/src/utils/class.utils.ts +0 -40
  36. package/src/src/utils/common.utils.ts +0 -86
  37. package/src/src/utils/condition.ts +0 -7
  38. package/src/src/utils/endpoint.utils.ts +0 -10
  39. package/src/src/utils/error.utils.ts +0 -3
  40. package/src/src/utils/factory.ts +0 -42
  41. package/src/src/utils/file-system.utils.ts +0 -61
  42. package/src/src/utils/generator.utils.ts +0 -6
  43. package/src/src/utils/object.utils.ts +0 -32
  44. package/src/src/utils/schema.utils.ts +0 -148
  45. package/src/src/utils/source-builder.ts +0 -597
  46. package/src/src/utils/string-builder/options.ts +0 -19
  47. package/src/src/utils/string-builder/string-builder.ts +0 -165
  48. package/src/src/utils/string-builder/utils.ts +0 -69
  49. package/src/src/utils/string.utils.ts +0 -369
  50. package/src/src/utils/string.utils.types.ts +0 -179
  51. package/src/src/utils/type.utils.ts +0 -93
  52. package/src/src/utils/yaml-info.ts +0 -48
@@ -1,206 +0,0 @@
1
- import * as dntShim from "../../_dnt.shims.js";
2
- import path from 'node:path';
3
- import { cwd } from 'node:process';
4
-
5
-
6
- import fs from 'fs-extra';
7
-
8
- import { collectOpenApi } from '../collect/collector.js';
9
- import type { ApiData } from '../transform/api-types.js';
10
- import { transformOpenApi } from '../transform/transformer.js';
11
- import { isNullish } from '../utils/common.utils.js';
12
- import { getLineInfo, parseYamlWithInfo } from '../utils/yaml-info.js';
13
- import { createDerefProxy } from './deref-proxy.js';
14
- import type { OpenApiDocument, OpenApiObject, OpenApiReference, OpenApiSchema } from './openapi-types.js';
15
- import { defaultOpenApiParserOptions, type Deref, type OpenApiParserOptions } from './types.js';
16
-
17
- type LoadedDocument = {
18
- file: string;
19
- document: OpenApiDocument;
20
- dereferencedDocument: Deref<OpenApiDocument>;
21
- dereferencedComponents: Map<string, Deref<OpenApiObject<string>>>;
22
- };
23
-
24
- export class OpenApiParser {
25
- private readonly _loadedDocuments = new Map<string, LoadedDocument>();
26
- private readonly _config: OpenApiParserOptions;
27
-
28
- constructor(config?: Partial<OpenApiParserOptions>) {
29
- this._config = { ...defaultOpenApiParserOptions, ...config };
30
- }
31
-
32
- public async parseApisAndTransform(...fileNames: (string | string[])[]): Promise<ApiData> {
33
- const flatFileNames = ([] as string[]).concat(...fileNames);
34
- const apis = await Promise.all(flatFileNames.map((fileName) => this.parseApi(fileName)));
35
-
36
- const transformed = this.transformApis(apis);
37
-
38
- return transformed;
39
- }
40
-
41
- public async parseApi(fileName: string): Promise<Deref<OpenApiDocument>> {
42
- const absoluteFilePath = this.isUrl(fileName) ? fileName : path.resolve(cwd(), fileName);
43
- const doc = await this.loadDocument(absoluteFilePath);
44
- return await this.dereference(absoluteFilePath, [], doc.document);
45
- }
46
-
47
- public transformApis(apis: Deref<OpenApiDocument>[]): ApiData {
48
- const collectedData = collectOpenApi(apis);
49
- const transformedData = transformOpenApi(collectedData, this._config);
50
- return transformedData;
51
- }
52
-
53
- private async dereference<T extends OpenApiObject<string>>(
54
- file: string,
55
- path: unknown[],
56
- value: T,
57
- ): Promise<Deref<T>> {
58
- const openApiPath = `/${path.join('/')}`;
59
- let existingDocument = this._loadedDocuments.get(file);
60
- if (existingDocument) {
61
- const existingComponent = existingDocument.dereferencedComponents.get(openApiPath);
62
- if (existingComponent) {
63
- return existingComponent as Deref<T>;
64
- }
65
- } else {
66
- existingDocument = await this.loadDocument(file);
67
- }
68
-
69
- const result = createDerefProxy(value, {
70
- file,
71
- pos: getLineInfo(existingDocument.document, path),
72
- path: openApiPath,
73
- document: existingDocument.dereferencedDocument,
74
- originalComponent: value,
75
- });
76
- existingDocument.dereferencedComponents.set(openApiPath, result as Deref<OpenApiObject<string>>);
77
-
78
- if (value.$ref) {
79
- // deno-lint-ignore no-explicit-any
80
- result.$ref = await this.resolveReference<T>(file, value as OpenApiReference) as any;
81
- }
82
- for (const key of Object.keys(value) as (keyof T)[]) {
83
- if (key === '$ref') continue;
84
- const v = value[key];
85
- if (isNullish(v)) continue;
86
- if (typeof v === 'object') {
87
- if (Array.isArray(v)) {
88
- (result as Record<keyof T, unknown>)[key] = await Promise.all(
89
- v.map(async (v, index) => {
90
- if (v && typeof v === 'object') {
91
- return await this.dereference(file, [...path, key, index], v);
92
- } else {
93
- return v;
94
- }
95
- }),
96
- );
97
- } else {
98
- (result as Record<keyof T, unknown>)[key] = await this.dereference(
99
- file,
100
- [...path, key],
101
- v as Record<string, unknown>,
102
- );
103
- }
104
- }
105
- if (typeof v === 'string' && openApiPath.endsWith('/discriminator/mapping')) {
106
- (result as Record<keyof T, unknown>)[key] = await this.resolveReference<OpenApiSchema>(file, { $ref: v });
107
- }
108
- }
109
-
110
- return result;
111
- }
112
-
113
- private async resolveReference<T extends OpenApiObject<string>>(
114
- file: string,
115
- ref: OpenApiReference,
116
- ): Promise<Deref<T> | undefined> {
117
- const [refFile, refPath] = ref.$ref.split('#');
118
-
119
- let absoluteRefFile: string = refFile;
120
- if (!/\S/.test(refFile)) {
121
- absoluteRefFile = file;
122
- } else if (!this.isUrl(refFile)) {
123
- absoluteRefFile = path.resolve(path.dirname(file), refFile);
124
- }
125
-
126
- const doc = await this.loadDocument(absoluteRefFile);
127
-
128
- const value = refPath ? getDeepProperty(doc.document, refPath.replace(/[/]/g, '.')) : doc.document;
129
- if (value && typeof value === 'object') {
130
- return this.dereference(absoluteRefFile, refPath.split('/').filter(Boolean), value as T);
131
- }
132
-
133
- return undefined;
134
- }
135
-
136
- private async loadDocument(fileOrUrl: string): Promise<LoadedDocument> {
137
- const existingDocument = this._loadedDocuments.get(fileOrUrl);
138
- if (existingDocument) {
139
- return existingDocument;
140
- }
141
-
142
- const documentContent = await this.getDocumentContent(fileOrUrl);
143
- const parsedDocument = this.tryParseYamlOrJson(documentContent);
144
- if (parsedDocument === undefined) {
145
- throw new Error(`Unable to parse ${fileOrUrl}`);
146
- }
147
-
148
- const doc = (parsedDocument ?? {}) as OpenApiDocument;
149
- const deref = createDerefProxy(doc, {
150
- file: fileOrUrl,
151
- pos: { line: 0, col: 0 },
152
- path: '',
153
- originalComponent: doc,
154
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
155
- document: undefined!,
156
- });
157
- deref.$src.document = deref;
158
- const loadedDocument = {
159
- file: fileOrUrl,
160
- document: doc,
161
- dereferencedDocument: deref,
162
- dereferencedComponents: new Map(),
163
- };
164
- this._loadedDocuments.set(fileOrUrl, loadedDocument);
165
- return loadedDocument;
166
- }
167
-
168
- private async getDocumentContent(fileOrUrl: string): Promise<string> {
169
- if (this.isUrl(fileOrUrl)) {
170
- const res = await dntShim.fetch(fileOrUrl);
171
- if (!res.ok) {
172
- throw new Error(`Unable to download ${fileOrUrl}: ${res.statusText}`);
173
- }
174
- return await res.text();
175
- } else {
176
- return (await fs.readFile(fileOrUrl)).toString();
177
- }
178
- }
179
-
180
- private isUrl(fileOrUrl: string): boolean {
181
- return /^https?:\/\//i.test(fileOrUrl);
182
- }
183
-
184
- private tryParseYamlOrJson(yamlOrJson: string): unknown {
185
- try {
186
- return parseYamlWithInfo(yamlOrJson);
187
- } catch {
188
- return undefined;
189
- }
190
- }
191
- }
192
-
193
- function getDeepProperty(value: Record<string, unknown>, path: string): unknown {
194
- const parts = path.split('.');
195
- let result: unknown = value;
196
- for (const part of parts) {
197
- if (!/\S/.test(part)) continue;
198
- if (typeof result === 'object') {
199
- result = (result as Record<string, unknown>)[part];
200
- } else {
201
- return undefined;
202
- }
203
- }
204
-
205
- return result;
206
- }
@@ -1,28 +0,0 @@
1
- import { defaultOpenApiTransformerOptions, type OpenApiTransformerOptions } from '../transform/types.js';
2
- import type { OpenApiDiscriminator, OpenApiDocument, OpenApiReference, OpenApiSchema } from './openapi-types.js';
3
-
4
- export type DerefSource<T> = {
5
- file: string;
6
- pos: { line: number; col: number };
7
- path: string;
8
- document: Deref<OpenApiDocument>;
9
- originalComponent: T;
10
- };
11
-
12
- type _DerefDiscriminator = Omit<OpenApiDiscriminator, 'mapping'> & {
13
- mapping?: Record<string, Deref<OpenApiSchema>>;
14
- };
15
- type _Deref<T extends object> = T extends OpenApiDiscriminator ? _DerefDiscriminator
16
- :
17
- & {
18
- [K in keyof T as K extends keyof OpenApiReference ? never : K]: Deref<T[K]>;
19
- }
20
- & {
21
- $src: DerefSource<T>;
22
- $ref?: Deref<T>;
23
- };
24
- export type Deref<T> = T extends object ? (T extends (infer A)[] ? Deref<A>[] : _Deref<T>) : T;
25
-
26
- export type OpenApiParserOptions = OpenApiTransformerOptions;
27
-
28
- export const defaultOpenApiParserOptions = { ...defaultOpenApiTransformerOptions };
@@ -1,245 +0,0 @@
1
- import type {
2
- KnownOpenApiParameterTarget,
3
- OpenApiDocument,
4
- OpenApiHeader,
5
- OpenApiHttpMethod,
6
- OpenApiMediaType,
7
- OpenApiOperation,
8
- OpenApiParameter,
9
- OpenApiPathItem,
10
- OpenApiRequestBody,
11
- OpenApiResponse,
12
- OpenApiSchema,
13
- OpenApiTag,
14
- } from '../parse/openapi-types.js';
15
- import type { Deref, DerefSource } from '../parse/types.js';
16
- import type { OptionalProperties } from '../utils/type.utils.js';
17
-
18
- export type ApiData = {
19
- documents: Deref<OpenApiDocument>[];
20
- services: ApiService[];
21
- endpoints: ApiEndpoint[];
22
- schemas: ApiSchema[];
23
- };
24
-
25
- export type ApiComponentSource<T> = DerefSource<T> & {
26
- component: Deref<T>;
27
- };
28
-
29
- export type Transformed<T> = T extends OpenApiTag ? ApiService
30
- : T extends OpenApiPathItem ? ApiPath
31
- : T extends OpenApiParameter ? ApiParameter
32
- : T extends OpenApiRequestBody ? ApiRequestBody
33
- : T extends OpenApiResponse ? ApiResponse
34
- : T extends OpenApiMediaType ? ApiContent
35
- : T extends OpenApiHeader ? ApiHeader
36
- : T extends OpenApiSchema ? ApiSchema
37
- : undefined;
38
-
39
- export type ApiComponent<T> = {
40
- id: string;
41
- $src: ApiComponentSource<T>;
42
- $ref: Transformed<T> | undefined;
43
- };
44
-
45
- export type ApiMethod = OpenApiHttpMethod;
46
- export type ApiParameterTarget = KnownOpenApiParameterTarget;
47
-
48
- export type ApiServiceComponent = OptionalProperties<ApiComponent<OpenApiTag>, '$src'>;
49
- export type ApiService = ApiServiceComponent & {
50
- name: string;
51
- description: string | undefined;
52
- endpoints: ApiEndpoint[];
53
- };
54
-
55
- export type ApiPathComponent = ApiComponent<OpenApiPathItem>;
56
- export type ApiPath = ApiPathComponent & {
57
- summary: string | undefined;
58
- description: string | undefined;
59
- path: string;
60
- parameters: ApiParameter[];
61
- } & { [K in ApiMethod]: ApiEndpoint | undefined };
62
-
63
- export type ApiEndpointComponent = ApiComponent<OpenApiOperation>;
64
- export type ApiEndpoint = ApiEndpointComponent & {
65
- name: string;
66
- path: string;
67
- pathInfo: ApiPath;
68
- method: ApiMethod;
69
- summary: string | undefined;
70
- description: string | undefined;
71
- parameters: ApiParameter[];
72
- deprecated: boolean;
73
- requestBody: ApiRequestBody | undefined;
74
- responses: ApiResponse[];
75
- tags: string[];
76
- custom: Record<string, unknown>;
77
- };
78
-
79
- export type ApiParameterComponent = ApiComponent<OpenApiParameter>;
80
- export type ApiParameter = ApiParameterComponent & {
81
- name: string;
82
- target: ApiParameterTarget;
83
- description: string | undefined;
84
- required: boolean;
85
- deprecated: boolean;
86
- allowEmptyValue: boolean | undefined;
87
- style: string | undefined;
88
- explode: boolean | undefined;
89
- allowReserved: boolean | undefined;
90
- schema: ApiSchema | undefined;
91
- };
92
-
93
- export type ApiRequestBodyComponent = ApiComponent<OpenApiRequestBody>;
94
- export type ApiRequestBody = ApiRequestBodyComponent & {
95
- description: string | undefined;
96
- content: ApiContent[];
97
- required: boolean;
98
- };
99
-
100
- export type ApiContentComponent = ApiComponent<OpenApiMediaType>;
101
- export type ApiContent = ApiContentComponent & {
102
- type: string;
103
- schema: ApiSchema | undefined;
104
- };
105
-
106
- export type ApiExampleComponent = ApiComponent<OpenApiResponse>;
107
- export type ApiResponse = ApiExampleComponent & {
108
- statusCode: number | undefined;
109
- description: string | undefined;
110
- headers: ApiHeader[];
111
- contentOptions: ApiContent[];
112
- };
113
-
114
- export type ApiHeaderComponent = Omit<ApiComponent<OpenApiHeader> | ApiComponent<OpenApiSchema>, '$ref'> & {
115
- $ref: ApiHeader | undefined;
116
- };
117
- export type ApiHeader = ApiHeaderComponent & Omit<ApiParameter, 'target'>;
118
-
119
- export type ApiSchemaType = 'string' | 'number' | 'integer' | 'array' | 'boolean' | 'null' | 'object' | string;
120
- export type ApiSchemaKind =
121
- | 'oneOf'
122
- | 'multi-type'
123
- | 'string'
124
- | 'number'
125
- | 'integer'
126
- | 'array'
127
- | 'boolean'
128
- | 'null'
129
- | 'object'
130
- | 'combined'
131
- | 'unknown';
132
- export type ApiSchemaAccessibility = 'all' | 'readOnly' | 'writeOnly' | 'none';
133
- export type ApiSchemaProperty = {
134
- name: string;
135
- schema: ApiSchema;
136
- };
137
- export type ApiSchemaDiscriminator = {
138
- propertyName: string;
139
- mapping: Record<string, ApiSchema>;
140
- };
141
- export type ApiSchemaComponent = ApiComponent<OpenApiSchema>;
142
- export type ApiSchemaBase = ApiSchemaComponent & {
143
- name: string;
144
- isNameGenerated: boolean;
145
- description: string | undefined;
146
- deprecated: boolean;
147
- accessibility: ApiSchemaAccessibility;
148
- enum: unknown[] | undefined;
149
- const: unknown | undefined;
150
- default: unknown | undefined;
151
- example: unknown | undefined;
152
- nullable: boolean;
153
- required: Set<string>;
154
- custom: Record<string, unknown>;
155
- not: ApiSchema | undefined;
156
- discriminator: ApiSchemaDiscriminator | undefined;
157
- inheritedSchemas: (ApiSchema & { discriminator: NonNullable<ApiSchema['discriminator']> })[];
158
- };
159
- export type ApiSchema<T extends ApiSchemaKind = ApiSchemaKind> = ApiSchemaBase & ApiSchemaExtensions<T>;
160
- type AdditionalCombinedSchemaProperties = {
161
- allOf: ApiSchema[];
162
- anyOf: ApiSchema[];
163
- };
164
- type AdditionalArraySchemaProperties = {
165
- items: ApiSchema | undefined;
166
- minItems: number | undefined;
167
- maxItems: number | undefined;
168
- };
169
- type AdditionalObjectSchemaProperties = {
170
- properties: Map<string, ApiSchemaProperty>;
171
- additionalProperties: boolean | undefined | ApiSchema;
172
- } & AdditionalCombinedSchemaProperties;
173
- type AdditionalNumberSchemaProperties = {
174
- minimum: number | undefined;
175
- maximum: number | undefined;
176
- };
177
- type AdditionalStringSchemaProperties = {
178
- pattern: string | undefined;
179
- minLength: number | undefined;
180
- maxLength: number | undefined;
181
- };
182
- type AdditionalPrimitiveSchemaProperties = {
183
- format: string | undefined;
184
- };
185
- export type ApiSchemaExtensions<T extends ApiSchemaKind> = T extends 'oneOf' ? { kind: 'oneOf'; oneOf: ApiSchema[] }
186
- : T extends 'multi-type' ?
187
- & {
188
- kind: 'multi-type';
189
- type: ApiSchemaType[];
190
- }
191
- & AdditionalArraySchemaProperties
192
- & AdditionalObjectSchemaProperties
193
- & AdditionalNumberSchemaProperties
194
- & AdditionalStringSchemaProperties
195
- & AdditionalPrimitiveSchemaProperties
196
- : T extends 'string'
197
- ? { kind: 'string'; type: 'string' } & AdditionalStringSchemaProperties & AdditionalPrimitiveSchemaProperties
198
- : T extends 'boolean' ? { kind: 'boolean'; type: 'boolean' } & AdditionalPrimitiveSchemaProperties
199
- : T extends 'null' ? { kind: 'null'; type: 'null' }
200
- : T extends 'number' | 'integer'
201
- ? { kind: 'number' | 'integer'; type: T } & AdditionalNumberSchemaProperties & AdditionalPrimitiveSchemaProperties
202
- : T extends 'array' ? {
203
- kind: 'array';
204
- type: 'array';
205
- } & AdditionalArraySchemaProperties
206
- : T extends 'object' ? {
207
- kind: 'object';
208
- type: 'object';
209
- } & AdditionalObjectSchemaProperties
210
- : T extends 'combined' ? {
211
- kind: 'combined';
212
- } & AdditionalCombinedSchemaProperties
213
- : { kind: 'unknown' };
214
- export type CombinedLikeApiSchema = ApiSchemaBase & {
215
- kind: 'combined' | 'object' | 'multi-type';
216
- type?: 'object' | string[];
217
- } & AdditionalCombinedSchemaProperties;
218
- export type NumberLikeApiSchema =
219
- & ApiSchemaBase
220
- & {
221
- kind: 'number' | 'integer' | 'multi-type';
222
- type: 'number' | 'integer' | string[];
223
- }
224
- & AdditionalNumberSchemaProperties
225
- & AdditionalPrimitiveSchemaProperties;
226
- export type ArrayLikeApiSchema = ApiSchemaBase & {
227
- kind: 'array' | 'multi-type';
228
- type: 'array' | string[];
229
- } & AdditionalArraySchemaProperties;
230
- export type ObjectLikeApiSchema = ApiSchemaBase & {
231
- kind: 'object' | 'multi-type';
232
- type: 'object' | string[];
233
- } & AdditionalObjectSchemaProperties;
234
- export type StringLikeApiSchema =
235
- & ApiSchemaBase
236
- & {
237
- kind: 'string' | 'multi-type';
238
- type: 'string' | string[];
239
- }
240
- & AdditionalStringSchemaProperties
241
- & AdditionalPrimitiveSchemaProperties;
242
- export type PrimitiveLikeApiSchema = ApiSchemaBase & {
243
- kind: 'string' | 'number' | 'integer' | 'boolean' | 'null' | 'multi-type';
244
- type: 'string' | 'number' | 'integer' | 'boolean' | 'null' | string[];
245
- } & AdditionalPrimitiveSchemaProperties;
@@ -1,218 +0,0 @@
1
- import { parse as parsePath } from 'node:path';
2
- import { isOpenApiObjectProperty } from '../internal-utils.js';
3
- import type { OpenApiHttpMethod, OpenApiObject } from '../parse/openapi-types.js';
4
- import type { Deref, DerefSource } from '../parse/types.js';
5
- import { isNullish } from '../utils/common.utils.js';
6
- import { getDeepProperty } from '../utils/object.utils.js';
7
- import { getLineInfo } from '../utils/yaml-info.js';
8
- import type { ApiSchema, ApiSchemaAccessibility, ApiSchemaKind, ApiSchemaProperty } from './api-types.js';
9
- import type { OpenApiTransformerContext } from './types.js';
10
-
11
- export function determineSchemaKind<
12
- T extends {
13
- oneOf?: unknown;
14
- allOf?: unknown;
15
- anyOf?: unknown;
16
- type?: string | string[];
17
- properties?: Record<string, unknown>;
18
- additionalProperties?: unknown;
19
- },
20
- >(ctx: OpenApiTransformerContext, schema: T): ApiSchemaKind {
21
- if (schema.oneOf) {
22
- return 'oneOf';
23
- } else if (schema.type !== 'object' && (schema.allOf || schema.anyOf)) {
24
- const hasProperties = (schema.properties && Object.keys(schema.properties).length > 0) ||
25
- schema.additionalProperties;
26
- return hasProperties ? 'object' : 'combined';
27
- } else if (Array.isArray(schema.type)) {
28
- return 'multi-type';
29
- } else if (
30
- schema.type === 'object' ||
31
- schema.type === 'string' ||
32
- schema.type === 'boolean' ||
33
- schema.type === 'null' ||
34
- schema.type === 'number' ||
35
- schema.type === 'integer' ||
36
- schema.type === 'array'
37
- ) {
38
- return schema.type;
39
- }
40
-
41
- const treadAsObject = ctx.config.unknownTypeBehavior === 'always-object' ||
42
- (ctx.config.unknownTypeBehavior === 'object-if-properties' &&
43
- schema.properties &&
44
- Object.keys(schema.properties).length > 0);
45
-
46
- return treadAsObject ? 'object' : 'unknown';
47
- }
48
-
49
- export function determineSchemaName(
50
- schema: {
51
- title?: string;
52
- $src: DerefSource<unknown>;
53
- },
54
- id: string,
55
- ): { name: string; isGenerated: boolean } {
56
- if (schema.title) return { name: schema.title, isGenerated: false };
57
- if (!schema.$src) {
58
- console.log('woot?');
59
- }
60
-
61
- if (schema.$src.path === '/') return { name: parsePath(schema.$src.file).name, isGenerated: true };
62
-
63
- const schemaNameMatch = schema.$src.path.match(/(?<=\/components\/schemas\/|\/definitions\/)[^/]+$/i);
64
- if (schemaNameMatch) {
65
- return { name: schemaNameMatch[0], isGenerated: false };
66
- }
67
-
68
- const paramNameMatch = schema.$src.path.match(/\/components\/parameters\/([^/]+)\/schema$/i);
69
- if (paramNameMatch) {
70
- return { name: paramNameMatch[1], isGenerated: true };
71
- }
72
-
73
- const responseMatch = schema.$src.path.match(/\/paths\/(?<path>.+)\/(?<method>.+)\/responses\/(?<status>\d+)\//);
74
- if (responseMatch && responseMatch.groups) {
75
- const { path, method, status } = responseMatch.groups;
76
- const operation = schema.$src.document.paths?.[path]?.[method as OpenApiHttpMethod] ?? {};
77
- return {
78
- name: `${determineEndpointName({ method, path: `/${path}`, operation })}_${status}_Response`,
79
- isGenerated: true,
80
- };
81
- }
82
-
83
- const responseCompMatch = schema.$src.path.match(/\/components\/responses\/([^/]+)\/content\/.+\/schema/);
84
- if (responseCompMatch) {
85
- return {
86
- name: responseCompMatch[1].toLowerCase().endsWith('response')
87
- ? responseCompMatch[1]
88
- : responseCompMatch[1] + 'Response',
89
- isGenerated: true,
90
- };
91
- }
92
-
93
- const requestBodyMatch = schema.$src.path.match(/\/paths\/(?<path>.+)\/(?<method>.+)\/requestBody\//);
94
- if (requestBodyMatch && requestBodyMatch.groups) {
95
- const { path, method } = requestBodyMatch.groups;
96
- const operation = schema.$src.document.paths?.[path]?.[method as OpenApiHttpMethod] ?? {};
97
- return { name: `${determineEndpointName({ method, path: `/${path}`, operation })}_Request`, isGenerated: true };
98
- }
99
-
100
- const parentSchemaMatch = schema.$src.path.match(/(.*)\/properties\/([^/]*)(\/additionalProperties)?$/);
101
- if (parentSchemaMatch) {
102
- const parentSchema = getDeepProperty(schema.$src.document, parentSchemaMatch[1].split('/').filter(Boolean));
103
- const parentSchemaName = determineSchemaName(
104
- {
105
- title: typeof parentSchema === 'object' && parentSchema !== null && 'title' in parentSchema
106
- ? String(parentSchema.title)
107
- : undefined,
108
- $src: {
109
- file: schema.$src.file,
110
- pos: getLineInfo(schema.$src.document, parentSchemaMatch[1].split('/').filter(Boolean)),
111
- path: parentSchemaMatch[1],
112
- document: schema.$src.document,
113
- originalComponent: parentSchema,
114
- },
115
- },
116
- id,
117
- );
118
- if (!parentSchemaName.isGenerated) {
119
- return { name: `${parentSchemaName.name}_${parentSchemaMatch[2]}`, isGenerated: true };
120
- }
121
- }
122
-
123
- return { name: id, isGenerated: true };
124
- }
125
-
126
- export function determineSchemaAccessibility(schema: {
127
- readOnly?: boolean;
128
- writeOnly?: boolean;
129
- }): ApiSchemaAccessibility {
130
- if (schema.readOnly === true) {
131
- return schema.writeOnly === true ? 'none' : 'readOnly';
132
- } else {
133
- return schema.writeOnly === true ? 'writeOnly' : 'all';
134
- }
135
- }
136
-
137
- export function updateSchemaAccessibility(
138
- accessibility: ApiSchemaAccessibility,
139
- schema: {
140
- readOnly?: boolean;
141
- writeOnly?: boolean;
142
- },
143
- ): ApiSchemaAccessibility {
144
- if (accessibility === 'none') return 'none';
145
- if (accessibility === 'readOnly') return schema.writeOnly === true ? 'none' : 'readOnly';
146
- if (accessibility === 'writeOnly') return schema.readOnly === true ? 'none' : 'writeOnly';
147
- return determineSchemaAccessibility(schema);
148
- }
149
-
150
- type CustomFields<T extends Record<string, unknown>> = {
151
- [K in keyof T as K extends `x-${string}` ? K : never]: T[K];
152
- };
153
-
154
- export function getCustomFields<T extends Record<string, unknown>>(schema: T): CustomFields<T> {
155
- const result = {} as CustomFields<T>;
156
- for (const key in schema) {
157
- if (key.startsWith('x-')) {
158
- const name = key.substring(2);
159
- (result as Record<string, unknown>)[name] = schema[key];
160
- }
161
- }
162
- return result;
163
- }
164
-
165
- export function determineEndpointName(endpointInfo: {
166
- method: string;
167
- path: string;
168
- operation: { operationId?: string };
169
- }): string {
170
- if (endpointInfo.operation.operationId) return endpointInfo.operation.operationId;
171
- return endpointInfo.method + endpointInfo.path.replace(/\{([^}]+)\}/g, ':$1').replace(/\//g, '_');
172
- }
173
-
174
- export function transformAdditionalProperties<TAdditionalProperties>(
175
- context: OpenApiTransformerContext,
176
- schema: {
177
- additionalProperties?: TAdditionalProperties;
178
- },
179
- transformSchema: (
180
- context: OpenApiTransformerContext,
181
- schema: Exclude<TAdditionalProperties, undefined | boolean>,
182
- ) => ApiSchema,
183
- ): boolean | ApiSchema | undefined {
184
- if (isNullish(schema.additionalProperties)) return undefined;
185
- if (typeof schema.additionalProperties === 'boolean') return schema.additionalProperties;
186
- return transformSchema(context, schema.additionalProperties as Exclude<TAdditionalProperties, undefined | boolean>);
187
- }
188
-
189
- export function transformSchemaProperties<TProperties>(
190
- context: OpenApiTransformerContext,
191
- schema: { properties?: Record<string, TProperties>; required?: string[] },
192
- transformSchema: (context: OpenApiTransformerContext, schema: TProperties) => ApiSchema,
193
- ): Map<string, ApiSchemaProperty> {
194
- const result = new Map<string, ApiSchemaProperty>();
195
- if (!schema.properties) return result;
196
- for (const name of Object.keys(schema.properties)) {
197
- if (!isOpenApiObjectProperty(name)) continue;
198
- result.set(name, {
199
- name,
200
- schema: transformSchema(context, schema.properties[name]),
201
- });
202
- }
203
- return result;
204
- }
205
-
206
- export class IdGenerator {
207
- private readonly _idMap = new Map<string, number>();
208
-
209
- public generateId(name: string): string {
210
- const id = this._idMap.get(name) ?? 1;
211
- this._idMap.set(name, id + 1);
212
- return `${name}-${id}`;
213
- }
214
- }
215
-
216
- export function getOpenApiObjectIdentifier(obj: Deref<OpenApiObject<string>>) {
217
- return obj.$src.file + '#' + obj.$src.path;
218
- }