@effect/openapi-generator 4.0.0-beta.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/LICENSE +21 -0
- package/dist/JsonSchemaGenerator.d.ts +8 -0
- package/dist/JsonSchemaGenerator.d.ts.map +1 -0
- package/dist/JsonSchemaGenerator.js +75 -0
- package/dist/JsonSchemaGenerator.js.map +1 -0
- package/dist/OpenApiGenerator.d.ts +32 -0
- package/dist/OpenApiGenerator.d.ts.map +1 -0
- package/dist/OpenApiGenerator.js +206 -0
- package/dist/OpenApiGenerator.js.map +1 -0
- package/dist/OpenApiPatch.d.ts +296 -0
- package/dist/OpenApiPatch.d.ts.map +1 -0
- package/dist/OpenApiPatch.js +448 -0
- package/dist/OpenApiPatch.js.map +1 -0
- package/dist/OpenApiTransformer.d.ts +24 -0
- package/dist/OpenApiTransformer.d.ts.map +1 -0
- package/dist/OpenApiTransformer.js +740 -0
- package/dist/OpenApiTransformer.js.map +1 -0
- package/dist/ParsedOperation.d.ts +29 -0
- package/dist/ParsedOperation.d.ts.map +1 -0
- package/dist/ParsedOperation.js +13 -0
- package/dist/ParsedOperation.js.map +1 -0
- package/dist/Utils.d.ts +6 -0
- package/dist/Utils.d.ts.map +1 -0
- package/dist/Utils.js +42 -0
- package/dist/Utils.js.map +1 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +7 -0
- package/dist/bin.js.map +1 -0
- package/dist/main.d.ts +5 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +47 -0
- package/dist/main.js.map +1 -0
- package/package.json +67 -0
- package/src/JsonSchemaGenerator.ts +94 -0
- package/src/OpenApiGenerator.ts +309 -0
- package/src/OpenApiPatch.ts +514 -0
- package/src/OpenApiTransformer.ts +954 -0
- package/src/ParsedOperation.ts +43 -0
- package/src/Utils.ts +50 -0
- package/src/bin.ts +10 -0
- package/src/main.ts +68 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type * as Types from "effect/Types"
|
|
2
|
+
import type { OpenAPISpecMethodName } from "effect/unstable/httpapi/OpenApi"
|
|
3
|
+
|
|
4
|
+
export interface ParsedOperation {
|
|
5
|
+
readonly id: string
|
|
6
|
+
readonly method: OpenAPISpecMethodName
|
|
7
|
+
readonly description: string | undefined
|
|
8
|
+
readonly params?: string
|
|
9
|
+
readonly paramsOptional: boolean
|
|
10
|
+
readonly urlParams: ReadonlyArray<string>
|
|
11
|
+
readonly headers: ReadonlyArray<string>
|
|
12
|
+
readonly cookies: ReadonlyArray<string>
|
|
13
|
+
readonly payload?: string
|
|
14
|
+
readonly payloadFormData: boolean
|
|
15
|
+
readonly pathIds: ReadonlyArray<string>
|
|
16
|
+
readonly pathTemplate: string
|
|
17
|
+
readonly successSchemas: ReadonlyMap<string, string>
|
|
18
|
+
readonly errorSchemas: ReadonlyMap<string, string>
|
|
19
|
+
readonly voidSchemas: ReadonlySet<string>
|
|
20
|
+
// SSE streaming response schema (text/event-stream)
|
|
21
|
+
readonly sseSchema?: string
|
|
22
|
+
// Binary stream response (application/octet-stream)
|
|
23
|
+
readonly binaryResponse: boolean
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export const makeDeepMutable = (options: {
|
|
27
|
+
readonly id: string
|
|
28
|
+
readonly method: OpenAPISpecMethodName
|
|
29
|
+
readonly pathIds: Array<string>
|
|
30
|
+
readonly pathTemplate: string
|
|
31
|
+
readonly description: string | undefined
|
|
32
|
+
}): Types.DeepMutable<ParsedOperation> => ({
|
|
33
|
+
...options,
|
|
34
|
+
urlParams: [],
|
|
35
|
+
headers: [],
|
|
36
|
+
cookies: [],
|
|
37
|
+
payloadFormData: false,
|
|
38
|
+
successSchemas: new Map(),
|
|
39
|
+
errorSchemas: new Map(),
|
|
40
|
+
voidSchemas: new Set(),
|
|
41
|
+
paramsOptional: true,
|
|
42
|
+
binaryResponse: false
|
|
43
|
+
})
|
package/src/Utils.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import * as String from "effect/String"
|
|
2
|
+
import * as UndefinedOr from "effect/UndefinedOr"
|
|
3
|
+
|
|
4
|
+
export const camelize = (self: string): string => {
|
|
5
|
+
let str = ""
|
|
6
|
+
let hadSymbol = false
|
|
7
|
+
for (let i = 0; i < self.length; i++) {
|
|
8
|
+
const charCode = self.charCodeAt(i)
|
|
9
|
+
if (
|
|
10
|
+
(charCode >= 65 && charCode <= 90) ||
|
|
11
|
+
(charCode >= 97 && charCode <= 122)
|
|
12
|
+
) {
|
|
13
|
+
str += hadSymbol ? self[i].toUpperCase() : self[i]
|
|
14
|
+
hadSymbol = false
|
|
15
|
+
} else if (charCode >= 48 && charCode <= 57) {
|
|
16
|
+
if (str.length > 0) {
|
|
17
|
+
str += self[i]
|
|
18
|
+
hadSymbol = true
|
|
19
|
+
}
|
|
20
|
+
} else if (str.length > 0) {
|
|
21
|
+
hadSymbol = true
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return str
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const identifier = (operationId: string) => String.capitalize(camelize(operationId))
|
|
28
|
+
|
|
29
|
+
export const nonEmptyString = (a: unknown): string | undefined => {
|
|
30
|
+
if (typeof a === "string") {
|
|
31
|
+
const trimmed = String.trim(a)
|
|
32
|
+
if (String.isNonEmpty(trimmed)) {
|
|
33
|
+
return trimmed
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const toComment = UndefinedOr.match({
|
|
39
|
+
onUndefined: () => "",
|
|
40
|
+
onDefined: (description: string) =>
|
|
41
|
+
`/**
|
|
42
|
+
* ${description.replace(/\*\//g, " * /").split("\n").join("\n* ")}
|
|
43
|
+
*/\n`
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
export const spreadElementsInto = <A>(source: Array<A>, destination: Array<A>): void => {
|
|
47
|
+
for (let i = 0; i < source.length; i++) {
|
|
48
|
+
destination.push(source[i])
|
|
49
|
+
}
|
|
50
|
+
}
|
package/src/bin.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import * as NodeRuntime from "@effect/platform-node/NodeRuntime"
|
|
3
|
+
import * as NodeServices from "@effect/platform-node/NodeServices"
|
|
4
|
+
import * as Effect from "effect/Effect"
|
|
5
|
+
import { run } from "./main.ts"
|
|
6
|
+
|
|
7
|
+
run.pipe(
|
|
8
|
+
Effect.provide(NodeServices.layer),
|
|
9
|
+
NodeRuntime.runMain
|
|
10
|
+
)
|
package/src/main.ts
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import * as Console from "effect/Console"
|
|
2
|
+
import * as Effect from "effect/Effect"
|
|
3
|
+
import type * as Schema from "effect/Schema"
|
|
4
|
+
import * as CliError from "effect/unstable/cli/CliError"
|
|
5
|
+
import * as Command from "effect/unstable/cli/Command"
|
|
6
|
+
import * as Flag from "effect/unstable/cli/Flag"
|
|
7
|
+
import type { OpenAPISpec } from "effect/unstable/httpapi/OpenApi"
|
|
8
|
+
import * as OpenApiGenerator from "./OpenApiGenerator.ts"
|
|
9
|
+
import * as OpenApiPatch from "./OpenApiPatch.ts"
|
|
10
|
+
|
|
11
|
+
const spec = Flag.fileParse("spec").pipe(
|
|
12
|
+
Flag.withAlias("s"),
|
|
13
|
+
Flag.withDescription("The OpenAPI spec file to generate the client from")
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
const name = Flag.string("name").pipe(
|
|
17
|
+
Flag.withAlias("n"),
|
|
18
|
+
Flag.withDescription("The name of the generated client"),
|
|
19
|
+
Flag.withDefault("Client")
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
const typeOnly = Flag.boolean("type-only").pipe(
|
|
23
|
+
Flag.withAlias("t"),
|
|
24
|
+
Flag.withDescription("Generate a type-only client without schemas")
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
const patch = Flag.string("patch").pipe(
|
|
28
|
+
Flag.withAlias("p"),
|
|
29
|
+
Flag.withDescription(
|
|
30
|
+
"JSON patch to apply to OpenAPI spec before generation. " +
|
|
31
|
+
"Can be a file path (.json, .yaml, .yml) or inline JSON array. " +
|
|
32
|
+
"Multiple patches are applied in order."
|
|
33
|
+
),
|
|
34
|
+
Flag.between(0, Infinity)
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
const root = Command.make("openapigen", { spec, typeOnly, name, patch }).pipe(
|
|
38
|
+
Command.withHandler(Effect.fnUntraced(function*({ name, spec, typeOnly, patch }) {
|
|
39
|
+
let patchedSpec: Schema.Json = spec as Schema.Json
|
|
40
|
+
|
|
41
|
+
if (patch.length > 0) {
|
|
42
|
+
const parsedPatches = yield* Effect.forEach(
|
|
43
|
+
patch,
|
|
44
|
+
(input) =>
|
|
45
|
+
OpenApiPatch.parsePatchInput(input).pipe(
|
|
46
|
+
Effect.map((p) => ({ source: input, patch: p })),
|
|
47
|
+
Effect.mapError((error) => new CliError.UserError({ cause: error }))
|
|
48
|
+
)
|
|
49
|
+
)
|
|
50
|
+
patchedSpec = yield* OpenApiPatch.applyPatches(parsedPatches, patchedSpec).pipe(
|
|
51
|
+
Effect.mapError((error) => new CliError.UserError({ cause: error }))
|
|
52
|
+
)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const generator = yield* OpenApiGenerator.OpenApiGenerator
|
|
56
|
+
const source = yield* generator.generate(patchedSpec as unknown as OpenAPISpec, { name, typeOnly })
|
|
57
|
+
return yield* Console.log(source)
|
|
58
|
+
})),
|
|
59
|
+
Command.provide(({ typeOnly }) =>
|
|
60
|
+
typeOnly
|
|
61
|
+
? OpenApiGenerator.layerTransformerTs
|
|
62
|
+
: OpenApiGenerator.layerTransformerSchema
|
|
63
|
+
)
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
export const run: Effect.Effect<void, CliError.CliError, Command.Environment> = Command.run(root, {
|
|
67
|
+
version: "0.0.0"
|
|
68
|
+
})
|