@redocly/cli 2.71.2 → 2.72.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 (159) hide show
  1. package/README.md +1 -1
  2. package/THIRD_PARTY_NOTICES +29 -7
  3. package/lib/chunks/{AZ5LIJEO.js → 2DXUH2SS.js} +1 -1
  4. package/lib/chunks/{UYZXI3NO.js → 2X2KHSF3.js} +1 -1
  5. package/lib/chunks/{BPJTNQBC.js → 42X6IAVM.js} +1 -1
  6. package/lib/chunks/44US4AGQ.js +13 -0
  7. package/lib/chunks/{R32HBYOH.js → 4AXMK6B2.js} +1 -1
  8. package/lib/chunks/4ZCKAKVF.js +199 -0
  9. package/lib/chunks/{N4GCIKY4.js → 5F7XFPS7.js} +1 -1
  10. package/lib/chunks/{YF4FMOTE.js → 6234PJEZ.js} +1 -1
  11. package/lib/chunks/{4H3DPQDW.js → 62SOICN7.js} +1 -1
  12. package/lib/chunks/{BOTTA3HA.js → 6JFL6L56.js} +1 -1
  13. package/lib/chunks/{NEWT5A6S.js → 6RTITFQA.js} +1 -1
  14. package/lib/chunks/{KJNBOXDI.js → 6XDKNFYM.js} +3 -3
  15. package/lib/chunks/{L325HIGK.js → 76AJ5P4Y.js} +1 -1
  16. package/lib/chunks/{JRVXIEVC.js → 7HTKK26B.js} +1 -1
  17. package/lib/chunks/{2TG3DP4I.js → A7XJ7Y3N.js} +1 -1
  18. package/lib/chunks/AB4FYNJN.js +22 -0
  19. package/lib/chunks/BJ6Z66KZ.js +7 -0
  20. package/lib/chunks/{XVS7B4RZ.js → BL2KWYMY.js} +1 -1
  21. package/lib/chunks/{BIS2ZFGS.js → BTLP7Z6A.js} +1 -1
  22. package/lib/chunks/{AXGFQS75.js → BXMYSHQY.js} +2 -2
  23. package/lib/chunks/{JOUEVI3E.js → BY22HEXP.js} +1 -1
  24. package/lib/chunks/D2BBHPMR.js +7 -0
  25. package/lib/chunks/{5CR6AOLY.js → D3C6XIMY.js} +3 -3
  26. package/lib/chunks/{DWXDCKZ7.js → DNDYWJHC.js} +1 -1
  27. package/lib/chunks/{ORWIUECT.js → E6QTRG6I.js} +1 -1
  28. package/lib/chunks/EHMG7I34.js +9 -0
  29. package/lib/chunks/{UTEOWLD5.js → EL6MGCYE.js} +1 -1
  30. package/lib/chunks/EVCSNWMN.js +7 -0
  31. package/lib/chunks/{KLGUZBJO.js → FJBULQ4P.js} +1 -1
  32. package/lib/chunks/{IQLQH23V.js → FLL7MTDS.js} +1 -1
  33. package/lib/chunks/{CRJEY7P3.js → FM7Y6KMP.js} +1 -1
  34. package/lib/chunks/{CR5TYFBN.js → FMMOQ6SE.js} +2 -2
  35. package/lib/chunks/{CZX7WE2R.js → FRC2YJS4.js} +1 -1
  36. package/lib/chunks/{W7TN2XCW.js → GHJGHHQP.js} +1 -1
  37. package/lib/chunks/{BH4FA7AH.js → GKPSJEG5.js} +1 -1
  38. package/lib/chunks/{UVKUPD4U.js → GMMFZFKJ.js} +1 -1
  39. package/lib/chunks/{MJ2NI6MR.js → GW4PLLFM.js} +1 -1
  40. package/lib/chunks/{5AVTCY6F.js → HIMEF7J2.js} +1 -1
  41. package/lib/chunks/{U4MI7HDM.js → HJ5KS42K.js} +1 -1
  42. package/lib/chunks/{DZT5NLUB.js → HXOTKK2E.js} +1 -1
  43. package/lib/chunks/{HP7UD4BA.js → IFGXTL5P.js} +1 -1
  44. package/lib/chunks/{XQEXWC5L.js → IHUHQ7PO.js} +1 -1
  45. package/lib/chunks/{3FR3456Q.js → IJYEZR53.js} +1 -1
  46. package/lib/chunks/{JO76DPXR.js → IKSSZYHA.js} +1 -1
  47. package/lib/chunks/{I4TK6NG7.js → IMNPDC6N.js} +1 -1
  48. package/lib/chunks/{7JS7JGWP.js → IO7M7MGV.js} +1 -1
  49. package/lib/chunks/{LY6BDOEF.js → J3TXBQDL.js} +1 -1
  50. package/lib/chunks/{HRZLSSOF.js → JZV7EL4R.js} +1 -1
  51. package/lib/chunks/{EHJ3FMW6.js → K6HV4EJS.js} +1 -1
  52. package/lib/chunks/{OFIQ2EST.js → KVXVR5Y4.js} +33 -33
  53. package/lib/chunks/{LKNL5LEV.js → L4HVZOFP.js} +1 -1
  54. package/lib/chunks/{Z6GFQV2Y.js → L5AG5NJZ.js} +1 -1
  55. package/lib/chunks/{ZXOX7NF2.js → LTJXST6M.js} +1 -1
  56. package/lib/chunks/MO6NEDYO.js +17 -0
  57. package/lib/chunks/{FDKY4PGX.js → NDECHUWV.js} +1 -1
  58. package/lib/chunks/{HMYFTLS7.js → NFGCHP5P.js} +1 -1
  59. package/lib/chunks/{GFSMOHUE.js → NNBSSORJ.js} +1 -1
  60. package/lib/chunks/{ADACRYTN.js → OLAN2CVV.js} +1 -1
  61. package/lib/chunks/{CER2D6ZD.js → OOKU5T7P.js} +7 -7
  62. package/lib/chunks/{U2F3JOT2.js → OQRUWVII.js} +1 -1
  63. package/lib/chunks/{WO6IIVA3.js → Q5WCA45H.js} +8 -8
  64. package/lib/chunks/{6X42UQ3O.js → QGKTPGO7.js} +1 -1
  65. package/lib/chunks/{DM4EUYBB.js → QQ2PGWE6.js} +2 -2
  66. package/lib/chunks/QYBWIG5Y.js +59 -0
  67. package/lib/chunks/{QTSECOQB.js → RC4LK3ZS.js} +2 -2
  68. package/lib/chunks/{NXREEI2S.js → RL2EIWFM.js} +1 -1
  69. package/lib/chunks/{4S6LRPCX.js → RQMFL6Q4.js} +1 -1
  70. package/lib/chunks/S3B562IT.js +7 -0
  71. package/lib/chunks/{P5T6BIKW.js → SPIFPTTE.js} +1 -1
  72. package/lib/chunks/SUQ7ZWYJ.js +7 -0
  73. package/lib/chunks/{VJZDR6KS.js → TFPMT2XD.js} +1529 -4
  74. package/lib/chunks/{FAFMGLT6.js → TVUYXOSQ.js} +1 -1
  75. package/lib/chunks/{T77BOGYV.js → UFDBFVRI.js} +1 -1
  76. package/lib/chunks/{HTFKP5HH.js → UH4Q6U6X.js} +1 -1
  77. package/lib/chunks/{LET2MPMU.js → UKVHQIBO.js} +1 -1
  78. package/lib/chunks/{SSEMMWPY.js → VF2YOZ3U.js} +1 -1
  79. package/lib/chunks/{5HPO77VR.js → VO775J6V.js} +2 -2
  80. package/lib/chunks/WKBXTA7Y.js +53 -0
  81. package/lib/chunks/{YWRFPTTI.js → WRGAJXOK.js} +1 -1
  82. package/lib/chunks/{3EQNI6CT.js → XKENZH7W.js} +1 -1
  83. package/lib/chunks/{QXXAC25O.js → YB52VXMW.js} +1 -1
  84. package/lib/chunks/{VPTYPV6P.js → ZSQF6YOF.js} +1 -1
  85. package/lib/chunks/{TH44RKLO.js → ZYHLWT2C.js} +1 -1
  86. package/lib/eject-assets/AGENTS.md +1 -1
  87. package/lib/eject-assets/generators/cli/docs.ts +1 -1
  88. package/lib/eject-assets/generators/cli/engine-source.ts +1 -1
  89. package/lib/eject-assets/generators/cli/index.ts +2 -2
  90. package/lib/eject-assets/generators/cli/render.ts +1 -1
  91. package/lib/eject-assets/generators/go/client.ts +1 -1
  92. package/lib/eject-assets/generators/go/descriptor.ts +1 -1
  93. package/lib/eject-assets/generators/go/index.ts +2 -2
  94. package/lib/eject-assets/generators/go/models.ts +1 -1
  95. package/lib/eject-assets/generators/go/naming.ts +1 -1
  96. package/lib/eject-assets/generators/go/operations.ts +1 -1
  97. package/lib/eject-assets/generators/go/pagination.ts +1 -1
  98. package/lib/eject-assets/generators/go/types.ts +1 -1
  99. package/lib/eject-assets/generators/java/client.ts +66 -0
  100. package/lib/eject-assets/generators/java/descriptor.ts +72 -0
  101. package/lib/eject-assets/generators/java/index.ts +245 -0
  102. package/lib/eject-assets/generators/java/models.ts +362 -0
  103. package/lib/eject-assets/generators/java/naming.ts +135 -0
  104. package/lib/eject-assets/generators/java/operations.ts +300 -0
  105. package/lib/eject-assets/generators/java/pagination.ts +94 -0
  106. package/lib/eject-assets/generators/java/types.ts +103 -0
  107. package/lib/eject-assets/generators/mock/faker.ts +1 -1
  108. package/lib/eject-assets/generators/mock/index.ts +2 -2
  109. package/lib/eject-assets/generators/mock/render.ts +1 -1
  110. package/lib/eject-assets/generators/mock/sample.ts +1 -1
  111. package/lib/eject-assets/generators/mock/values.ts +1 -1
  112. package/lib/eject-assets/generators/php/client.ts +1 -1
  113. package/lib/eject-assets/generators/php/descriptor.ts +1 -1
  114. package/lib/eject-assets/generators/php/index.ts +2 -2
  115. package/lib/eject-assets/generators/php/models.ts +1 -1
  116. package/lib/eject-assets/generators/php/naming.ts +1 -1
  117. package/lib/eject-assets/generators/php/operations.ts +1 -1
  118. package/lib/eject-assets/generators/php/pagination.ts +1 -1
  119. package/lib/eject-assets/generators/php/types.ts +1 -1
  120. package/lib/eject-assets/generators/python/client.ts +1 -1
  121. package/lib/eject-assets/generators/python/descriptor.ts +1 -1
  122. package/lib/eject-assets/generators/python/index.ts +2 -2
  123. package/lib/eject-assets/generators/python/models.ts +1 -1
  124. package/lib/eject-assets/generators/python/naming.ts +1 -1
  125. package/lib/eject-assets/generators/python/operations.ts +1 -1
  126. package/lib/eject-assets/generators/python/pagination.ts +1 -1
  127. package/lib/eject-assets/generators/python/types.ts +1 -1
  128. package/lib/eject-assets/generators/swr/index.ts +2 -2
  129. package/lib/eject-assets/generators/swr/render.ts +1 -1
  130. package/lib/eject-assets/generators/tanstack-query/index.ts +2 -2
  131. package/lib/eject-assets/generators/tanstack-query/render.ts +1 -1
  132. package/lib/eject-assets/generators/transformers/index.ts +2 -2
  133. package/lib/eject-assets/generators/transformers/render.ts +1 -1
  134. package/lib/eject-assets/generators/typescript/banner.ts +1 -1
  135. package/lib/eject-assets/generators/typescript/client.ts +1 -1
  136. package/lib/eject-assets/generators/typescript/descriptor.ts +1 -1
  137. package/lib/eject-assets/generators/typescript/index.ts +2 -2
  138. package/lib/eject-assets/generators/typescript/inline-runtime.ts +1 -1
  139. package/lib/eject-assets/generators/typescript/operation-signature.ts +1 -1
  140. package/lib/eject-assets/generators/typescript/operation-types.ts +1 -1
  141. package/lib/eject-assets/generators/typescript/operations.ts +1 -1
  142. package/lib/eject-assets/generators/typescript/response-headers.ts +1 -1
  143. package/lib/eject-assets/generators/typescript/type-guards.ts +1 -1
  144. package/lib/eject-assets/generators/typescript/types.ts +1 -1
  145. package/lib/eject-assets/generators/zod/index.ts +2 -2
  146. package/lib/eject-assets/generators/zod/schemas.ts +1 -1
  147. package/lib/eject-assets/skills/client-generators/SKILL.md +1 -1
  148. package/lib/eject-assets/skills/java-generator/SKILL.md +161 -0
  149. package/lib/index.js +91 -90
  150. package/lib/skills/redocly-cli/SKILL.md +3 -1
  151. package/package.json +1 -1
  152. package/lib/chunks/2ZEKFWJQ.js +0 -13
  153. package/lib/chunks/3VOQMXIZ.js +0 -7
  154. package/lib/chunks/7GADNOVY.js +0 -9
  155. package/lib/chunks/FRQHCFKP.js +0 -199
  156. package/lib/chunks/J2EPFEBO.js +0 -17
  157. package/lib/chunks/JZATGVUO.js +0 -53
  158. package/lib/chunks/KNMNSDHR.js +0 -7
  159. package/lib/chunks/U5DIFWGH.js +0 -7
@@ -0,0 +1,72 @@
1
+ // Ejected from @redocly/client-generator@0.8.0 — the built-in "java" generator.
2
+ // This file is yours: edit freely; the generated client stays machine-owned and is
3
+ // rebuilt by `redocly generate-client`. Newer generator versions merge in with
4
+ // `redocly eject-generator java --update`.
5
+ // The `descriptor` stage: the per-operation `Operation` literal — security
6
+ // OR-alternatives and the pagination spec — and the envelope-header coerce specs.
7
+
8
+ import {
9
+ type ApiModel,
10
+ headerCoerceType,
11
+ type NeutralPaginationRule,
12
+ type OperationModel,
13
+ securityRequirements,
14
+ uniqueIdentifiers,
15
+ } from '@redocly/client-generator';
16
+
17
+ import { JAVA, javaString } from './naming.ts';
18
+
19
+ /** `List.of(List.of(SecuritySpec…))` for the operation's security OR-alternatives. */
20
+ export function javaSecurityLiteral(op: OperationModel, model: ApiModel): string {
21
+ const alternatives = securityRequirements(op, model).map((alternative) => {
22
+ const specs = alternative.map((spec) =>
23
+ spec.kind === 'apiKey'
24
+ ? `new SecuritySpec(${javaString(spec.scheme)}, "apiKey", ${javaString(spec.name)}, ${javaString(spec.in)})`
25
+ : `SecuritySpec.of(${javaString(spec.scheme)}, ${javaString(spec.kind)})`
26
+ );
27
+ return `List.of(${specs.join(', ')})`;
28
+ });
29
+ return `List.of(${alternatives.join(', ')})`;
30
+ }
31
+
32
+ /** The neutral rule as a `new PaginationSpec(…)` literal, or `null` when the operation does not paginate. */
33
+ export function javaPaginationLiteral(rule: NeutralPaginationRule | undefined): string {
34
+ if (rule === undefined) return 'null';
35
+ const field = (value: string | undefined) => (value === undefined ? 'null' : javaString(value));
36
+ return `new PaginationSpec(${[
37
+ javaString(rule.style),
38
+ field(rule.param),
39
+ field(rule.nextCursor),
40
+ field(rule.hasMore),
41
+ field(rule.nextLink),
42
+ field(rule.limitParam),
43
+ field(rule.items),
44
+ ].join(', ')})`;
45
+ }
46
+
47
+ /**
48
+ * The operation as the runtime describes it, built where it is used: a table of every
49
+ * operation in one static initializer would exceed the JVM's method size on a large
50
+ * description, and the record is a trivial allocation.
51
+ */
52
+ export function javaOperationLiteral(
53
+ op: OperationModel,
54
+ model: ApiModel,
55
+ rule: NeutralPaginationRule | undefined
56
+ ): string {
57
+ return `new Operation(${javaString(op.specName ?? op.name)}, ${javaString(op.method.toUpperCase())}, ${javaString(op.path)}, ${javaSecurityLiteral(op, model)}, ${javaPaginationLiteral(rule)})`;
58
+ }
59
+
60
+ /** Declared response headers as runtime coerce specs: `{wire name, camelCase key, type}`. */
61
+ export function envelopeHeaderSpecs(op: OperationModel, model: ApiModel): string {
62
+ const headers = op.successResponseHeaders ?? [];
63
+ const keys = uniqueIdentifiers(
64
+ headers.map((header) => header.name),
65
+ { style: 'camel', reserved: JAVA }
66
+ );
67
+ const specs = headers.map((header, index) => {
68
+ const type = headerCoerceType(header.schema, model);
69
+ return `{${javaString(header.name)}, ${javaString(keys[index])}, ${javaString(type)}}`;
70
+ });
71
+ return `new String[][] {${specs.join(', ')}}`;
72
+ }
@@ -0,0 +1,245 @@
1
+ // Ejected from @redocly/client-generator@0.8.0 — the built-in "java" generator.
2
+ // This file is yours: edit freely; the generated client stays machine-owned and is
3
+ // rebuilt by `redocly generate-client`. Newer generator versions merge in with
4
+ // `redocly eject-generator java --update`.
5
+ // The built-in `java` generator — the fourth non-TypeScript library entry, authored
6
+ // with the language-neutral toolkit only (same dogfooding invariant as python/go/php,
7
+ // pinned by the guard test). Output is one Java >= 17 file over java.net.http: a public
8
+ // class named after the output stem that nests the records (with fromJson/toJson
9
+ // hydration), wire-backed enums, sealed union interfaces, the embedded runtime, and one
10
+ // typed method per operation. Exceptions are the error mode (`errorMode` does not apply).
11
+
12
+ import {
13
+ type CodeSample,
14
+ type Generator,
15
+ identifierFor,
16
+ jsonSuccessSchema,
17
+ type NeutralPaginationRule,
18
+ type OperationModel,
19
+ renderReferencePage,
20
+ type SampleContext,
21
+ sseResponse,
22
+ } from '@redocly/client-generator';
23
+ import { JavaPrinter } from '@redocly/client-generator/printers/java';
24
+ import { JAVA_RUNTIME_SOURCE } from '@redocly/client-generator/runtime-sources';
25
+ import { logger } from '@redocly/openapi-core';
26
+
27
+ import { writeServers } from './client.ts';
28
+ import { writeJavaModels } from './models.ts';
29
+ import {
30
+ JAVA,
31
+ javaClassName,
32
+ javaPackageName,
33
+ javaString,
34
+ methodIdents,
35
+ plainPackageName,
36
+ } from './naming.ts';
37
+ import { paramsClassName, writeJavaMethod, writeParamsClass } from './operations.ts';
38
+ import { writeJavaPaginationWrappers } from './pagination.ts';
39
+ import { javaType } from './types.ts';
40
+
41
+ export { renderJavaModels } from './models.ts';
42
+ export { javaType } from './types.ts';
43
+
44
+ /** The `import` lines of the runtime, which the generated file declares for both of them. */
45
+ function runtimeImports(): string[] {
46
+ return JAVA_RUNTIME_SOURCE.split('\n').filter((line) => line.startsWith('import '));
47
+ }
48
+
49
+ /**
50
+ * The runtime's class body — everything between its class line and its closing brace, at
51
+ * the one-level indent the generated class also uses — for inline embedding.
52
+ */
53
+ function runtimeBody(): string[] {
54
+ const lines = JAVA_RUNTIME_SOURCE.split('\n');
55
+ const start = lines.findIndex((line) => line.startsWith('abstract class Runtime {'));
56
+ const end = lines.lastIndexOf('}');
57
+ return lines
58
+ .slice(start + 1, end)
59
+ .map((line) => (line.startsWith(' ') ? line.slice(4) : line));
60
+ }
61
+
62
+ /** The whole generated file: package, imports, and the class nesting models, runtime, and methods. */
63
+ export const javaGenerator: Generator = ({ model, output, banner, emit, pagination }) => {
64
+ const printer = new JavaPrinter();
65
+ const dateType = emit.dateType ?? 'string';
66
+ const className = javaClassName(output.stem, model);
67
+ const packageName = javaPackageName(model.title);
68
+ // A package the reader cannot see in the title would surprise the code that imports it.
69
+ if (packageName !== plainPackageName(model.title)) {
70
+ logger.warn(
71
+ `generate-client: the API title ${JSON.stringify(model.title)} cannot be a Java package as is, so the java SDK uses package "${packageName}" — rename the API title in the description to choose another.\n`
72
+ );
73
+ }
74
+ const moduleRuntime = emit.runtime === 'module';
75
+ const runtimeClass = `${className}Runtime`;
76
+ const operations = model.services.flatMap((service) => service.operations);
77
+ const idents = methodIdents(model);
78
+ // Pagination arrives RESOLVED from the pipeline — one fit-verified answer per run.
79
+ const paginationRules = new Map<string, NeutralPaginationRule>();
80
+ for (const op of operations) {
81
+ const spec = pagination?.get(op.name)?.spec;
82
+ if (spec !== undefined) paginationRules.set(op.name, spec);
83
+ }
84
+
85
+ printer.line(
86
+ `// Code generated by @redocly/client-generator (java) from ${javaString(model.title)} ${model.version}. DO NOT EDIT.`
87
+ );
88
+ printer.line(
89
+ '// Regenerate with `redocly generate-client`. Java >= 17, standard library only — zero dependencies.'
90
+ );
91
+ printer.blank();
92
+ printer.line(`package ${packageName};`);
93
+ printer.blank();
94
+ for (const line of runtimeImports()) printer.line(line);
95
+ printer.blank();
96
+ printer.doc(className, `Client for ${model.title} (${model.version}).`);
97
+ printer.block(
98
+ `public final class ${className}${moduleRuntime ? ` extends ${runtimeClass}` : ''} {`,
99
+ () => {
100
+ writeJavaModels(printer, model, dateType);
101
+ writeServers(printer, model);
102
+ if (!moduleRuntime) {
103
+ printer.line('// ─── Embedded runtime (@redocly/client-generator java runtime) ───');
104
+ for (const line of runtimeBody()) printer.line(line);
105
+ printer.blank();
106
+ }
107
+ for (const op of operations) {
108
+ writeParamsClass(printer, op, idents.get(op.name)!, model, dateType);
109
+ }
110
+ printer.line('private final Config config;');
111
+ printer.blank();
112
+ printer.block(
113
+ `public ${className}(Config config) {`,
114
+ () => {
115
+ printer.line('this.config = config;');
116
+ printer.block(
117
+ 'if (config.serverUrl.isEmpty()) {',
118
+ () => {
119
+ printer.line(
120
+ `config.serverUrl = ${javaString(emit.serverUrl ?? model.serverUrl ?? '')};`
121
+ );
122
+ },
123
+ '}'
124
+ );
125
+ },
126
+ '}'
127
+ );
128
+ printer.blank();
129
+ printer.block(
130
+ `public ${className}() {`,
131
+ () => {
132
+ printer.line('this(new Config());');
133
+ },
134
+ '}'
135
+ );
136
+ printer.blank();
137
+
138
+ for (const op of operations) {
139
+ const ident = idents.get(op.name)!;
140
+ const rule = paginationRules.get(op.name);
141
+ writeJavaMethod(printer, op, ident, model, dateType, rule);
142
+ if (sseResponse(op) === undefined && (op.successResponseHeaders?.length ?? 0) > 0) {
143
+ writeJavaMethod(printer, op, ident, model, dateType, rule, true);
144
+ }
145
+ if (rule !== undefined) {
146
+ writeJavaPaginationWrappers(printer, op, ident, model, dateType, rule);
147
+ }
148
+ }
149
+ },
150
+ '}'
151
+ );
152
+
153
+ const entry = {
154
+ path: output.path.replace(/[^\\/]+$/, `${className}.java`),
155
+ content: printer.toString(),
156
+ };
157
+ if (!moduleRuntime) return [entry];
158
+ // The runtime as a real file: the same source, as a public base class the client extends,
159
+ // so every runtime name resolves unqualified in both runtime modes.
160
+ const header = banner.map((line) => `// ${line}`).join('\n');
161
+ const runtimeSource = JAVA_RUNTIME_SOURCE.replace(
162
+ /^abstract class Runtime \{$/m,
163
+ `public abstract class ${runtimeClass} {`
164
+ );
165
+ return [
166
+ entry,
167
+ {
168
+ path: output.path.replace(/[^\\/]+$/, `${runtimeClass}.java`),
169
+ content: `${header}\npackage ${packageName};\n\n${runtimeSource
170
+ .replace(/^\/\/.*\n/gm, '')
171
+ .trimStart()
172
+ .trimEnd()}\n`,
173
+ },
174
+ ];
175
+ };
176
+
177
+ /** One idiomatic Java call per operation — feeds `x-codeSamples` for docs. */
178
+ export function javaSample(op: OperationModel, ctx: SampleContext): CodeSample {
179
+ const dateType = ctx.emit.dateType ?? 'string';
180
+ const stem = ctx.outputPath.replace(/^.*[\\/]/, '').replace(/\.[^.]+$/, '');
181
+ const className = javaClassName(stem, ctx.model);
182
+ const ident = methodIdents(ctx.model).get(op.name) ?? identifierFor(op.name, { reserved: JAVA });
183
+ const args = [
184
+ ...op.pathParams.map((param) =>
185
+ javaString(`<${identifierFor(param.name, { reserved: JAVA })}>`)
186
+ ),
187
+ ...(op.requestBody ? ['body'] : []),
188
+ ...(paramsClassName(op, ident, ctx.model) === undefined
189
+ ? []
190
+ : [`new ${className}.${paramsClassName(op, ident, ctx.model)}()`]),
191
+ ];
192
+ const call = `client.${ident}(${args.join(', ')})`;
193
+ // The statement matches the return shape: a void method has no result, an SSE method is
194
+ // iterated, and a typed method binds its result.
195
+ const statement =
196
+ sseResponse(op) !== undefined
197
+ ? `for (var event : ${call}) {\n System.out.println(event.data());\n}`
198
+ : jsonSuccessSchema(op) === undefined
199
+ ? `${call};`
200
+ : `${javaType(jsonSuccessSchema(op)!, ctx.model, dateType)} result = ${call};`;
201
+ // The import names the package this run writes, so the snippet compiles as shown.
202
+ return {
203
+ lang: 'java',
204
+ label: 'Java SDK',
205
+ source: `import ${javaPackageName(ctx.model.title)}.${className};\n\n${className} client = new ${className}(new ${className}.Config());\n${statement}\n`,
206
+ };
207
+ }
208
+
209
+ /**
210
+ * The SDK's own reference page, written when `client.docs` is on. The call snippets come
211
+ * from `javaSample` — this generator's own hook — so the page can only ever show the syntax
212
+ * of the SDK beside it, and ejecting this generator takes the page with it.
213
+ */
214
+ export const javaDocs: Generator = ({ model, output, emit, pagination }) => [
215
+ {
216
+ path: output.path.replace(/\.[^.\\/]+$/, '.java.md'),
217
+ content: renderReferencePage(model, {
218
+ title: `${model.title} Java SDK reference`,
219
+ frontmatter: emit.docsFrontmatter === true,
220
+ language: {
221
+ name: 'java',
222
+ label: 'Java',
223
+ fence: 'java',
224
+ requires: 'The SDK needs Java 17 or newer and the standard library only.',
225
+ },
226
+ sample: (op) => javaSample(op, { model, emit, outputPath: output.path }),
227
+ paginated: new Set(pagination?.keys() ?? []),
228
+ }),
229
+ },
230
+ ];
231
+
232
+ export default {
233
+ name: 'java',
234
+ run: javaGenerator,
235
+ sample: javaSample,
236
+ docs: javaDocs,
237
+ errorModes: ["throw"],
238
+ notApplicable: {
239
+ "outputMode": "it always emits one self-contained file",
240
+ "runtime": "the runtime is always embedded in the generated file",
241
+ "argsStyle": "inputs follow the target language's own idiom",
242
+ "importExt": "the generated file has no relative imports"
243
+ },
244
+ requiresGenerator: '^0.8.0',
245
+ };
@@ -0,0 +1,362 @@
1
+ // Ejected from @redocly/client-generator@0.8.0 — the built-in "java" generator.
2
+ // This file is yours: edit freely; the generated client stays machine-owned and is
3
+ // rebuilt by `redocly generate-client`. Newer generator versions merge in with
4
+ // `redocly eject-generator java --update`.
5
+ // The `models` stage: named schemas as records with fromJson/toJson hydration,
6
+ // wire-backed enums, and sealed union interfaces with a parse dispatcher — plus the
7
+ // wire→typed value expressions the methods reuse.
8
+
9
+ import {
10
+ type ApiModel,
11
+ type DateType,
12
+ deref,
13
+ discriminatorCases,
14
+ enumValues,
15
+ flattenAllOf,
16
+ type PropertyModel,
17
+ type SchemaModel,
18
+ uniqueIdentifiers,
19
+ unwrapNullable,
20
+ } from '@redocly/client-generator';
21
+ import { JavaPrinter } from '@redocly/client-generator/printers/java';
22
+
23
+ import { JAVA, javaString, typeName } from './naming.ts';
24
+ import { classify, isDateFormat, javaType } from './types.ts';
25
+
26
+ /**
27
+ * Wire value → typed value expression over the runtime's JSON graph, or undefined when
28
+ * the raw `Object` is already the type. `depth` names the lambda parameters of nested
29
+ * collections (`item`, `item2`, …): Java rejects a lambda parameter that shadows one.
30
+ */
31
+ export function hydration(
32
+ schema: SchemaModel,
33
+ expr: string,
34
+ model: ApiModel,
35
+ // Required on purpose: a defaulted `'string'` let a call site forget it, and the method
36
+ // then returned a raw string where its own signature declared `OffsetDateTime`.
37
+ dateType: DateType,
38
+ depth = 0
39
+ ): string | undefined {
40
+ const bare = unwrapNullable(schema);
41
+ switch (bare.kind) {
42
+ case 'scalar':
43
+ if (dateType === 'Date' && bare.scalar === 'string' && isDateFormat(bare)) {
44
+ return bare.metadata?.format === 'date'
45
+ ? `asLocalDate(${expr})`
46
+ : `asOffsetDateTime(${expr})`;
47
+ }
48
+ return `${SCALAR_READERS[bare.scalar]}(${expr})`;
49
+ case 'enum':
50
+ return `${SCALAR_READERS[bare.scalar]}(${expr})`;
51
+ case 'literal':
52
+ return `${SCALAR_READERS[typeof bare.value === 'string' ? 'string' : typeof bare.value === 'boolean' ? 'boolean' : 'number']}(${expr})`;
53
+ case 'omit':
54
+ return hydration({ kind: 'ref', name: bare.base }, expr, model, dateType, depth);
55
+ case 'ref': {
56
+ const kind = classify(bare.name, model);
57
+ if (kind === 'record') return `${typeName(bare.name)}.fromJson(${expr})`;
58
+ if (kind === 'enum') {
59
+ const reader = enumValues(deref(bare, model)!)!.scalar === 'string' ? 'asString' : 'asLong';
60
+ return `${typeName(bare.name)}.fromWire(${reader}(${expr}))`;
61
+ }
62
+ if (kind === 'union') return `parse${typeName(bare.name)}(${expr})`;
63
+ const target = deref(bare, model);
64
+ return target === undefined ? undefined : hydration(target, expr, model, dateType, depth);
65
+ }
66
+ case 'array':
67
+ case 'record': {
68
+ const item = depth === 0 ? 'item' : `item${depth + 1}`;
69
+ const inner =
70
+ hydration(
71
+ bare.kind === 'array' ? bare.items : bare.value,
72
+ item,
73
+ model,
74
+ dateType,
75
+ depth + 1
76
+ ) ?? item;
77
+ return `${bare.kind === 'array' ? 'asList' : 'asMap'}(${expr}, ${item} -> ${inner})`;
78
+ }
79
+ case 'union':
80
+ case 'null':
81
+ case 'object':
82
+ case 'intersection':
83
+ case 'unknown':
84
+ return undefined;
85
+ }
86
+ }
87
+
88
+ const SCALAR_READERS = {
89
+ string: 'asString',
90
+ integer: 'asLong',
91
+ number: 'asDouble',
92
+ boolean: 'asBoolean',
93
+ };
94
+
95
+ /** The record components may not collide with the record's own methods or the locals of fromJson/toJson. */
96
+ // prettier-ignore
97
+ const RECORD_MEMBER_SLOTS = [
98
+ 'data', 'json', 'toJson', 'fromJson', 'hashCode', 'equals', 'toString', 'getClass', 'wait',
99
+ 'notify', 'notifyAll', 'clone', 'finalize',
100
+ ];
101
+
102
+ /** The sealed unions a record is a member of, so it declares `implements` for each. */
103
+ function unionsOf(name: string, model: ApiModel): string[] {
104
+ const unions: string[] = [];
105
+ for (const candidate of model.schemas) {
106
+ if (classify(candidate.name, model) !== 'union') continue;
107
+ const cases = discriminatorCases(candidate.schema, model)!;
108
+ if (cases.cases.some((entry) => entry.schemaName === name)) {
109
+ unions.push(typeName(candidate.name));
110
+ }
111
+ }
112
+ return unions;
113
+ }
114
+
115
+ function writeRecord(
116
+ printer: JavaPrinter,
117
+ name: string,
118
+ properties: PropertyModel[],
119
+ model: ApiModel,
120
+ dateType: DateType,
121
+ description?: string
122
+ ): void {
123
+ // A response never carries a `writeOnly` property, and responses decode into this same
124
+ // record, so the property is optional even when the description requires it.
125
+ const fields = properties.map((property) =>
126
+ property.writeOnly ? { ...property, required: false } : property
127
+ );
128
+ const components = uniqueIdentifiers(
129
+ fields.map((property) => property.name),
130
+ { style: 'camel', reserved: JAVA, taken: RECORD_MEMBER_SLOTS }
131
+ );
132
+ const record = typeName(name);
133
+ const interfaces = ['JsonValue', ...unionsOf(name, model)];
134
+ printer.doc(record, description);
135
+ printer.block(
136
+ `public record ${record}(`,
137
+ () => {
138
+ fields.forEach((property, index) => {
139
+ const comma = index < fields.length - 1 ? ',' : '';
140
+ printer.line(`${javaType(property.schema, model, dateType)} ${components[index]}${comma}`);
141
+ });
142
+ },
143
+ `) implements ${interfaces.join(', ')} {`
144
+ );
145
+ printer.indent(() => {
146
+ printer.block(
147
+ `public static ${record} fromJson(Object json) {`,
148
+ () => {
149
+ printer.line('Map<String, Object> data = asObject(json);');
150
+ printer.block(
151
+ 'if (data == null) {',
152
+ () => {
153
+ printer.line('return null;');
154
+ },
155
+ '}'
156
+ );
157
+ printer.block(
158
+ `return new ${record}(`,
159
+ () => {
160
+ fields.forEach((property, index) => {
161
+ const raw = `data.get(${javaString(property.name)})`;
162
+ const comma = index < fields.length - 1 ? ',' : '';
163
+ printer.line(`${hydration(property.schema, raw, model, dateType) ?? raw}${comma}`);
164
+ });
165
+ },
166
+ ');'
167
+ );
168
+ },
169
+ '}'
170
+ );
171
+ printer.blank();
172
+ printer.line('@Override');
173
+ printer.block(
174
+ 'public Map<String, Object> toJson() {',
175
+ () => {
176
+ printer.line('Map<String, Object> data = new LinkedHashMap<>();');
177
+ fields.forEach((property, index) => {
178
+ const put = `data.put(${javaString(property.name)}, ${components[index]});`;
179
+ if (property.required) {
180
+ printer.line(put);
181
+ } else {
182
+ printer.block(
183
+ `if (${components[index]} != null) {`,
184
+ () => {
185
+ printer.line(put);
186
+ },
187
+ '}'
188
+ );
189
+ }
190
+ });
191
+ printer.line('return data;');
192
+ },
193
+ '}'
194
+ );
195
+ });
196
+ printer.line('}');
197
+ printer.blank();
198
+ }
199
+
200
+ function writeEnum(
201
+ printer: JavaPrinter,
202
+ name: string,
203
+ values: Array<string | number | boolean>,
204
+ scalar: 'string' | 'integer',
205
+ description?: string
206
+ ): void {
207
+ const enumName = typeName(name);
208
+ const wireType = scalar === 'string' ? 'String' : 'Long';
209
+ const members = enumValues({ kind: 'enum', values, scalar })!.memberNames;
210
+ printer.doc(enumName, description);
211
+ printer.block(
212
+ `public enum ${enumName} implements WireValue {`,
213
+ () => {
214
+ values.forEach((value, index) => {
215
+ const literal = typeof value === 'string' ? javaString(value) : `${value}L`;
216
+ printer.line(`${members[index]}(${literal})${index < values.length - 1 ? ',' : ';'}`);
217
+ });
218
+ printer.blank();
219
+ printer.line(`private final ${wireType} wire;`);
220
+ printer.blank();
221
+ printer.block(
222
+ `${enumName}(${wireType} wire) {`,
223
+ () => {
224
+ printer.line('this.wire = wire;');
225
+ },
226
+ '}'
227
+ );
228
+ printer.blank();
229
+ printer.line('@Override');
230
+ printer.block(
231
+ `public ${wireType} wire() {`,
232
+ () => {
233
+ printer.line('return wire;');
234
+ },
235
+ '}'
236
+ );
237
+ printer.blank();
238
+ printer.line(
239
+ '/** The member for a wire value; null for null, an exception for an unknown value. */'
240
+ );
241
+ printer.block(
242
+ `public static ${enumName} fromWire(${wireType} wire) {`,
243
+ () => {
244
+ printer.block(
245
+ 'if (wire == null) {',
246
+ () => {
247
+ printer.line('return null;');
248
+ },
249
+ '}'
250
+ );
251
+ printer.block(
252
+ `for (${enumName} member : values()) {`,
253
+ () => {
254
+ printer.block(
255
+ 'if (member.wire.equals(wire)) {',
256
+ () => {
257
+ printer.line('return member;');
258
+ },
259
+ '}'
260
+ );
261
+ },
262
+ '}'
263
+ );
264
+ printer.line(`throw new IllegalArgumentException("Unknown ${enumName} value: " + wire);`);
265
+ },
266
+ '}'
267
+ );
268
+ },
269
+ '}'
270
+ );
271
+ printer.blank();
272
+ }
273
+
274
+ /** Write every named schema at the printer's current depth: records, enums, union dispatchers. */
275
+ export function writeJavaModels(
276
+ printer: JavaPrinter,
277
+ model: ApiModel,
278
+ dateType: DateType = 'string'
279
+ ): void {
280
+ for (const { name, schema } of model.schemas) {
281
+ const asEnum = enumValues(schema);
282
+ if (asEnum !== undefined && (asEnum.scalar === 'string' || asEnum.scalar === 'integer')) {
283
+ writeEnum(printer, name, asEnum.values, asEnum.scalar, schema.description);
284
+ continue;
285
+ }
286
+ if (schema.kind === 'object' || schema.kind === 'intersection') {
287
+ const flat = flattenAllOf(schema, model);
288
+ if (flat !== undefined) {
289
+ writeRecord(
290
+ printer,
291
+ name,
292
+ flat.properties,
293
+ model,
294
+ dateType,
295
+ flat.description ?? schema.description
296
+ );
297
+ continue;
298
+ }
299
+ }
300
+ const cases = discriminatorCases(schema, model);
301
+ if (cases !== undefined) {
302
+ const union = typeName(name);
303
+ const sealed = classify(name, model) === 'union';
304
+ const table = cases.cases
305
+ .map((entry) => `${entry.value} -> ${typeName(entry.schemaName)}`)
306
+ .join(', ');
307
+ // The table carries the description's own values, so it goes through the comment escaper.
308
+ printer.doc(union, `A discriminated union on ${javaString(cases.property)}: ${table}.`);
309
+ if (sealed) {
310
+ // The permitted records sit in the same compilation unit, so `permits` is implied.
311
+ printer.line(`public sealed interface ${union} extends JsonValue {}`);
312
+ printer.blank();
313
+ }
314
+ // A target without a record of its own keeps the raw value, so the union is `Object`.
315
+ printer.block(
316
+ `public static ${sealed ? union : 'Object'} parse${union}(Object json) {`,
317
+ () => {
318
+ printer.line('Map<String, Object> data = asObject(json);');
319
+ printer.block(
320
+ 'if (data == null) {',
321
+ () => {
322
+ printer.line('return null;');
323
+ },
324
+ '}'
325
+ );
326
+ printer.block(
327
+ `switch (String.valueOf(data.get(${javaString(cases.property)}))) {`,
328
+ () => {
329
+ for (const entry of cases.cases) {
330
+ const target = { kind: 'ref', name: entry.schemaName } as const;
331
+ const typed = hydration(target, 'data', model, dateType) ?? 'data';
332
+ printer.line(`case ${javaString(entry.value)}:`);
333
+ printer.indent(() => printer.line(`return ${typed};`));
334
+ }
335
+ printer.line('default:');
336
+ printer.indent(() =>
337
+ printer.line(
338
+ sealed
339
+ ? `throw new IllegalArgumentException("Unknown ${cases.property} value: " + data.get(${javaString(cases.property)}));`
340
+ : 'return data;'
341
+ )
342
+ );
343
+ },
344
+ '}'
345
+ );
346
+ },
347
+ '}'
348
+ );
349
+ printer.blank();
350
+ continue;
351
+ }
352
+ // Everything else (plain unions, aliases, records) has no Java declaration;
353
+ // references resolve to the underlying type via javaType.
354
+ }
355
+ }
356
+
357
+ /** Render every named schema at depth 0 — the models section on its own, for the render bars. */
358
+ export function renderJavaModels(model: ApiModel, dateType: DateType = 'string'): string {
359
+ const printer = new JavaPrinter();
360
+ writeJavaModels(printer, model, dateType);
361
+ return printer.toString();
362
+ }