@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.
Files changed (42) hide show
  1. package/LICENSE +21 -0
  2. package/dist/JsonSchemaGenerator.d.ts +8 -0
  3. package/dist/JsonSchemaGenerator.d.ts.map +1 -0
  4. package/dist/JsonSchemaGenerator.js +75 -0
  5. package/dist/JsonSchemaGenerator.js.map +1 -0
  6. package/dist/OpenApiGenerator.d.ts +32 -0
  7. package/dist/OpenApiGenerator.d.ts.map +1 -0
  8. package/dist/OpenApiGenerator.js +206 -0
  9. package/dist/OpenApiGenerator.js.map +1 -0
  10. package/dist/OpenApiPatch.d.ts +296 -0
  11. package/dist/OpenApiPatch.d.ts.map +1 -0
  12. package/dist/OpenApiPatch.js +448 -0
  13. package/dist/OpenApiPatch.js.map +1 -0
  14. package/dist/OpenApiTransformer.d.ts +24 -0
  15. package/dist/OpenApiTransformer.d.ts.map +1 -0
  16. package/dist/OpenApiTransformer.js +740 -0
  17. package/dist/OpenApiTransformer.js.map +1 -0
  18. package/dist/ParsedOperation.d.ts +29 -0
  19. package/dist/ParsedOperation.d.ts.map +1 -0
  20. package/dist/ParsedOperation.js +13 -0
  21. package/dist/ParsedOperation.js.map +1 -0
  22. package/dist/Utils.d.ts +6 -0
  23. package/dist/Utils.d.ts.map +1 -0
  24. package/dist/Utils.js +42 -0
  25. package/dist/Utils.js.map +1 -0
  26. package/dist/bin.d.ts +3 -0
  27. package/dist/bin.d.ts.map +1 -0
  28. package/dist/bin.js +7 -0
  29. package/dist/bin.js.map +1 -0
  30. package/dist/main.d.ts +5 -0
  31. package/dist/main.d.ts.map +1 -0
  32. package/dist/main.js +47 -0
  33. package/dist/main.js.map +1 -0
  34. package/package.json +67 -0
  35. package/src/JsonSchemaGenerator.ts +94 -0
  36. package/src/OpenApiGenerator.ts +309 -0
  37. package/src/OpenApiPatch.ts +514 -0
  38. package/src/OpenApiTransformer.ts +954 -0
  39. package/src/ParsedOperation.ts +43 -0
  40. package/src/Utils.ts +50 -0
  41. package/src/bin.ts +10 -0
  42. 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
+ })