@goast/core 0.4.1-springwebclient1 → 0.4.2
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/LICENSE +21 -21
- package/README.md +67 -67
- package/package.json +1 -1
- package/src/_dnt.shims.ts +69 -0
- package/src/mod.ts +33 -0
- package/src/src/ast/node.ts +46 -0
- package/src/src/codegen/config.ts +28 -0
- package/src/src/codegen/endpoints-generator.ts +72 -0
- package/src/src/codegen/generator.ts +181 -0
- package/src/src/codegen/internal-utils.ts +6 -0
- package/src/src/codegen/schemas-generator.ts +87 -0
- package/src/src/codegen/services-generator.ts +77 -0
- package/src/src/codegen/types.ts +37 -0
- package/src/src/collect/collector.ts +142 -0
- package/src/src/collect/helpers.ts +36 -0
- package/src/src/collect/types.ts +21 -0
- package/src/src/internal-utils.ts +5 -0
- package/src/src/parse/deref-proxy.ts +64 -0
- package/src/src/parse/openapi-types.ts +949 -0
- package/src/src/parse/parser.ts +220 -0
- package/src/src/parse/types.ts +27 -0
- package/src/src/transform/api-types.ts +243 -0
- package/src/src/transform/helpers.ts +216 -0
- package/src/src/transform/transform-document.ts +35 -0
- package/src/src/transform/transform-endpoint.ts +331 -0
- package/src/src/transform/transform-schema.ts +217 -0
- package/src/src/transform/transformer.ts +52 -0
- package/src/src/transform/types.ts +52 -0
- package/src/src/utils/action-provider.ts +58 -0
- package/src/src/utils/array.utils.ts +7 -0
- package/src/src/utils/asset-manager.ts +51 -0
- package/src/src/utils/class.utils.ts +40 -0
- package/src/src/utils/common.utils.ts +86 -0
- package/src/src/utils/condition.ts +7 -0
- package/src/src/utils/endpoint.utils.ts +10 -0
- package/src/src/utils/error.utils.ts +3 -0
- package/src/src/utils/factory.ts +42 -0
- package/src/src/utils/file-system.utils.ts +48 -0
- package/src/src/utils/generator.utils.ts +6 -0
- package/src/src/utils/object.utils.ts +32 -0
- package/src/src/utils/schema.utils.ts +142 -0
- package/src/src/utils/source-builder.ts +597 -0
- package/src/src/utils/string-builder/options.ts +19 -0
- package/src/src/utils/string-builder/string-builder.ts +165 -0
- package/src/src/utils/string-builder/utils.ts +69 -0
- package/src/src/utils/string.utils.ts +369 -0
- package/src/src/utils/string.utils.types.ts +179 -0
- package/src/src/utils/type.utils.ts +93 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { ApiSchema } from '../transform/api-types.js';
|
|
2
|
+
import { getSchemaReference } from '../utils/schema.utils.js';
|
|
3
|
+
import type { MaybePromise } from '../utils/type.utils.js';
|
|
4
|
+
import { type DefaultGenerationProviderConfig, OpenApiGenerationProviderBase } from './generator.js';
|
|
5
|
+
import { addSourceIfTest } from './internal-utils.js';
|
|
6
|
+
import type {
|
|
7
|
+
AnyConfig,
|
|
8
|
+
OpenApiGenerationProviderContext,
|
|
9
|
+
OpenApiGeneratorContext,
|
|
10
|
+
OpenApiGeneratorInput,
|
|
11
|
+
OpenApiGeneratorOutput,
|
|
12
|
+
} from './types.js';
|
|
13
|
+
|
|
14
|
+
export type OpenApiSchemasGenerationProviderContext<
|
|
15
|
+
TInput extends OpenApiGeneratorInput,
|
|
16
|
+
TOutput extends OpenApiGeneratorOutput,
|
|
17
|
+
TConfig extends AnyConfig,
|
|
18
|
+
TSchemaOutput extends OpenApiGeneratorOutput,
|
|
19
|
+
> = OpenApiGenerationProviderContext<TInput, TConfig> & {
|
|
20
|
+
existingSchemaResults: Map<string, TSchemaOutput>;
|
|
21
|
+
output: TOutput;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export abstract class OpenApiSchemasGenerationProviderBase<
|
|
25
|
+
TInput extends OpenApiGeneratorInput,
|
|
26
|
+
TOutput extends OpenApiGeneratorOutput,
|
|
27
|
+
TConfig extends AnyConfig,
|
|
28
|
+
TSchemaOutput extends OpenApiGeneratorOutput,
|
|
29
|
+
TContext extends OpenApiSchemasGenerationProviderContext<TInput, TOutput, TConfig, TSchemaOutput>,
|
|
30
|
+
> extends OpenApiGenerationProviderBase<TInput, TOutput, TConfig, TContext> {
|
|
31
|
+
protected override getProviderContext(
|
|
32
|
+
context: OpenApiGeneratorContext<TInput>,
|
|
33
|
+
config: Partial<TConfig> | undefined,
|
|
34
|
+
defaultConfig: DefaultGenerationProviderConfig<TConfig>,
|
|
35
|
+
): OpenApiSchemasGenerationProviderContext<TInput, TOutput, TConfig, TSchemaOutput> {
|
|
36
|
+
const ctx = super.getProviderContext(context, config, defaultConfig);
|
|
37
|
+
return Object.assign(ctx, {
|
|
38
|
+
existingSchemaResults: new Map<string, TSchemaOutput>(),
|
|
39
|
+
output: this.initResult(ctx),
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
protected override async onGenerate(ctx: TContext): Promise<TOutput> {
|
|
44
|
+
for (const schema of ctx.data.schemas) {
|
|
45
|
+
await this.getSchemaResult(ctx, schema);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
await this.generateAdditionalFiles(ctx);
|
|
49
|
+
|
|
50
|
+
return ctx.output;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
public async getSchemaResult(ctx: TContext, schema: ApiSchema): Promise<TSchemaOutput> {
|
|
54
|
+
const existingResult = ctx.existingSchemaResults.get(schema.id);
|
|
55
|
+
if (existingResult) return existingResult;
|
|
56
|
+
|
|
57
|
+
let result: TSchemaOutput;
|
|
58
|
+
const reference = this.getSchemaReference(ctx, schema);
|
|
59
|
+
if (reference) {
|
|
60
|
+
result = await this.getSchemaResult(ctx, reference);
|
|
61
|
+
} else {
|
|
62
|
+
result = await this.generateSchema(ctx, schema);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
addSourceIfTest(ctx.config, result, () => `${schema.$src.file}#${schema.$src.path}`);
|
|
66
|
+
ctx.existingSchemaResults.set(schema.id, result);
|
|
67
|
+
this.addSchemaResult(ctx, schema, result);
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// deno-lint-ignore no-unused-vars
|
|
72
|
+
protected getSchemaReference(ctx: TContext, schema: ApiSchema): ApiSchema | undefined {
|
|
73
|
+
const ref = getSchemaReference(schema, ['description']);
|
|
74
|
+
return ref.id !== schema.id ? ref : undefined;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
protected abstract initResult(ctx: OpenApiGenerationProviderContext<TInput, TConfig>): TOutput;
|
|
78
|
+
|
|
79
|
+
protected abstract generateSchema(ctx: TContext, schema: ApiSchema): MaybePromise<TSchemaOutput>;
|
|
80
|
+
|
|
81
|
+
protected abstract addSchemaResult(ctx: TContext, schema: ApiSchema, result: TSchemaOutput): void;
|
|
82
|
+
|
|
83
|
+
// deno-lint-ignore no-unused-vars
|
|
84
|
+
protected generateAdditionalFiles(ctx: TContext): MaybePromise<void> {
|
|
85
|
+
// Override if needed
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { ApiService } from '../transform/api-types.js';
|
|
2
|
+
import type { MaybePromise } from '../utils/type.utils.js';
|
|
3
|
+
import { type DefaultGenerationProviderConfig, OpenApiGenerationProviderBase } from './generator.js';
|
|
4
|
+
import { addSourceIfTest } from './internal-utils.js';
|
|
5
|
+
import type {
|
|
6
|
+
AnyConfig,
|
|
7
|
+
OpenApiGenerationProviderContext,
|
|
8
|
+
OpenApiGeneratorContext,
|
|
9
|
+
OpenApiGeneratorInput,
|
|
10
|
+
OpenApiGeneratorOutput,
|
|
11
|
+
} from './types.js';
|
|
12
|
+
|
|
13
|
+
export type OpenApiServicesGenerationProviderContext<
|
|
14
|
+
TInput extends OpenApiGeneratorInput,
|
|
15
|
+
TOutput extends OpenApiGeneratorOutput,
|
|
16
|
+
TConfig extends AnyConfig,
|
|
17
|
+
TServiceOutput extends OpenApiGeneratorOutput,
|
|
18
|
+
> = OpenApiGenerationProviderContext<TInput, TConfig> & {
|
|
19
|
+
existingServiceResults: Map<string, TServiceOutput>;
|
|
20
|
+
output: TOutput;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export abstract class OpenApiServicesGenerationProviderBase<
|
|
24
|
+
TInput extends OpenApiGeneratorInput,
|
|
25
|
+
TOutput extends OpenApiGeneratorOutput,
|
|
26
|
+
TConfig extends AnyConfig,
|
|
27
|
+
TServiceOutput extends OpenApiGeneratorOutput,
|
|
28
|
+
TContext extends OpenApiServicesGenerationProviderContext<TInput, TOutput, TConfig, TServiceOutput>,
|
|
29
|
+
> extends OpenApiGenerationProviderBase<TInput, TOutput, TConfig, TContext> {
|
|
30
|
+
protected override getProviderContext(
|
|
31
|
+
context: OpenApiGeneratorContext<TInput>,
|
|
32
|
+
config: Partial<TConfig> | undefined,
|
|
33
|
+
defaultConfig: DefaultGenerationProviderConfig<TConfig>,
|
|
34
|
+
): OpenApiServicesGenerationProviderContext<TInput, TOutput, TConfig, TServiceOutput> {
|
|
35
|
+
const ctx = super.getProviderContext(context, config, defaultConfig);
|
|
36
|
+
return Object.assign(ctx, {
|
|
37
|
+
existingServiceResults: new Map<string, TServiceOutput>(),
|
|
38
|
+
output: this.initResult(ctx),
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
protected override async onGenerate(ctx: TContext): Promise<TOutput> {
|
|
43
|
+
for (const service of ctx.data.services) {
|
|
44
|
+
await this.getServiceResult(ctx, service);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
await this.generateAdditionalFiles(ctx);
|
|
48
|
+
|
|
49
|
+
return ctx.output;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
protected async getServiceResult(ctx: TContext, service: ApiService): Promise<TServiceOutput> {
|
|
53
|
+
const existingResult = ctx.existingServiceResults.get(service.id);
|
|
54
|
+
if (existingResult) return existingResult;
|
|
55
|
+
|
|
56
|
+
const result = await this.generateService(ctx, service);
|
|
57
|
+
|
|
58
|
+
addSourceIfTest(
|
|
59
|
+
ctx.config,
|
|
60
|
+
result,
|
|
61
|
+
() => service.$src ? `${service.$src.file}#${service.$src.path}` : `tag:${service.name}`,
|
|
62
|
+
);
|
|
63
|
+
this.addServiceResult(ctx, service, result);
|
|
64
|
+
return result;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
protected abstract initResult(ctx: OpenApiGenerationProviderContext<TInput, TConfig>): TOutput;
|
|
68
|
+
|
|
69
|
+
protected abstract generateService(ctx: TContext, service: ApiService): MaybePromise<TServiceOutput>;
|
|
70
|
+
|
|
71
|
+
protected abstract addServiceResult(ctx: TContext, service: ApiService, result: TServiceOutput): void;
|
|
72
|
+
|
|
73
|
+
// deno-lint-ignore no-unused-vars
|
|
74
|
+
protected generateAdditionalFiles(ctx: TContext): MaybePromise<void> {
|
|
75
|
+
// Override if needed
|
|
76
|
+
}
|
|
77
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { ApiData } from '../transform/api-types.js';
|
|
2
|
+
import type { MaybePromise } from '../utils/type.utils.js';
|
|
3
|
+
import type { OpenApiGeneratorConfig } from './config.js';
|
|
4
|
+
|
|
5
|
+
export type AnyConfig = Readonly<Record<string, unknown>>;
|
|
6
|
+
export type OpenApiGeneratorInput = Record<string, unknown>;
|
|
7
|
+
export type OpenApiGeneratorOutput = Record<string, unknown> | undefined;
|
|
8
|
+
export type OpenApiGeneratorContext<TInput extends OpenApiGeneratorInput = OpenApiGeneratorInput> = {
|
|
9
|
+
data: ApiData;
|
|
10
|
+
input: TInput;
|
|
11
|
+
config: OpenApiGeneratorConfig;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type OpenApiGenerationProviderContext<TInput extends OpenApiGeneratorInput, TConfig extends AnyConfig> =
|
|
15
|
+
& Omit<
|
|
16
|
+
OpenApiGeneratorContext<TInput>,
|
|
17
|
+
'config'
|
|
18
|
+
>
|
|
19
|
+
& {
|
|
20
|
+
config: OpenApiGeneratorConfig & TConfig;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export interface OpenApiGenerationProvider<
|
|
24
|
+
TInput extends OpenApiGeneratorInput = OpenApiGeneratorInput,
|
|
25
|
+
TOutput extends OpenApiGeneratorOutput = OpenApiGeneratorOutput,
|
|
26
|
+
TConfig extends AnyConfig = AnyConfig,
|
|
27
|
+
> {
|
|
28
|
+
generate(
|
|
29
|
+
...args: Parameters<OpenApiGenerationProviderFn<TInput, TOutput, TConfig>>
|
|
30
|
+
): ReturnType<OpenApiGenerationProviderFn<TInput, TOutput, TConfig>>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type OpenApiGenerationProviderFn<
|
|
34
|
+
TInput extends OpenApiGeneratorInput = OpenApiGeneratorInput,
|
|
35
|
+
TOutput extends OpenApiGeneratorOutput = OpenApiGeneratorOutput,
|
|
36
|
+
TConfig extends AnyConfig = AnyConfig,
|
|
37
|
+
> = (context: OpenApiGeneratorContext<TInput>, config?: Partial<TConfig>) => MaybePromise<TOutput>;
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
OpenApiDocument,
|
|
3
|
+
OpenApiHeader,
|
|
4
|
+
OpenApiMediaType,
|
|
5
|
+
OpenApiParameter,
|
|
6
|
+
OpenApiPathItem,
|
|
7
|
+
OpenApiRequestBody,
|
|
8
|
+
OpenApiResponse,
|
|
9
|
+
OpenApiSchema,
|
|
10
|
+
} from '../parse/openapi-types.js';
|
|
11
|
+
import type { Deref } from '../parse/types.js';
|
|
12
|
+
import { collect, collectRecord } from './helpers.js';
|
|
13
|
+
import type { OpenApiCollectorData } from './types.js';
|
|
14
|
+
|
|
15
|
+
type Collect<T> = Deref<T> | Deref<T>[] | undefined;
|
|
16
|
+
|
|
17
|
+
export function collectOpenApi(apis: Deref<OpenApiDocument>[]): OpenApiCollectorData {
|
|
18
|
+
const data: OpenApiCollectorData = {
|
|
19
|
+
documents: [],
|
|
20
|
+
schemas: new Map(),
|
|
21
|
+
endpoints: new Map(),
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
collectDocument(data, apis);
|
|
25
|
+
|
|
26
|
+
return data;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const jsonSchemaProperties = ['$id', 'allOf', 'anyOf', 'oneOf', 'enum', 'not', 'properties', 'title', 'type'];
|
|
30
|
+
|
|
31
|
+
function collectDocument(data: OpenApiCollectorData, documents: Collect<OpenApiDocument>) {
|
|
32
|
+
collect(data, documents, (data, document) => {
|
|
33
|
+
data.documents.push(document);
|
|
34
|
+
collectPaths(data, document.paths);
|
|
35
|
+
collectRecord<Deref<OpenApiSchema>>(data, document.components?.schemas, collectSchema);
|
|
36
|
+
collectRecord<Deref<OpenApiSchema>>(data, document.definitions, collectSchema);
|
|
37
|
+
collectRecord<Deref<OpenApiParameter>>(data, document.components?.parameters, collectParameter);
|
|
38
|
+
collectRecord<Deref<OpenApiParameter>>(data, document.parameters, collectParameter);
|
|
39
|
+
collectRecord<Deref<OpenApiRequestBody>>(data, document.components?.requestBodies, collectRequestBody);
|
|
40
|
+
collectRecord<Deref<OpenApiResponse>>(data, document.components?.responses, collectResponse);
|
|
41
|
+
collectRecord<Deref<OpenApiResponse>>(data, document.responses, collectResponse);
|
|
42
|
+
collectRecord<Deref<OpenApiHeader>>(data, document.components?.headers, collectHeader);
|
|
43
|
+
|
|
44
|
+
if (Object.keys(document).some((key) => jsonSchemaProperties.includes(key))) {
|
|
45
|
+
collectSchema(data, document as Deref<OpenApiSchema>);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function collectPaths(data: OpenApiCollectorData, paths: Collect<Record<string, OpenApiPathItem>>) {
|
|
51
|
+
collect(data, paths, (data, path) => {
|
|
52
|
+
collectRecord<Deref<OpenApiPathItem>>(data, path, collectPathItem);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function collectPathItem(data: OpenApiCollectorData, pathItems: Collect<OpenApiPathItem>, path: string) {
|
|
57
|
+
collect(data, pathItems, (data, pathItem) => {
|
|
58
|
+
collectParameter(data, pathItem.parameters);
|
|
59
|
+
for (const m of ['get', 'put', 'post', 'delete', 'options', 'head', 'patch', 'trace'] as const) {
|
|
60
|
+
const operation = pathItem[m];
|
|
61
|
+
if (!operation) continue;
|
|
62
|
+
const key = `${operation.$src.file}#${operation.$src.path}`;
|
|
63
|
+
if (data.endpoints.has(key)) continue;
|
|
64
|
+
data.endpoints.set(key, {
|
|
65
|
+
path,
|
|
66
|
+
method: m,
|
|
67
|
+
pathItem,
|
|
68
|
+
operation,
|
|
69
|
+
});
|
|
70
|
+
collectParameter(data, operation.parameters);
|
|
71
|
+
collectRequestBody(data, operation.requestBody);
|
|
72
|
+
collectRecord<Deref<OpenApiResponse>>(data, operation.responses, collectResponse);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function collectSchema(data: OpenApiCollectorData, schemas: Collect<OpenApiSchema>) {
|
|
78
|
+
collect(data, schemas, (data, schema) => {
|
|
79
|
+
const key = `${schema.$src.file}#${schema.$src.path}`;
|
|
80
|
+
if (data.schemas.has(key)) return;
|
|
81
|
+
data.schemas.set(key, schema);
|
|
82
|
+
collectSchema(data, schema.allOf);
|
|
83
|
+
collectSchema(data, schema.anyOf);
|
|
84
|
+
collectSchema(data, schema.oneOf);
|
|
85
|
+
collectSchema(data, schema.items);
|
|
86
|
+
collectSchema(data, schema.not);
|
|
87
|
+
collectRecord<Deref<OpenApiSchema>>(data, schema.properties, collectSchema);
|
|
88
|
+
collectRecord<Deref<OpenApiSchema>>(data, schema.patternProperties, collectSchema);
|
|
89
|
+
collectRecord<Deref<OpenApiSchema>>(data, schema.dependencies, collectSchema);
|
|
90
|
+
collectRecord<Deref<OpenApiSchema>>(data, schema.definitions, collectSchema);
|
|
91
|
+
if (typeof schema.additionalProperties === 'object') {
|
|
92
|
+
collectSchema(data, schema.additionalProperties);
|
|
93
|
+
}
|
|
94
|
+
if (typeof schema.additionalItems === 'object') {
|
|
95
|
+
collectSchema(data, schema.additionalItems);
|
|
96
|
+
}
|
|
97
|
+
if (typeof schema.discriminator === 'object') {
|
|
98
|
+
collectRecord<Deref<OpenApiSchema>>(data, schema.discriminator?.mapping, collectSchema);
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function collectParameter(data: OpenApiCollectorData, parameters: Collect<OpenApiParameter>) {
|
|
104
|
+
collect(data, parameters, (data, parameter) => {
|
|
105
|
+
collectSchema(data, parameter.schema);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function collectMediaType(data: OpenApiCollectorData, mediaTypes: Collect<OpenApiMediaType>) {
|
|
110
|
+
collect(data, mediaTypes, (data, mediaType) => {
|
|
111
|
+
collectSchema(data, mediaType.schema);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function collectRequestBody(data: OpenApiCollectorData, requestBody: Collect<OpenApiRequestBody>) {
|
|
116
|
+
collect(data, requestBody, (data, requestBody) => {
|
|
117
|
+
collectRecord<Deref<OpenApiMediaType>>(data, requestBody.content, collectMediaType);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function isSchema(obj: Record<string, unknown>): obj is Deref<OpenApiSchema> {
|
|
122
|
+
return obj && typeof obj === 'object' && obj['type'] !== undefined;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function collectResponse(data: OpenApiCollectorData, responses: Collect<OpenApiResponse>) {
|
|
126
|
+
collect(data, responses, (data, responses) => {
|
|
127
|
+
collectRecord<Deref<OpenApiMediaType>>(data, responses.content, collectMediaType);
|
|
128
|
+
collect(data, responses.headers, (data, header) => {
|
|
129
|
+
if (isSchema(header)) {
|
|
130
|
+
collectSchema(data, header);
|
|
131
|
+
} else {
|
|
132
|
+
collectHeader(data, header);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function collectHeader(data: OpenApiCollectorData, headers: Collect<OpenApiHeader>) {
|
|
139
|
+
collect(data, headers, (data, headers) => {
|
|
140
|
+
collectSchema(data, headers.schema);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { isOpenApiObjectProperty } from '../internal-utils.js';
|
|
2
|
+
import type { Nullable } from '../utils/type.utils.js';
|
|
3
|
+
import type { OpenApiCollectorData } from './types.js';
|
|
4
|
+
|
|
5
|
+
export function collect<T extends Record<`x-${string}`, unknown>>(
|
|
6
|
+
data: OpenApiCollectorData,
|
|
7
|
+
obj: Nullable<T | Nullable<T>[]>,
|
|
8
|
+
func: (data: OpenApiCollectorData, obj: NonNullable<T>) => void,
|
|
9
|
+
) {
|
|
10
|
+
if (!obj) return;
|
|
11
|
+
if (Array.isArray(obj)) {
|
|
12
|
+
for (const o of obj) {
|
|
13
|
+
if (!o) continue;
|
|
14
|
+
if (o['x-ignore']) continue;
|
|
15
|
+
func(data, o);
|
|
16
|
+
}
|
|
17
|
+
} else {
|
|
18
|
+
if (obj['x-ignore']) return;
|
|
19
|
+
func(data, obj);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function collectRecord<T extends Record<`x-${string}`, unknown>>(
|
|
24
|
+
data: OpenApiCollectorData,
|
|
25
|
+
obj: Nullable<Record<string, Nullable<T>>>,
|
|
26
|
+
func: (data: OpenApiCollectorData, obj: NonNullable<T>, key: string) => void,
|
|
27
|
+
) {
|
|
28
|
+
if (!obj) return;
|
|
29
|
+
for (const key in obj) {
|
|
30
|
+
if (!isOpenApiObjectProperty(key)) continue;
|
|
31
|
+
const value = obj[key];
|
|
32
|
+
if (!value) continue;
|
|
33
|
+
if (value['x-ignore']) continue;
|
|
34
|
+
func(data, value, key);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
OpenApiDocument,
|
|
3
|
+
OpenApiHttpMethod,
|
|
4
|
+
OpenApiOperation,
|
|
5
|
+
OpenApiPathItem,
|
|
6
|
+
OpenApiSchema,
|
|
7
|
+
} from '../parse/openapi-types.js';
|
|
8
|
+
import type { Deref } from '../parse/types.js';
|
|
9
|
+
|
|
10
|
+
export type OpenApiCollectorEndpointInfo = {
|
|
11
|
+
path: string;
|
|
12
|
+
method: OpenApiHttpMethod;
|
|
13
|
+
pathItem: Deref<OpenApiPathItem>;
|
|
14
|
+
operation: Deref<OpenApiOperation>;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type OpenApiCollectorData = {
|
|
18
|
+
documents: Deref<OpenApiDocument>[];
|
|
19
|
+
schemas: Map<string, Deref<OpenApiSchema>>;
|
|
20
|
+
endpoints: Map<string, OpenApiCollectorEndpointInfo>;
|
|
21
|
+
};
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { isNullish } from '../utils/common.utils.js';
|
|
2
|
+
import type { OpenApiObject } from './openapi-types.js';
|
|
3
|
+
import type { Deref, DerefSource } from './types.js';
|
|
4
|
+
|
|
5
|
+
export function createDerefProxy<T extends OpenApiObject<string>>(
|
|
6
|
+
obj: T,
|
|
7
|
+
source: DerefSource<T>,
|
|
8
|
+
ref?: Deref<T>,
|
|
9
|
+
): Deref<T> {
|
|
10
|
+
return new Proxy(obj, new DerefProxyHandler(source, ref)) as Deref<T>;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const propertiesNotDerivedFromRef: PropertyKey[] = ['title'];
|
|
14
|
+
|
|
15
|
+
class DerefProxyHandler<T extends OpenApiObject<string>> implements ProxyHandler<T> {
|
|
16
|
+
private readonly _overwrittenValues: Record<PropertyKey, unknown> = {};
|
|
17
|
+
|
|
18
|
+
constructor(
|
|
19
|
+
private readonly _source: DerefSource<T>,
|
|
20
|
+
private _ref?: Deref<T>,
|
|
21
|
+
) {}
|
|
22
|
+
|
|
23
|
+
public get(target: T, prop: PropertyKey): unknown {
|
|
24
|
+
if (prop === '$ref') {
|
|
25
|
+
return this._ref;
|
|
26
|
+
} else if (prop === '$src') {
|
|
27
|
+
return this._source;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (this._overwrittenValues[prop] !== undefined) {
|
|
31
|
+
return this._overwrittenValues[prop];
|
|
32
|
+
}
|
|
33
|
+
if ((target as Record<PropertyKey, unknown>)[prop] !== undefined) {
|
|
34
|
+
return (target as Record<PropertyKey, unknown>)[prop];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return propertiesNotDerivedFromRef.includes(prop)
|
|
38
|
+
? undefined
|
|
39
|
+
: (this._ref as Record<PropertyKey, unknown> | undefined)?.[prop];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public set(_target: T, prop: PropertyKey, value: unknown): boolean {
|
|
43
|
+
if (prop === '$ref' && (typeof value === 'object' || typeof value === 'undefined')) {
|
|
44
|
+
this._ref = !value ? undefined : (value as Deref<T>);
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
if (prop === '$src') {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
this._overwrittenValues[prop] = value;
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public has(target: T, prop: PropertyKey): boolean {
|
|
56
|
+
return prop in target || (!isNullish(this._ref) && prop in this._ref);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
public ownKeys(target: T): ArrayLike<string | symbol> {
|
|
60
|
+
return Array.from(
|
|
61
|
+
new Set([...Object.keys(target), ...(!isNullish(this._ref) ? Object.keys(this._ref) : []), '$ref', '$src']),
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
}
|