@effect/openapi-generator 4.0.0-beta.4 → 4.0.0-beta.41
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/dist/HttpApiTransformer.d.ts +6 -0
- package/dist/HttpApiTransformer.d.ts.map +1 -0
- package/dist/HttpApiTransformer.js +354 -0
- package/dist/HttpApiTransformer.js.map +1 -0
- package/dist/JsonSchemaGenerator.d.ts +13 -3
- package/dist/JsonSchemaGenerator.d.ts.map +1 -1
- package/dist/JsonSchemaGenerator.js +146 -47
- package/dist/JsonSchemaGenerator.js.map +1 -1
- package/dist/OpenApiGenerator.d.ts +17 -5
- package/dist/OpenApiGenerator.d.ts.map +1 -1
- package/dist/OpenApiGenerator.js +650 -117
- package/dist/OpenApiGenerator.js.map +1 -1
- package/dist/OpenApiPatch.d.ts +2 -2
- package/dist/OpenApiTransformer.d.ts +10 -10
- package/dist/OpenApiTransformer.d.ts.map +1 -1
- package/dist/OpenApiTransformer.js +14 -22
- package/dist/OpenApiTransformer.js.map +1 -1
- package/dist/ParsedOperation.d.ts +80 -1
- package/dist/ParsedOperation.d.ts.map +1 -1
- package/dist/ParsedOperation.js +25 -0
- package/dist/ParsedOperation.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +19 -8
- package/dist/main.js.map +1 -1
- package/package.json +5 -5
- package/src/HttpApiTransformer.ts +497 -0
- package/src/JsonSchemaGenerator.ts +240 -47
- package/src/OpenApiGenerator.ts +907 -171
- package/src/OpenApiTransformer.ts +18 -28
- package/src/ParsedOperation.ts +127 -1
- package/src/main.ts +36 -10
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import * as Layer from "effect/Layer"
|
|
2
2
|
import * as Predicate from "effect/Predicate"
|
|
3
3
|
import * as ServiceMap from "effect/ServiceMap"
|
|
4
|
-
import type {
|
|
5
|
-
import type { ParsedOperation } from "./ParsedOperation.ts"
|
|
4
|
+
import type { ParsedOpenApi, ParsedOperation } from "./ParsedOperation.ts"
|
|
6
5
|
import * as Utils from "./Utils.ts"
|
|
7
6
|
|
|
8
7
|
export class OpenApiTransformer extends ServiceMap.Service<
|
|
9
8
|
OpenApiTransformer,
|
|
10
9
|
{
|
|
11
|
-
readonly imports: (importName: string,
|
|
12
|
-
readonly toTypes: (importName: string, name: string,
|
|
13
|
-
readonly toImplementation: (importName: string, name: string,
|
|
10
|
+
readonly imports: (importName: string, parsed: ParsedOpenApi) => string
|
|
11
|
+
readonly toTypes: (importName: string, name: string, parsed: ParsedOpenApi) => string
|
|
12
|
+
readonly toImplementation: (importName: string, name: string, parsed: ParsedOpenApi) => string
|
|
14
13
|
}
|
|
15
14
|
>()("OpenApiTransformer") {}
|
|
16
15
|
|
|
@@ -36,17 +35,6 @@ const computeImportRequirements = (operations: ReadonlyArray<ParsedOperation>):
|
|
|
36
35
|
const requiresStreaming = (requirements: ImportRequirements): boolean =>
|
|
37
36
|
requirements.eventStream || requirements.octetStream
|
|
38
37
|
|
|
39
|
-
const httpClientMethodNames: Record<OpenAPISpecMethodName, string> = {
|
|
40
|
-
get: "get",
|
|
41
|
-
put: "put",
|
|
42
|
-
post: "post",
|
|
43
|
-
delete: "del",
|
|
44
|
-
options: "options",
|
|
45
|
-
head: "head",
|
|
46
|
-
patch: "patch",
|
|
47
|
-
trace: `make("TRACE")`
|
|
48
|
-
}
|
|
49
|
-
|
|
50
38
|
export const makeTransformerSchema = () => {
|
|
51
39
|
const operationsToInterface = (
|
|
52
40
|
_importName: string,
|
|
@@ -306,7 +294,7 @@ export const make = (
|
|
|
306
294
|
|
|
307
295
|
return (
|
|
308
296
|
`"${operation.id}": (${params}) => ` +
|
|
309
|
-
`HttpClientRequest.${
|
|
297
|
+
`HttpClientRequest.${operation.method}(${operation.pathTemplate})` +
|
|
310
298
|
`.pipe(\n ${pipeline.join(",\n ")}\n )`
|
|
311
299
|
)
|
|
312
300
|
}
|
|
@@ -347,7 +335,7 @@ export const make = (
|
|
|
347
335
|
|
|
348
336
|
return (
|
|
349
337
|
`"${operation.id}Sse": (${params}) => ` +
|
|
350
|
-
`HttpClientRequest.${
|
|
338
|
+
`HttpClientRequest.${operation.method}(${operation.pathTemplate})` +
|
|
351
339
|
`.pipe(\n ${pipeline.join(",\n ")}\n )`
|
|
352
340
|
)
|
|
353
341
|
}
|
|
@@ -388,13 +376,14 @@ export const make = (
|
|
|
388
376
|
|
|
389
377
|
return (
|
|
390
378
|
`"${operation.id}Stream": (${params}) => ` +
|
|
391
|
-
`HttpClientRequest.${
|
|
379
|
+
`HttpClientRequest.${operation.method}(${operation.pathTemplate})` +
|
|
392
380
|
`.pipe(\n ${pipeline.join(",\n ")}\n )`
|
|
393
381
|
)
|
|
394
382
|
}
|
|
395
383
|
|
|
396
384
|
return OpenApiTransformer.of({
|
|
397
|
-
imports: (importName,
|
|
385
|
+
imports: (importName, parsed) => {
|
|
386
|
+
const operations = parsed.operations
|
|
398
387
|
const requirements = computeImportRequirements(operations)
|
|
399
388
|
const imports = [
|
|
400
389
|
`import * as Data from "effect/Data"`,
|
|
@@ -421,8 +410,8 @@ export const make = (
|
|
|
421
410
|
)
|
|
422
411
|
return imports.join("\n")
|
|
423
412
|
},
|
|
424
|
-
toTypes: operationsToInterface,
|
|
425
|
-
toImplementation: operationsToImpl
|
|
413
|
+
toTypes: (importName, name, parsed) => operationsToInterface(importName, name, parsed.operations),
|
|
414
|
+
toImplementation: (importName, name, parsed) => operationsToImpl(importName, name, parsed.operations)
|
|
426
415
|
})
|
|
427
416
|
}
|
|
428
417
|
|
|
@@ -701,7 +690,7 @@ export const make = (
|
|
|
701
690
|
|
|
702
691
|
return (
|
|
703
692
|
`"${operation.id}": (${params}) => ` +
|
|
704
|
-
`HttpClientRequest.${
|
|
693
|
+
`HttpClientRequest.${operation.method}(${operation.pathTemplate})` +
|
|
705
694
|
`.pipe(\n ${pipeline.join(",\n ")}\n )`
|
|
706
695
|
)
|
|
707
696
|
}
|
|
@@ -742,7 +731,7 @@ export const make = (
|
|
|
742
731
|
|
|
743
732
|
return (
|
|
744
733
|
`"${operation.id}Sse": (${params}) => ` +
|
|
745
|
-
`HttpClientRequest.${
|
|
734
|
+
`HttpClientRequest.${operation.method}(${operation.pathTemplate})` +
|
|
746
735
|
`.pipe(\n ${pipeline.join(",\n ")}\n )`
|
|
747
736
|
)
|
|
748
737
|
}
|
|
@@ -783,13 +772,14 @@ export const make = (
|
|
|
783
772
|
|
|
784
773
|
return (
|
|
785
774
|
`"${operation.id}Stream": (${params}) => ` +
|
|
786
|
-
`HttpClientRequest.${
|
|
775
|
+
`HttpClientRequest.${operation.method}(${operation.pathTemplate})` +
|
|
787
776
|
`.pipe(\n ${pipeline.join(",\n ")}\n )`
|
|
788
777
|
)
|
|
789
778
|
}
|
|
790
779
|
|
|
791
780
|
return OpenApiTransformer.of({
|
|
792
|
-
imports: (_importName,
|
|
781
|
+
imports: (_importName, parsed) => {
|
|
782
|
+
const operations = parsed.operations
|
|
793
783
|
const requirements = computeImportRequirements(operations)
|
|
794
784
|
const imports = [
|
|
795
785
|
`import * as Data from "effect/Data"`,
|
|
@@ -806,8 +796,8 @@ export const make = (
|
|
|
806
796
|
)
|
|
807
797
|
return imports.join("\n")
|
|
808
798
|
},
|
|
809
|
-
toTypes: operationsToInterface,
|
|
810
|
-
toImplementation: operationsToImpl
|
|
799
|
+
toTypes: (importName, name, parsed) => operationsToInterface(importName, name, parsed.operations),
|
|
800
|
+
toImplementation: (importName, name, parsed) => operationsToImpl(importName, name, parsed.operations)
|
|
811
801
|
})
|
|
812
802
|
}
|
|
813
803
|
|
package/src/ParsedOperation.ts
CHANGED
|
@@ -1,9 +1,104 @@
|
|
|
1
1
|
import type * as Types from "effect/Types"
|
|
2
|
-
import type {
|
|
2
|
+
import type {
|
|
3
|
+
OpenAPISecurityRequirement,
|
|
4
|
+
OpenAPISpecExternalDocs,
|
|
5
|
+
OpenAPISpecLicense,
|
|
6
|
+
OpenAPISpecMethodName,
|
|
7
|
+
OpenAPISpecServer
|
|
8
|
+
} from "effect/unstable/httpapi/OpenApi"
|
|
9
|
+
|
|
10
|
+
export interface ParsedOpenApiMetadata {
|
|
11
|
+
readonly title: string
|
|
12
|
+
readonly version: string
|
|
13
|
+
readonly summary: string | undefined
|
|
14
|
+
readonly description: string | undefined
|
|
15
|
+
readonly license: OpenAPISpecLicense | undefined
|
|
16
|
+
readonly servers: ReadonlyArray<OpenAPISpecServer> | undefined
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface ParsedOpenApiTag {
|
|
20
|
+
readonly name: string
|
|
21
|
+
readonly description: string | undefined
|
|
22
|
+
readonly externalDocs: OpenAPISpecExternalDocs | undefined
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface ParsedOpenApiSecurityScheme {
|
|
26
|
+
readonly name: string
|
|
27
|
+
readonly type: "basic" | "bearer" | "apiKey"
|
|
28
|
+
readonly description: string | undefined
|
|
29
|
+
readonly bearerFormat: string | undefined
|
|
30
|
+
readonly key: string | undefined
|
|
31
|
+
readonly in: "header" | "query" | "cookie" | undefined
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ParsedOpenApi {
|
|
35
|
+
readonly metadata: ParsedOpenApiMetadata
|
|
36
|
+
readonly tags: ReadonlyArray<ParsedOpenApiTag>
|
|
37
|
+
readonly securitySchemes: ReadonlyArray<ParsedOpenApiSecurityScheme>
|
|
38
|
+
readonly operations: ReadonlyArray<ParsedOperation>
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface ParsedOperationMetadata {
|
|
42
|
+
readonly summary: string | undefined
|
|
43
|
+
readonly description: string | undefined
|
|
44
|
+
readonly deprecated: boolean
|
|
45
|
+
readonly externalDocs: OpenAPISpecExternalDocs | undefined
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface ParsedOperationParameter {
|
|
49
|
+
readonly name: string
|
|
50
|
+
readonly in: "path" | "query" | "header" | "cookie"
|
|
51
|
+
readonly required: boolean
|
|
52
|
+
readonly description: string | undefined
|
|
53
|
+
readonly schema: {}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export interface ParsedOperationRequestBody {
|
|
57
|
+
readonly required: boolean
|
|
58
|
+
readonly contentTypes: Array<string>
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export type ParsedOperationMediaTypeEncoding =
|
|
62
|
+
| "json"
|
|
63
|
+
| "multipart"
|
|
64
|
+
| "form-url-encoded"
|
|
65
|
+
| "text"
|
|
66
|
+
| "binary"
|
|
67
|
+
|
|
68
|
+
export interface ParsedOperationMediaTypeSchema {
|
|
69
|
+
readonly contentType: string
|
|
70
|
+
readonly encoding: ParsedOperationMediaTypeEncoding
|
|
71
|
+
readonly schema: string
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface ParsedOperationResponse {
|
|
75
|
+
readonly status: string
|
|
76
|
+
readonly description: string | undefined
|
|
77
|
+
readonly contentTypes: Array<string>
|
|
78
|
+
readonly hasHeaders: boolean
|
|
79
|
+
readonly isEmpty: boolean
|
|
80
|
+
readonly representable: ReadonlyArray<ParsedOperationMediaTypeSchema>
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export type ParsedOperationSecurityRequirement = Readonly<OpenAPISecurityRequirement>
|
|
3
84
|
|
|
4
85
|
export interface ParsedOperation {
|
|
5
86
|
readonly id: string
|
|
87
|
+
readonly operationId: string | undefined
|
|
88
|
+
readonly path: string
|
|
6
89
|
readonly method: OpenAPISpecMethodName
|
|
90
|
+
readonly tags: ReadonlyArray<string>
|
|
91
|
+
readonly metadata: ParsedOperationMetadata
|
|
92
|
+
readonly parameters: {
|
|
93
|
+
readonly path: ReadonlyArray<ParsedOperationParameter>
|
|
94
|
+
readonly query: ReadonlyArray<ParsedOperationParameter>
|
|
95
|
+
readonly header: ReadonlyArray<ParsedOperationParameter>
|
|
96
|
+
readonly cookie: ReadonlyArray<ParsedOperationParameter>
|
|
97
|
+
}
|
|
98
|
+
readonly requestBody: ParsedOperationRequestBody | undefined
|
|
99
|
+
readonly responses: ReadonlyArray<ParsedOperationResponse>
|
|
100
|
+
readonly defaultResponse: ParsedOperationResponse | undefined
|
|
101
|
+
readonly effectiveSecurity: ReadonlyArray<ParsedOperationSecurityRequirement>
|
|
7
102
|
readonly description: string | undefined
|
|
8
103
|
readonly params?: string
|
|
9
104
|
readonly paramsOptional: boolean
|
|
@@ -12,6 +107,12 @@ export interface ParsedOperation {
|
|
|
12
107
|
readonly cookies: ReadonlyArray<string>
|
|
13
108
|
readonly payload?: string
|
|
14
109
|
readonly payloadFormData: boolean
|
|
110
|
+
readonly pathSchema: string | undefined
|
|
111
|
+
readonly querySchema: string | undefined
|
|
112
|
+
readonly querySchemaOptional: boolean
|
|
113
|
+
readonly headersSchema: string | undefined
|
|
114
|
+
readonly headersSchemaOptional: boolean
|
|
115
|
+
readonly requestBodyRepresentable: ReadonlyArray<ParsedOperationMediaTypeSchema>
|
|
15
116
|
readonly pathIds: ReadonlyArray<string>
|
|
16
117
|
readonly pathTemplate: string
|
|
17
118
|
readonly successSchemas: ReadonlyMap<string, string>
|
|
@@ -31,10 +132,35 @@ export const makeDeepMutable = (options: {
|
|
|
31
132
|
readonly description: string | undefined
|
|
32
133
|
}): Types.DeepMutable<ParsedOperation> => ({
|
|
33
134
|
...options,
|
|
135
|
+
operationId: undefined,
|
|
136
|
+
path: "",
|
|
137
|
+
tags: [],
|
|
138
|
+
metadata: {
|
|
139
|
+
summary: undefined,
|
|
140
|
+
description: options.description,
|
|
141
|
+
deprecated: false,
|
|
142
|
+
externalDocs: undefined
|
|
143
|
+
},
|
|
144
|
+
parameters: {
|
|
145
|
+
path: [],
|
|
146
|
+
query: [],
|
|
147
|
+
header: [],
|
|
148
|
+
cookie: []
|
|
149
|
+
},
|
|
150
|
+
requestBody: undefined,
|
|
151
|
+
responses: [],
|
|
152
|
+
defaultResponse: undefined,
|
|
153
|
+
effectiveSecurity: [],
|
|
34
154
|
urlParams: [],
|
|
35
155
|
headers: [],
|
|
36
156
|
cookies: [],
|
|
37
157
|
payloadFormData: false,
|
|
158
|
+
pathSchema: undefined,
|
|
159
|
+
querySchema: undefined,
|
|
160
|
+
querySchemaOptional: true,
|
|
161
|
+
headersSchema: undefined,
|
|
162
|
+
headersSchemaOptional: true,
|
|
163
|
+
requestBodyRepresentable: [],
|
|
38
164
|
successSchemas: new Map(),
|
|
39
165
|
errorSchemas: new Map(),
|
|
40
166
|
voidSchemas: new Set(),
|
package/src/main.ts
CHANGED
|
@@ -10,18 +10,21 @@ import * as OpenApiPatch from "./OpenApiPatch.ts"
|
|
|
10
10
|
|
|
11
11
|
const spec = Flag.fileParse("spec").pipe(
|
|
12
12
|
Flag.withAlias("s"),
|
|
13
|
-
Flag.withDescription("The OpenAPI spec file to generate
|
|
13
|
+
Flag.withDescription("The OpenAPI spec file to generate output from")
|
|
14
14
|
)
|
|
15
15
|
|
|
16
16
|
const name = Flag.string("name").pipe(
|
|
17
17
|
Flag.withAlias("n"),
|
|
18
|
-
Flag.withDescription("The name of the generated
|
|
18
|
+
Flag.withDescription("The name of the generated output"),
|
|
19
19
|
Flag.withDefault("Client")
|
|
20
20
|
)
|
|
21
21
|
|
|
22
|
-
const
|
|
23
|
-
Flag.withAlias("
|
|
24
|
-
Flag.withDescription(
|
|
22
|
+
const format = Flag.choice("format", ["httpclient", "httpclient-type-only", "httpapi"] as const).pipe(
|
|
23
|
+
Flag.withAlias("f"),
|
|
24
|
+
Flag.withDescription(
|
|
25
|
+
"Output format to generate: httpclient | httpclient-type-only | httpapi (default: httpclient)"
|
|
26
|
+
),
|
|
27
|
+
Flag.withDefault("httpclient")
|
|
25
28
|
)
|
|
26
29
|
|
|
27
30
|
const patch = Flag.string("patch").pipe(
|
|
@@ -34,8 +37,8 @@ const patch = Flag.string("patch").pipe(
|
|
|
34
37
|
Flag.between(0, Infinity)
|
|
35
38
|
)
|
|
36
39
|
|
|
37
|
-
const root = Command.make("openapigen", { spec,
|
|
38
|
-
Command.withHandler(Effect.fnUntraced(function*({ name, spec,
|
|
40
|
+
const root = Command.make("openapigen", { spec, format, name, patch }).pipe(
|
|
41
|
+
Command.withHandler(Effect.fnUntraced(function*({ name, spec, format, patch }) {
|
|
39
42
|
let patchedSpec: Schema.Json = spec as Schema.Json
|
|
40
43
|
|
|
41
44
|
if (patch.length > 0) {
|
|
@@ -53,11 +56,23 @@ const root = Command.make("openapigen", { spec, typeOnly, name, patch }).pipe(
|
|
|
53
56
|
}
|
|
54
57
|
|
|
55
58
|
const generator = yield* OpenApiGenerator.OpenApiGenerator
|
|
56
|
-
const
|
|
59
|
+
const warnings: Array<OpenApiGenerator.OpenApiGeneratorWarning> = []
|
|
60
|
+
const source = yield* generator.generate(patchedSpec as unknown as OpenAPISpec, {
|
|
61
|
+
name,
|
|
62
|
+
format,
|
|
63
|
+
onWarning: (warning) => {
|
|
64
|
+
warnings.push(warning)
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
yield* Effect.forEach(
|
|
68
|
+
warnings,
|
|
69
|
+
(warning) => Console.error(formatWarning(warning)),
|
|
70
|
+
{ discard: true }
|
|
71
|
+
)
|
|
57
72
|
return yield* Console.log(source)
|
|
58
73
|
})),
|
|
59
|
-
Command.provide(({
|
|
60
|
-
|
|
74
|
+
Command.provide(({ format }) =>
|
|
75
|
+
format === "httpclient-type-only"
|
|
61
76
|
? OpenApiGenerator.layerTransformerTs
|
|
62
77
|
: OpenApiGenerator.layerTransformerSchema
|
|
63
78
|
)
|
|
@@ -66,3 +81,14 @@ const root = Command.make("openapigen", { spec, typeOnly, name, patch }).pipe(
|
|
|
66
81
|
export const run: Effect.Effect<void, CliError.CliError, Command.Environment> = Command.run(root, {
|
|
67
82
|
version: "0.0.0"
|
|
68
83
|
})
|
|
84
|
+
|
|
85
|
+
const formatWarning = (warning: OpenApiGenerator.OpenApiGeneratorWarning): string => {
|
|
86
|
+
const context = [
|
|
87
|
+
warning.method?.toUpperCase(),
|
|
88
|
+
warning.path,
|
|
89
|
+
warning.operationId ? `(${warning.operationId})` : undefined
|
|
90
|
+
].filter((value): value is string => value !== undefined)
|
|
91
|
+
return context.length > 0
|
|
92
|
+
? `WARNING [${warning.code}] ${context.join(" ")}: ${warning.message}`
|
|
93
|
+
: `WARNING [${warning.code}] ${warning.message}`
|
|
94
|
+
}
|