@redocly/cli 2.71.1 → 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 (161) hide show
  1. package/README.md +1 -1
  2. package/THIRD_PARTY_NOTICES +29 -7
  3. package/lib/chunks/{Q3IJURM3.js → 2DXUH2SS.js} +1 -1
  4. package/lib/chunks/{UYZXI3NO.js → 2X2KHSF3.js} +1 -1
  5. package/lib/chunks/{CX76LEFC.js → 42X6IAVM.js} +1 -1
  6. package/lib/chunks/44US4AGQ.js +13 -0
  7. package/lib/chunks/{DP3T6VZX.js → 4AXMK6B2.js} +1 -1
  8. package/lib/chunks/4ZCKAKVF.js +199 -0
  9. package/lib/chunks/5F7XFPS7.js +8 -0
  10. package/lib/chunks/{WJ3YDHQA.js → 6234PJEZ.js} +1 -1
  11. package/lib/chunks/{TNRIBQHV.js → 62SOICN7.js} +1 -1
  12. package/lib/chunks/{BPSWCUZE.js → 6JFL6L56.js} +1 -1
  13. package/lib/chunks/{NEWT5A6S.js → 6RTITFQA.js} +1 -1
  14. package/lib/chunks/{2IUGP2IH.js → 6XDKNFYM.js} +3 -3
  15. package/lib/chunks/{IYV7EO2J.js → 76AJ5P4Y.js} +1 -1
  16. package/lib/chunks/{6TCEQRVX.js → 7HTKK26B.js} +1 -1
  17. package/lib/chunks/{BXXX3ZCV.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/{3N7LKSDD.js → BL2KWYMY.js} +1 -1
  21. package/lib/chunks/{PBQOGZ5C.js → BTLP7Z6A.js} +1 -1
  22. package/lib/chunks/{5DW3QRJB.js → BXMYSHQY.js} +2 -2
  23. package/lib/chunks/{ILYXMZRT.js → BY22HEXP.js} +1 -1
  24. package/lib/chunks/D2BBHPMR.js +7 -0
  25. package/lib/chunks/{ACAPGKSR.js → D3C6XIMY.js} +3 -3
  26. package/lib/chunks/{INGAWMLB.js → DNDYWJHC.js} +1 -1
  27. package/lib/chunks/{KJIUDCYZ.js → E6QTRG6I.js} +1 -1
  28. package/lib/chunks/EHMG7I34.js +9 -0
  29. package/lib/chunks/{MH5X2EVH.js → EL6MGCYE.js} +1 -1
  30. package/lib/chunks/EVCSNWMN.js +7 -0
  31. package/lib/chunks/{RBNQC5ZB.js → FJBULQ4P.js} +1 -1
  32. package/lib/chunks/{Y5DDX2MU.js → FLL7MTDS.js} +1 -1
  33. package/lib/chunks/{X7C43UBI.js → FM7Y6KMP.js} +1 -1
  34. package/lib/chunks/{ZQTHHPK3.js → FMMOQ6SE.js} +2 -2
  35. package/lib/chunks/{FBEEMKTB.js → FRC2YJS4.js} +1 -1
  36. package/lib/chunks/{5SSWNONM.js → GHJGHHQP.js} +1 -1
  37. package/lib/chunks/{CHX7O5AM.js → GKPSJEG5.js} +1 -1
  38. package/lib/chunks/{TN3JQHMS.js → GMMFZFKJ.js} +1 -1
  39. package/lib/chunks/{DVLD4FET.js → GW4PLLFM.js} +1 -1
  40. package/lib/chunks/{L2O63VRM.js → HIMEF7J2.js} +1 -1
  41. package/lib/chunks/{Y3LDWYZD.js → HJ5KS42K.js} +1 -1
  42. package/lib/chunks/{IZMDARCL.js → HXOTKK2E.js} +1 -1
  43. package/lib/chunks/{HBWCMTP3.js → IFGXTL5P.js} +1 -1
  44. package/lib/chunks/{NLAPR7D4.js → IHUHQ7PO.js} +1 -1
  45. package/lib/chunks/{EO65RJ4Z.js → IJYEZR53.js} +1 -1
  46. package/lib/chunks/{BOWZV5S6.js → IKSSZYHA.js} +1 -1
  47. package/lib/chunks/{QZJAXPYF.js → IMNPDC6N.js} +1 -1
  48. package/lib/chunks/{25RSTUQA.js → IO7M7MGV.js} +1 -1
  49. package/lib/chunks/{QXHFS5G6.js → J3TXBQDL.js} +1 -1
  50. package/lib/chunks/{7HFLPQAB.js → JZV7EL4R.js} +1 -1
  51. package/lib/chunks/{2NYYNBAC.js → K6HV4EJS.js} +1 -1
  52. package/lib/chunks/{WLI2XLEP.js → KVXVR5Y4.js} +33 -33
  53. package/lib/chunks/{RNA6JDRC.js → L4HVZOFP.js} +1 -1
  54. package/lib/chunks/{WCJ2HAN3.js → L5AG5NJZ.js} +1 -1
  55. package/lib/chunks/{ZBB6PS6I.js → LTJXST6M.js} +1 -1
  56. package/lib/chunks/MO6NEDYO.js +17 -0
  57. package/lib/chunks/{S6W63XAS.js → NDECHUWV.js} +1 -1
  58. package/lib/chunks/{XZIK3H6V.js → NFGCHP5P.js} +1 -1
  59. package/lib/chunks/{W2CABGMW.js → NNBSSORJ.js} +1 -1
  60. package/lib/chunks/{XGPGFZMH.js → OLAN2CVV.js} +1 -1
  61. package/lib/chunks/{TZROZMVE.js → OOKU5T7P.js} +7 -7
  62. package/lib/chunks/{DURXFJ7H.js → OQRUWVII.js} +1 -1
  63. package/lib/chunks/{HWOL3HHR.js → Q5WCA45H.js} +8 -8
  64. package/lib/chunks/{HAGYQ4BK.js → QGKTPGO7.js} +1 -1
  65. package/lib/chunks/{UMN7TKTH.js → QQ2PGWE6.js} +2 -2
  66. package/lib/chunks/QYBWIG5Y.js +59 -0
  67. package/lib/chunks/{CJWIDJTB.js → RC4LK3ZS.js} +2 -2
  68. package/lib/chunks/{O53IFFP3.js → RL2EIWFM.js} +1 -1
  69. package/lib/chunks/{5BQNATJZ.js → RQMFL6Q4.js} +1 -1
  70. package/lib/chunks/S3B562IT.js +7 -0
  71. package/lib/chunks/{EQOFXQKD.js → SPIFPTTE.js} +1 -1
  72. package/lib/chunks/SUQ7ZWYJ.js +7 -0
  73. package/lib/chunks/{BE4Z5FOC.js → TFPMT2XD.js} +1603 -16
  74. package/lib/chunks/{AH6ANNYP.js → TVUYXOSQ.js} +1 -1
  75. package/lib/chunks/{ACECCMWJ.js → UFDBFVRI.js} +1 -1
  76. package/lib/chunks/UH4Q6U6X.js +7 -0
  77. package/lib/chunks/{YL3LVBZF.js → UKVHQIBO.js} +1 -1
  78. package/lib/chunks/{4DUOPCYP.js → VF2YOZ3U.js} +1 -1
  79. package/lib/chunks/{6ZNJ7RDX.js → VO775J6V.js} +2 -2
  80. package/lib/chunks/WKBXTA7Y.js +53 -0
  81. package/lib/chunks/{RUPTJCF5.js → WRGAJXOK.js} +1 -1
  82. package/lib/chunks/{EBHQXW6Z.js → XKENZH7W.js} +1 -1
  83. package/lib/chunks/{B5SUTVG4.js → YB52VXMW.js} +1 -1
  84. package/lib/chunks/{G6SB4ZP5.js → ZSQF6YOF.js} +1 -1
  85. package/lib/chunks/{54EKLNNI.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/DORXGE7Z.js +0 -199
  153. package/lib/chunks/EAFZ6DNQ.js +0 -53
  154. package/lib/chunks/FGJDZKD2.js +0 -13
  155. package/lib/chunks/GAKWND4S.js +0 -8
  156. package/lib/chunks/JOWPA3MJ.js +0 -7
  157. package/lib/chunks/RBJCAITC.js +0 -9
  158. package/lib/chunks/RNL7TC2W.js +0 -17
  159. package/lib/chunks/U5DIFWGH.js +0 -7
  160. package/lib/chunks/U7KIB7CD.js +0 -7
  161. package/lib/chunks/V34D5CUU.js +0 -7
@@ -0,0 +1,135 @@
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 `naming` stage: the shared printer/naming instance, the string escaper, the
6
+ // enclosing class name, and the collision-free type/member/method identifiers every
7
+ // other stage builds on.
8
+
9
+ import {
10
+ type ApiModel,
11
+ casing,
12
+ identifierFor,
13
+ NotSupportedError,
14
+ type OperationModel,
15
+ RESERVED_WORDS,
16
+ } from '@redocly/client-generator';
17
+ import { JavaPrinter } from '@redocly/client-generator/printers/java';
18
+ import { JAVA_RUNTIME_SOURCE } from '@redocly/client-generator/runtime-sources';
19
+
20
+ export const JAVA = RESERVED_WORDS.java;
21
+
22
+ // Naming and escaping delegate to the printer — one implementation, one policy.
23
+ export const naming = new JavaPrinter();
24
+
25
+ /**
26
+ * The type names the runtime already uses inside the generated class — its own nested
27
+ * types and every JDK type it references, read off its source — plus the names the
28
+ * generated methods add. A schema that folds to one of them gets a `Model` suffix: a
29
+ * nested `record Config` would shadow the runtime's `Config`, and a nested `record Thread`
30
+ * (GitHub has one) would shadow `java.lang.Thread` for the runtime's retry sleep.
31
+ */
32
+ const TAKEN_TYPE_NAMES = new Set([
33
+ ...[
34
+ // Comments, char literals (a `'"'` would otherwise open a string), then strings go.
35
+ ...JAVA_RUNTIME_SOURCE.replace(/\/\*[\s\S]*?\*\/|\/\/.*$/gm, '')
36
+ .replace(/'(?:[^'\\]|\\.)'/g, "''")
37
+ .replace(/"(?:[^"\\]|\\.)*"/g, '""')
38
+ .matchAll(/\b[A-Z][A-Za-z0-9]*\b/g),
39
+ ].map((match) => match[0]),
40
+ 'Servers',
41
+ 'Void',
42
+ ]);
43
+
44
+ export function typeName(name: string): string {
45
+ const ident = naming.typeName(name);
46
+ return TAKEN_TYPE_NAMES.has(ident) ? `${ident}Model` : ident;
47
+ }
48
+
49
+ export function memberName(name: string): string {
50
+ return naming.memberName(name);
51
+ }
52
+
53
+ /** `"…"` with the escapes javac needs — safe for any spec-supplied text. */
54
+ export function javaString(value: string): string {
55
+ return naming.string(value);
56
+ }
57
+
58
+ /**
59
+ * The enclosing class — Java puts one public class per file, named after it — derived
60
+ * from the `--output` stem the way Python derives its module name (`openapi.client` →
61
+ * `OpenapiClient`). A schema of the same name cannot nest inside it, so that is rejected
62
+ * with the fix rather than renamed behind the user's back.
63
+ */
64
+ export function javaClassName(stem: string, model: ApiModel): string {
65
+ const name = identifierFor(stem, { style: 'pascal', reserved: JAVA });
66
+ // Inside a class named `Config` or `List`, those names mean the class itself.
67
+ if (TAKEN_TYPE_NAMES.has(name)) {
68
+ throw new NotSupportedError(
69
+ `The output file name "${stem}" gives the Java class the name "${name}", which the SDK already uses for one of its own types. Choose another --output name, for example "${stem}-sdk.ts".`
70
+ );
71
+ }
72
+ const clash = model.schemas.find((schema) => typeName(schema.name) === name);
73
+ if (clash !== undefined) {
74
+ throw new NotSupportedError(
75
+ `The output file name "${stem}" gives the Java class the same name as the "${clash.name}" schema, which cannot nest inside it. Choose another --output name, for example "${stem}-sdk.ts".`
76
+ );
77
+ }
78
+ return name;
79
+ }
80
+
81
+ /**
82
+ * The package clause the output declares, derived from the API title the way the PHP
83
+ * generator derives its namespace: the title's words in lowercase, run together, as Java
84
+ * package names are written (`Cafe Orders API` → `cafeordersapi`). A class in the default
85
+ * package cannot be imported from any named package, so there is always one. A name that
86
+ * cannot be a package as is follows Oracle's package-naming convention: a keyword gets a
87
+ * trailing underscore (`class_`), and so does `java`, which the JVM keeps for the JDK; a
88
+ * leading digit gets a leading underscore (`_3dprintingapi`).
89
+ */
90
+ export function javaPackageName(title: string): string {
91
+ const plain = plainPackageName(title);
92
+ if (plain === '') return 'api';
93
+ if (/^[0-9]/.test(plain)) return `_${plain}`;
94
+ return JAVA.has(plain) || plain === 'java' ? `${plain}_` : plain;
95
+ }
96
+
97
+ /** The title's words in lowercase, run together — the package before any convention applies. */
98
+ export function plainPackageName(title: string): string {
99
+ return casing.pascal(title).toLowerCase();
100
+ }
101
+
102
+ export function methodName(op: OperationModel): string {
103
+ return identifierFor(op.name, { style: 'camel', reserved: JAVA });
104
+ }
105
+
106
+ /** The methods every class inherits from `Object`; an operation named after one would override it. */
107
+ // prettier-ignore
108
+ const OBJECT_METHODS = [
109
+ 'getClass', 'hashCode', 'equals', 'clone', 'toString', 'notify', 'notifyAll', 'wait',
110
+ 'finalize',
111
+ ];
112
+
113
+ /** The methods an operation may add beside its own, all named after it. */
114
+ const METHOD_VARIANTS = ['', 'WithHeaders', 'Pages', 'Items'];
115
+
116
+ /**
117
+ * The method name for every operation, unique across the client — two operationIds may
118
+ * camel-case to one name (`get-user`, `getUser`). Each name also books its `WithHeaders`,
119
+ * `Pages`, and `Items` variants, so a later operation named like one of them moves aside
120
+ * (`listOrdersPages2`). Keyed by the IR name, which the sanitizer already made unique.
121
+ */
122
+ export function methodIdents(model: ApiModel): Map<string, string> {
123
+ const used = new Set(OBJECT_METHODS);
124
+ const idents = new Map<string, string>();
125
+ for (const op of model.services.flatMap((service) => service.operations)) {
126
+ const base = identifierFor(op.name, { style: 'camel', reserved: JAVA });
127
+ let ident = base;
128
+ for (let suffix = 2; METHOD_VARIANTS.some((variant) => used.has(ident + variant)); suffix++) {
129
+ ident = `${base}${suffix}`;
130
+ }
131
+ for (const variant of METHOD_VARIANTS) used.add(ident + variant);
132
+ idents.set(op.name, ident);
133
+ }
134
+ return idents;
135
+ }
@@ -0,0 +1,300 @@
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 `operations` stage: one typed request method per operation, the `<Op>Params`
6
+ // class its optional query parameters travel in, and the argument planning and request
7
+ // prologue it shares with the pagination wrappers.
8
+
9
+ import {
10
+ type ApiModel,
11
+ type DateType,
12
+ deref,
13
+ isBinaryContentType,
14
+ isMultipartBody,
15
+ isUntypedMultipartBody,
16
+ jsonSuccessSchema,
17
+ type NeutralPaginationRule,
18
+ type OperationModel,
19
+ sseResponse,
20
+ uniqueIdentifiers,
21
+ unwrapNullable,
22
+ } from '@redocly/client-generator';
23
+ import type { JavaPrinter } from '@redocly/client-generator/printers/java';
24
+
25
+ import { envelopeHeaderSpecs, javaOperationLiteral } from './descriptor.ts';
26
+ import { hydration } from './models.ts';
27
+ import { JAVA, javaString, typeName } from './naming.ts';
28
+ import { javaType } from './types.ts';
29
+
30
+ type MethodArgs = {
31
+ pathArgs: Array<{ java: string; wire: string }>;
32
+ /** The optional query parameters, as fields of the `<Op>Params` class. */
33
+ queryFields: Array<{ java: string; wire: string; type: string; isArray: boolean }>;
34
+ paramsClass: string | undefined;
35
+ signature: string[];
36
+ };
37
+
38
+ /**
39
+ * The argument and local names a request method declares beside its path parameters. A
40
+ * parameter named after one of them takes a suffixed name instead, so each keeps its meaning.
41
+ */
42
+ // prettier-ignore
43
+ const METHOD_ARG_SLOTS = [
44
+ 'body', 'params', 'op', 'auth', 'query', 'url', 'spec', 'payload', 'response', 'data',
45
+ 'headers', 'extraHeaders', 'pageParams', 'call', 'base', 'item', 'page', 'config',
46
+ ];
47
+
48
+ /** Whether the body is `Object` and the runtime sorts out what it got (`multipartBody` / `binaryBody`). */
49
+ function passesThrough(op: OperationModel): boolean {
50
+ return (
51
+ isUntypedMultipartBody(op) ||
52
+ (op.requestBody !== undefined && isBinaryContentType(op.requestBody.contentType))
53
+ );
54
+ }
55
+
56
+ /**
57
+ * The `<Op>Params` class name, when the operation has query parameters. A schema may fold
58
+ * to the same name (`ListOrdersParams` is a plausible schema), and two nested classes cannot
59
+ * share one, so the class moves aside with a suffix.
60
+ */
61
+ export function paramsClassName(
62
+ op: OperationModel,
63
+ ident: string,
64
+ model: ApiModel
65
+ ): string | undefined {
66
+ if (op.queryParams.length === 0) return undefined;
67
+ return uniqueIdentifiers([`${ident}Params`], {
68
+ style: 'pascal',
69
+ reserved: JAVA,
70
+ taken: model.schemas.map((schema) => typeName(schema.name)),
71
+ })[0];
72
+ }
73
+
74
+ export function methodArgs(
75
+ op: OperationModel,
76
+ ident: string,
77
+ model: ApiModel,
78
+ includeBody: boolean,
79
+ dateType: DateType
80
+ ): MethodArgs {
81
+ // Path parameters are arguments, so their names share one namespace with the locals the
82
+ // method declares itself; a repeat moves aside (`body`, `body2`). Query parameters live in
83
+ // their own class, so they only need to be unique among themselves.
84
+ const pathNames = uniqueIdentifiers(
85
+ op.pathParams.map((param) => param.name),
86
+ { style: 'camel', reserved: JAVA, taken: METHOD_ARG_SLOTS }
87
+ );
88
+ const queryNames = uniqueIdentifiers(
89
+ op.queryParams.map((param) => param.name),
90
+ { style: 'camel', reserved: JAVA }
91
+ );
92
+ const pathArgs = op.pathParams.map((param, index) => ({
93
+ java: pathNames[index],
94
+ wire: param.name,
95
+ }));
96
+ const queryFields = op.queryParams.map((param, index) => ({
97
+ java: queryNames[index],
98
+ wire: param.name,
99
+ type: javaType(param.schema, model, dateType),
100
+ // Through a `$ref`: a named list alias repeats the key like an inline array does.
101
+ isArray: deref(unwrapNullable(param.schema), model)?.kind === 'array',
102
+ }));
103
+ const paramsClass = paramsClassName(op, ident, model);
104
+ const signature = [
105
+ ...op.pathParams.map(
106
+ (param, index) => `${javaType(param.schema, model, dateType)} ${pathNames[index]}`
107
+ ),
108
+ ...(includeBody && op.requestBody
109
+ ? [`${passesThrough(op) ? 'Object' : javaType(op.requestBody.schema, model, dateType)} body`]
110
+ : []),
111
+ ...(paramsClass === undefined ? [] : [`${paramsClass} params`]),
112
+ ];
113
+ return { pathArgs, queryFields, paramsClass, signature };
114
+ }
115
+
116
+ /** The `<Op>Params` class: one nullable field per query parameter, with a fluent setter each. */
117
+ export function writeParamsClass(
118
+ printer: JavaPrinter,
119
+ op: OperationModel,
120
+ ident: string,
121
+ model: ApiModel,
122
+ dateType: DateType
123
+ ): void {
124
+ const { queryFields, paramsClass } = methodArgs(op, ident, model, false, dateType);
125
+ if (paramsClass === undefined) return;
126
+ printer.line(`/** The query parameters of ${ident}; an unset field is not sent. */`);
127
+ printer.block(
128
+ `public static final class ${paramsClass} {`,
129
+ () => {
130
+ for (const field of queryFields) printer.line(`public ${field.type} ${field.java};`);
131
+ for (const field of queryFields) {
132
+ printer.blank();
133
+ printer.block(
134
+ `public ${paramsClass} ${field.java}(${field.type} ${field.java}) {`,
135
+ () => {
136
+ printer.line(`this.${field.java} = ${field.java};`);
137
+ printer.line('return this;');
138
+ },
139
+ '}'
140
+ );
141
+ }
142
+ },
143
+ '}'
144
+ );
145
+ printer.blank();
146
+ }
147
+
148
+ /** Add the set query parameters to `target` (a `Map<String, List<String>>`). */
149
+ export function writeQueryParams(printer: JavaPrinter, args: MethodArgs, target: string): void {
150
+ if (args.paramsClass === undefined) return;
151
+ printer.block(
152
+ 'if (params != null) {',
153
+ () => {
154
+ for (const field of args.queryFields) {
155
+ printer.block(
156
+ `if (params.${field.java} != null) {`,
157
+ () => {
158
+ // An array repeats the key per element (OpenAPI `form` + `explode`, the default).
159
+ if (field.isArray) {
160
+ printer.block(
161
+ `for (Object item : params.${field.java}) {`,
162
+ () => {
163
+ printer.line(`addQuery(${target}, ${javaString(field.wire)}, item);`);
164
+ },
165
+ '}'
166
+ );
167
+ } else {
168
+ printer.line(`addQuery(${target}, ${javaString(field.wire)}, params.${field.java});`);
169
+ }
170
+ },
171
+ '}'
172
+ );
173
+ }
174
+ },
175
+ '}'
176
+ );
177
+ }
178
+
179
+ /** The `buildUrl(…)` expression for the operation's path. */
180
+ export function urlExpression(args: MethodArgs): string {
181
+ const pairs = args.pathArgs
182
+ .map(({ java, wire }) => `${javaString(wire)}, textValue(${java})`)
183
+ .join(', ');
184
+ return `buildUrl(config.serverUrl, op.path(), pathParams(${pairs}))`;
185
+ }
186
+
187
+ export function writeJavaMethod(
188
+ printer: JavaPrinter,
189
+ op: OperationModel,
190
+ ident: string,
191
+ model: ApiModel,
192
+ dateType: DateType,
193
+ rule: NeutralPaginationRule | undefined,
194
+ envelope = false
195
+ ): void {
196
+ const args = methodArgs(op, ident, model, true, dateType);
197
+ const sse = sseResponse(op);
198
+ const success = jsonSuccessSchema(op);
199
+ // Non-JSON success bodies (PDFs, images, octet streams) return the raw bytes.
200
+ const rawBody =
201
+ sse === undefined &&
202
+ success === undefined &&
203
+ op.successResponses.some((response) => response.contentType !== '');
204
+ const dataType =
205
+ success !== undefined ? javaType(success, model, dateType) : rawBody ? 'byte[]' : 'void';
206
+ const returnType = envelope
207
+ ? `Envelope<${dataType === 'void' ? 'Void' : dataType}>`
208
+ : sse !== undefined
209
+ ? 'Iterable<ServerSentEvent>'
210
+ : dataType;
211
+ const name = envelope ? `${ident}WithHeaders` : ident;
212
+ printer.doc(
213
+ name,
214
+ envelope
215
+ ? `Like ${ident}, returning an Envelope with the declared response headers.`
216
+ : (op.summary ?? `${op.method.toUpperCase()} ${op.path}`)
217
+ );
218
+ printer.block(
219
+ `public ${returnType} ${name}(${args.signature.join(', ')}) {`,
220
+ () => {
221
+ printer.line(`Operation op = ${javaOperationLiteral(op, model, rule)};`);
222
+ if (sse !== undefined) {
223
+ // Auth resolves inside the opener, so every reconnect asks a token supplier again
224
+ // — a long-lived stream outlives the token it started with.
225
+ const jsonData = sse.schema !== undefined && sse.schema.kind !== 'unknown';
226
+ printer.line('Map<String, List<String>> base = new LinkedHashMap<>();');
227
+ writeQueryParams(printer, args, 'base');
228
+ printer.line(`String url = ${urlExpression(args)};`);
229
+ printer.block(
230
+ 'return iterSse(extraHeaders -> {',
231
+ () => {
232
+ printer.line('ResolvedAuth auth = resolveAuth(op.security(), config.auth);');
233
+ printer.line('Map<String, List<String>> query = cloneQuery(auth.query());');
234
+ printer.line('query.putAll(base);');
235
+ printer.line('Map<String, String> headers = new LinkedHashMap<>(auth.headers());');
236
+ printer.line('headers.putAll(extraHeaders);');
237
+ printer.line(
238
+ 'return send(config, new RequestSpec(op.id(), op.method(), url).headers(headers).query(query));'
239
+ );
240
+ },
241
+ `}, ${jsonData});`
242
+ );
243
+ return;
244
+ }
245
+ printer.line('ResolvedAuth auth = resolveAuth(op.security(), config.auth);');
246
+ printer.line('Map<String, List<String>> query = auth.query();');
247
+ writeQueryParams(printer, args, 'query');
248
+ printer.line(`String url = ${urlExpression(args)};`);
249
+ printer.line(
250
+ 'RequestSpec spec = new RequestSpec(op.id(), op.method(), url).headers(auth.headers()).query(query);'
251
+ );
252
+ if (op.requestBody !== undefined) {
253
+ const contentType = javaString(op.requestBody.contentType);
254
+ const payload = isUntypedMultipartBody(op)
255
+ ? 'multipartBody(body)'
256
+ : passesThrough(op)
257
+ ? `binaryBody(body, ${contentType})`
258
+ : isMultipartBody(op)
259
+ ? 'multipartBody(body)'
260
+ : `jsonBody(body, ${contentType})`;
261
+ printer.line(`Body payload = ${payload};`);
262
+ printer.line('spec.body(payload.body(), payload.contentType());');
263
+ }
264
+ printer.line('HttpResponse<InputStream> response = send(config, spec);');
265
+ printer.block(
266
+ 'if (response.statusCode() >= 400) {',
267
+ () => {
268
+ printer.line('throw apiErrorFrom(response, url);');
269
+ },
270
+ '}'
271
+ );
272
+ const decoded = rawBody
273
+ ? 'readBody(response)'
274
+ : success === undefined
275
+ ? undefined
276
+ : (hydration(success, 'decodeJson(response)', model, dateType) ?? 'decodeJson(response)');
277
+ if (envelope) {
278
+ if (decoded === undefined) {
279
+ printer.line('readBody(response);');
280
+ printer.line(
281
+ `return new Envelope<>(null, readEnvelopeHeaders(response, ${envelopeHeaderSpecs(op, model)}), response.statusCode());`
282
+ );
283
+ } else {
284
+ printer.line(`${dataType} data = ${decoded};`);
285
+ printer.line(
286
+ `return new Envelope<>(data, readEnvelopeHeaders(response, ${envelopeHeaderSpecs(op, model)}), response.statusCode());`
287
+ );
288
+ }
289
+ return;
290
+ }
291
+ if (decoded === undefined) {
292
+ printer.line('readBody(response);');
293
+ return;
294
+ }
295
+ printer.line(`return ${decoded};`);
296
+ },
297
+ '}'
298
+ );
299
+ printer.blank();
300
+ }
@@ -0,0 +1,94 @@
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 `pagination` stage: the `<op>Pages()` / `<op>Items()` iterables over the
6
+ // runtime's iterPages.
7
+
8
+ import {
9
+ type ApiModel,
10
+ type DateType,
11
+ jsonSuccessSchema,
12
+ type NeutralPaginationRule,
13
+ type OperationModel,
14
+ paginationItemSchema,
15
+ } from '@redocly/client-generator';
16
+ import type { JavaPrinter } from '@redocly/client-generator/printers/java';
17
+
18
+ import { javaOperationLiteral } from './descriptor.ts';
19
+ import { hydration } from './models.ts';
20
+ import { methodArgs, urlExpression, writeQueryParams } from './operations.ts';
21
+ import { javaType } from './types.ts';
22
+
23
+ /** `<op>Pages()` / `<op>Items()` iterables over the runtime's iterPages, hydrated per element. */
24
+ export function writeJavaPaginationWrappers(
25
+ printer: JavaPrinter,
26
+ op: OperationModel,
27
+ ident: string,
28
+ model: ApiModel,
29
+ dateType: DateType,
30
+ rule: NeutralPaginationRule
31
+ ): void {
32
+ const args = methodArgs(op, ident, model, false, dateType);
33
+ const success = jsonSuccessSchema(op);
34
+ const pageType = success === undefined ? 'Object' : javaType(success, model, dateType);
35
+ const element = paginationItemSchema(success, rule.items, model);
36
+ const itemType = element === undefined ? 'Object' : javaType(element, model, dateType);
37
+ const pageHydration =
38
+ success === undefined ? 'page' : (hydration(success, 'page', model, dateType) ?? 'page');
39
+ const itemHydration =
40
+ element === undefined ? 'item' : (hydration(element, 'item', model, dateType, 1) ?? 'item');
41
+
42
+ const writeCall = () => {
43
+ printer.line(`Operation op = ${javaOperationLiteral(op, model, rule)};`);
44
+ printer.line('Map<String, List<String>> base = new LinkedHashMap<>();');
45
+ writeQueryParams(printer, args, 'base');
46
+ printer.block(
47
+ 'PageCall call = pageParams -> {',
48
+ () => {
49
+ printer.line('ResolvedAuth auth = resolveAuth(op.security(), config.auth);');
50
+ printer.line('Map<String, List<String>> query = cloneQuery(auth.query());');
51
+ printer.line('query.putAll(pageParams);');
52
+ printer.line(`String url = ${urlExpression(args)};`);
53
+ printer.line(
54
+ 'HttpResponse<InputStream> response = send(config, new RequestSpec(op.id(), op.method(), url).headers(auth.headers()).query(query));'
55
+ );
56
+ printer.block(
57
+ 'if (response.statusCode() >= 400) {',
58
+ () => {
59
+ printer.line('throw apiErrorFrom(response, url);');
60
+ },
61
+ '}'
62
+ );
63
+ printer.line('return new PageResult(decodeJson(response), response);');
64
+ },
65
+ '};'
66
+ );
67
+ };
68
+
69
+ printer.line(`/** ${ident} response pages, following the pagination rule automatically. */`);
70
+ printer.block(
71
+ `public Iterable<${pageType}> ${ident}Pages(${args.signature.join(', ')}) {`,
72
+ () => {
73
+ writeCall();
74
+ printer.line(
75
+ `return mapIterable(iterPages(call, op.pagination(), base), page -> ${pageHydration});`
76
+ );
77
+ },
78
+ '}'
79
+ );
80
+ printer.blank();
81
+
82
+ printer.line(`/** The items of every ${ident} page. */`);
83
+ printer.block(
84
+ `public Iterable<${itemType}> ${ident}Items(${args.signature.join(', ')}) {`,
85
+ () => {
86
+ writeCall();
87
+ printer.line(
88
+ `return mapIterable(flattenItems(iterPages(call, op.pagination(), base), op.pagination().items()), item -> ${itemHydration});`
89
+ );
90
+ },
91
+ '}'
92
+ );
93
+ printer.blank();
94
+ }
@@ -0,0 +1,103 @@
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 `types` stage: the Java type for a schema, and what a named schema renders as.
6
+
7
+ import {
8
+ type ApiModel,
9
+ type DateType,
10
+ deref,
11
+ discriminatorCases,
12
+ enumValues,
13
+ flattenAllOf,
14
+ type SchemaModel,
15
+ unwrapNullable,
16
+ } from '@redocly/client-generator';
17
+
18
+ import { typeName } from './naming.ts';
19
+
20
+ /**
21
+ * What a named schema renders as: a record, a wire-backed enum, a sealed union interface
22
+ * (every mapping target is a record, so the interface can seal over them), or nothing.
23
+ */
24
+ export function classify(name: string, model: ApiModel): 'record' | 'enum' | 'union' | 'other' {
25
+ const named = model.schemas.find((candidate) => candidate.name === name);
26
+ if (named === undefined) return 'other';
27
+ const schema = named.schema;
28
+ const asEnum = enumValues(schema);
29
+ if (asEnum !== undefined && (asEnum.scalar === 'string' || asEnum.scalar === 'integer')) {
30
+ return 'enum';
31
+ }
32
+ if (
33
+ (schema.kind === 'object' || schema.kind === 'intersection') &&
34
+ flattenAllOf(schema, model) !== undefined
35
+ ) {
36
+ return 'record';
37
+ }
38
+ const cases = discriminatorCases(schema, model);
39
+ if (
40
+ cases !== undefined &&
41
+ cases.cases.length > 0 &&
42
+ cases.cases.every((entry) => classify(entry.schemaName, model) === 'record')
43
+ ) {
44
+ return 'union';
45
+ }
46
+ return 'other';
47
+ }
48
+
49
+ const SCALARS = { string: 'String', integer: 'Long', number: 'Double', boolean: 'Boolean' };
50
+
51
+ /** `date` or `date-time` — the two formats `dateType: Date` turns into java.time objects. */
52
+ export function isDateFormat(schema: SchemaModel): boolean {
53
+ const format = schema.metadata?.format;
54
+ return format === 'date' || format === 'date-time';
55
+ }
56
+
57
+ /**
58
+ * The Java type for a schema. Every type is a reference type (`Long`, not `long`), so an
59
+ * optional or nullable value is simply `null`; untyped shapes widen to `Object`.
60
+ */
61
+ export function javaType(
62
+ schema: SchemaModel,
63
+ model: ApiModel,
64
+ dateType: DateType = 'string'
65
+ ): string {
66
+ const bare = unwrapNullable(schema);
67
+ switch (bare.kind) {
68
+ case 'scalar':
69
+ if (dateType === 'Date' && bare.scalar === 'string') {
70
+ if (bare.metadata?.format === 'date-time') return 'OffsetDateTime';
71
+ if (bare.metadata?.format === 'date') return 'LocalDate';
72
+ }
73
+ return SCALARS[bare.scalar];
74
+ case 'array':
75
+ return `List<${javaType(bare.items, model, dateType)}>`;
76
+ case 'record':
77
+ return `Map<String, ${javaType(bare.value, model, dateType)}>`;
78
+ case 'ref': {
79
+ const kind = classify(bare.name, model);
80
+ if (kind !== 'other') return typeName(bare.name);
81
+ const target = deref(bare, model);
82
+ return target === undefined ? 'Object' : javaType(target, model, dateType);
83
+ }
84
+ case 'enum':
85
+ // Anonymous (inline) enums keep the wire scalar; only NAMED enums get types.
86
+ return SCALARS[bare.scalar];
87
+ case 'literal':
88
+ return typeof bare.value === 'string'
89
+ ? 'String'
90
+ : typeof bare.value === 'boolean'
91
+ ? 'Boolean'
92
+ : 'Double';
93
+ case 'omit':
94
+ // Java has no Omit; the base type is the honest annotation.
95
+ return javaType({ kind: 'ref', name: bare.base }, model, dateType);
96
+ case 'union':
97
+ case 'null':
98
+ case 'object':
99
+ case 'intersection':
100
+ case 'unknown':
101
+ return 'Object';
102
+ }
103
+ }
@@ -1,4 +1,4 @@
1
- // Ejected from @redocly/client-generator@0.7.12 — the built-in "mock" generator.
1
+ // Ejected from @redocly/client-generator@0.8.0 — the built-in "mock" generator.
2
2
  // This file is yours: edit freely; the generated client stays machine-owned and is
3
3
  // rebuilt by `redocly generate-client`. Newer generator versions merge in with
4
4
  // `redocly eject-generator mock --update`.
@@ -1,4 +1,4 @@
1
- // Ejected from @redocly/client-generator@0.7.12 — the built-in "mock" generator.
1
+ // Ejected from @redocly/client-generator@0.8.0 — the built-in "mock" generator.
2
2
  // This file is yours: edit freely; the generated client stays machine-owned and is
3
3
  // rebuilt by `redocly generate-client`. Newer generator versions merge in with
4
4
  // `redocly eject-generator mock --update`.
@@ -32,5 +32,5 @@ export default {
32
32
  name: 'mock',
33
33
  run: mockGenerator,
34
34
  requires: ["typescript"],
35
- requiresGenerator: '^0.7.12',
35
+ requiresGenerator: '^0.8.0',
36
36
  };
@@ -1,4 +1,4 @@
1
- // Ejected from @redocly/client-generator@0.7.12 — the built-in "mock" generator.
1
+ // Ejected from @redocly/client-generator@0.8.0 — the built-in "mock" generator.
2
2
  // This file is yours: edit freely; the generated client stays machine-owned and is
3
3
  // rebuilt by `redocly generate-client`. Newer generator versions merge in with
4
4
  // `redocly eject-generator mock --update`.
@@ -1,4 +1,4 @@
1
- // Ejected from @redocly/client-generator@0.7.12 — the built-in "mock" generator.
1
+ // Ejected from @redocly/client-generator@0.8.0 — the built-in "mock" generator.
2
2
  // This file is yours: edit freely; the generated client stays machine-owned and is
3
3
  // rebuilt by `redocly generate-client`. Newer generator versions merge in with
4
4
  // `redocly eject-generator mock --update`.
@@ -1,4 +1,4 @@
1
- // Ejected from @redocly/client-generator@0.7.12 — the built-in "mock" generator.
1
+ // Ejected from @redocly/client-generator@0.8.0 — the built-in "mock" generator.
2
2
  // This file is yours: edit freely; the generated client stays machine-owned and is
3
3
  // rebuilt by `redocly generate-client`. Newer generator versions merge in with
4
4
  // `redocly eject-generator mock --update`.
@@ -1,4 +1,4 @@
1
- // Ejected from @redocly/client-generator@0.7.12 — the built-in "php" generator.
1
+ // Ejected from @redocly/client-generator@0.8.0 — the built-in "php" generator.
2
2
  // This file is yours: edit freely; the generated client stays machine-owned and is
3
3
  // rebuilt by `redocly generate-client`. Newer generator versions merge in with
4
4
  // `redocly eject-generator php --update`.