@redocly/client-generator 0.0.0-snapshot.1786868116 → 0.0.0-snapshot.1787063344

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 (294) hide show
  1. package/README.md +22 -30
  2. package/eject-assets/AGENTS.md +130 -0
  3. package/eject-assets/generators/cli.mjs +700 -0
  4. package/eject-assets/generators/go.mjs +856 -0
  5. package/eject-assets/generators/mock.mjs +689 -0
  6. package/eject-assets/generators/php.mjs +830 -0
  7. package/eject-assets/generators/python.mjs +742 -0
  8. package/eject-assets/generators/swr.mjs +267 -0
  9. package/eject-assets/generators/tanstack-query.mjs +632 -0
  10. package/eject-assets/generators/transformers.mjs +403 -0
  11. package/eject-assets/generators/typescript.mjs +1819 -0
  12. package/eject-assets/generators/zod.mjs +520 -0
  13. package/eject-assets/skills/cli-generator/SKILL.md +112 -0
  14. package/eject-assets/skills/client-generators/SKILL.md +135 -0
  15. package/eject-assets/skills/go-generator/SKILL.md +89 -0
  16. package/eject-assets/skills/mock-generator/SKILL.md +45 -0
  17. package/eject-assets/skills/php-generator/SKILL.md +105 -0
  18. package/eject-assets/skills/python-generator/SKILL.md +93 -0
  19. package/eject-assets/skills/swr-generator/SKILL.md +44 -0
  20. package/eject-assets/skills/tanstack-query-generator/SKILL.md +48 -0
  21. package/eject-assets/skills/transformers-generator/SKILL.md +43 -0
  22. package/eject-assets/skills/typescript-generator/SKILL.md +73 -0
  23. package/eject-assets/skills/zod-generator/SKILL.md +49 -0
  24. package/lib/authoring/index.d.ts +10 -0
  25. package/lib/authoring/index.d.ts.map +1 -0
  26. package/lib/authoring/index.js +33 -0
  27. package/lib/authoring/index.js.map +1 -0
  28. package/lib/authoring/naming.d.ts +19 -0
  29. package/lib/authoring/naming.d.ts.map +1 -0
  30. package/lib/authoring/naming.js +80 -0
  31. package/lib/authoring/naming.js.map +1 -0
  32. package/lib/authoring/options.d.ts +9 -0
  33. package/lib/authoring/options.d.ts.map +1 -0
  34. package/lib/authoring/options.js +5 -0
  35. package/lib/authoring/options.js.map +1 -0
  36. package/lib/authoring/pagination.d.ts +19 -0
  37. package/lib/authoring/pagination.d.ts.map +1 -0
  38. package/lib/authoring/pagination.js +45 -0
  39. package/lib/authoring/pagination.js.map +1 -0
  40. package/lib/authoring/printer.d.ts +16 -0
  41. package/lib/authoring/printer.d.ts.map +1 -0
  42. package/lib/authoring/printer.js +36 -0
  43. package/lib/authoring/printer.js.map +1 -0
  44. package/lib/authoring/reference-page.d.ts +29 -0
  45. package/lib/authoring/reference-page.d.ts.map +1 -0
  46. package/lib/authoring/reference-page.js +157 -0
  47. package/lib/authoring/reference-page.js.map +1 -0
  48. package/lib/authoring/schema.d.ts +49 -0
  49. package/lib/authoring/schema.d.ts.map +1 -0
  50. package/lib/authoring/schema.js +185 -0
  51. package/lib/authoring/schema.js.map +1 -0
  52. package/lib/emitters/cli-docs.d.ts +17 -0
  53. package/lib/emitters/cli-docs.d.ts.map +1 -0
  54. package/lib/emitters/cli-docs.js +186 -0
  55. package/lib/emitters/cli-docs.js.map +1 -0
  56. package/lib/emitters/cli.d.ts +38 -0
  57. package/lib/emitters/cli.d.ts.map +1 -0
  58. package/lib/emitters/cli.js +230 -0
  59. package/lib/emitters/cli.js.map +1 -0
  60. package/lib/emitters/client-assembly.d.ts.map +1 -1
  61. package/lib/emitters/client-assembly.js +45 -172
  62. package/lib/emitters/client-assembly.js.map +1 -1
  63. package/lib/emitters/descriptor.d.ts +4 -13
  64. package/lib/emitters/descriptor.d.ts.map +1 -1
  65. package/lib/emitters/descriptor.js +34 -123
  66. package/lib/emitters/descriptor.js.map +1 -1
  67. package/lib/emitters/emit-options.d.ts +22 -1
  68. package/lib/emitters/emit-options.d.ts.map +1 -1
  69. package/lib/emitters/emit-options.js +1 -1
  70. package/lib/emitters/emit-options.js.map +1 -1
  71. package/lib/emitters/faker.d.ts +3 -3
  72. package/lib/emitters/faker.d.ts.map +1 -1
  73. package/lib/emitters/faker.js +66 -107
  74. package/lib/emitters/faker.js.map +1 -1
  75. package/lib/emitters/go-runtime-sources.d.ts +2 -0
  76. package/lib/emitters/go-runtime-sources.d.ts.map +1 -0
  77. package/lib/emitters/go-runtime-sources.js +3 -0
  78. package/lib/emitters/go-runtime-sources.js.map +1 -0
  79. package/lib/emitters/identifier.d.ts.map +1 -1
  80. package/lib/emitters/identifier.js +10 -0
  81. package/lib/emitters/identifier.js.map +1 -1
  82. package/lib/emitters/inline-runtime.d.ts +2 -0
  83. package/lib/emitters/inline-runtime.d.ts.map +1 -1
  84. package/lib/emitters/inline-runtime.js +14 -45
  85. package/lib/emitters/inline-runtime.js.map +1 -1
  86. package/lib/emitters/jsdoc.d.ts +2 -0
  87. package/lib/emitters/jsdoc.d.ts.map +1 -1
  88. package/lib/emitters/jsdoc.js +4 -1
  89. package/lib/emitters/jsdoc.js.map +1 -1
  90. package/lib/emitters/mock-value.d.ts +33 -0
  91. package/lib/emitters/mock-value.d.ts.map +1 -0
  92. package/lib/emitters/mock-value.js +47 -0
  93. package/lib/emitters/mock-value.js.map +1 -0
  94. package/lib/emitters/mock.d.ts.map +1 -1
  95. package/lib/emitters/mock.js +88 -123
  96. package/lib/emitters/mock.js.map +1 -1
  97. package/lib/emitters/operation-types.d.ts +1 -24
  98. package/lib/emitters/operation-types.d.ts.map +1 -1
  99. package/lib/emitters/operation-types.js +1 -107
  100. package/lib/emitters/operation-types.js.map +1 -1
  101. package/lib/emitters/operations.d.ts +2 -10
  102. package/lib/emitters/operations.d.ts.map +1 -1
  103. package/lib/emitters/operations.js +1 -34
  104. package/lib/emitters/operations.js.map +1 -1
  105. package/lib/emitters/pagination.d.ts +6 -13
  106. package/lib/emitters/pagination.d.ts.map +1 -1
  107. package/lib/emitters/pagination.js +11 -49
  108. package/lib/emitters/pagination.js.map +1 -1
  109. package/lib/emitters/php-runtime-sources.d.ts +2 -0
  110. package/lib/emitters/php-runtime-sources.d.ts.map +1 -0
  111. package/lib/emitters/php-runtime-sources.js +3 -0
  112. package/lib/emitters/php-runtime-sources.js.map +1 -0
  113. package/lib/emitters/python-runtime-sources.d.ts +12 -0
  114. package/lib/emitters/python-runtime-sources.d.ts.map +1 -0
  115. package/lib/emitters/python-runtime-sources.js +12 -0
  116. package/lib/emitters/python-runtime-sources.js.map +1 -0
  117. package/lib/emitters/render-client.d.ts +31 -0
  118. package/lib/emitters/render-client.d.ts.map +1 -0
  119. package/lib/emitters/render-client.js +346 -0
  120. package/lib/emitters/render-client.js.map +1 -0
  121. package/lib/emitters/reserved-names.d.ts +4 -1
  122. package/lib/emitters/reserved-names.d.ts.map +1 -1
  123. package/lib/emitters/reserved-names.js +16 -27
  124. package/lib/emitters/reserved-names.js.map +1 -1
  125. package/lib/emitters/response-headers.d.ts +5 -6
  126. package/lib/emitters/response-headers.d.ts.map +1 -1
  127. package/lib/emitters/response-headers.js +13 -63
  128. package/lib/emitters/response-headers.js.map +1 -1
  129. package/lib/emitters/runtime-sources.d.ts +20 -0
  130. package/lib/emitters/runtime-sources.d.ts.map +1 -1
  131. package/lib/emitters/runtime-sources.js +130 -0
  132. package/lib/emitters/runtime-sources.js.map +1 -1
  133. package/lib/emitters/setup-bake.d.ts.map +1 -1
  134. package/lib/emitters/setup-bake.js +10 -1
  135. package/lib/emitters/setup-bake.js.map +1 -1
  136. package/lib/emitters/sse.d.ts +3 -5
  137. package/lib/emitters/sse.d.ts.map +1 -1
  138. package/lib/emitters/sse.js +1 -11
  139. package/lib/emitters/sse.js.map +1 -1
  140. package/lib/emitters/swr.d.ts.map +1 -1
  141. package/lib/emitters/swr.js +34 -65
  142. package/lib/emitters/swr.js.map +1 -1
  143. package/lib/emitters/tanstack-query.d.ts.map +1 -1
  144. package/lib/emitters/tanstack-query.js +3 -5
  145. package/lib/emitters/tanstack-query.js.map +1 -1
  146. package/lib/emitters/transformers.d.ts.map +1 -1
  147. package/lib/emitters/transformers.js +123 -162
  148. package/lib/emitters/transformers.js.map +1 -1
  149. package/lib/emitters/ts-literal.d.ts +5 -0
  150. package/lib/emitters/ts-literal.d.ts.map +1 -0
  151. package/lib/emitters/ts-literal.js +35 -0
  152. package/lib/emitters/ts-literal.js.map +1 -0
  153. package/lib/emitters/ts-type.d.ts +9 -0
  154. package/lib/emitters/ts-type.d.ts.map +1 -0
  155. package/lib/emitters/ts-type.js +133 -0
  156. package/lib/emitters/ts-type.js.map +1 -0
  157. package/lib/emitters/type-guards.d.ts +2 -19
  158. package/lib/emitters/type-guards.d.ts.map +1 -1
  159. package/lib/emitters/type-guards.js +18 -52
  160. package/lib/emitters/type-guards.js.map +1 -1
  161. package/lib/emitters/types.d.ts +1 -14
  162. package/lib/emitters/types.d.ts.map +1 -1
  163. package/lib/emitters/types.js +4 -124
  164. package/lib/emitters/types.js.map +1 -1
  165. package/lib/emitters/wrapper-support.d.ts +10 -23
  166. package/lib/emitters/wrapper-support.d.ts.map +1 -1
  167. package/lib/emitters/wrapper-support.js +23 -53
  168. package/lib/emitters/wrapper-support.js.map +1 -1
  169. package/lib/emitters/zod.d.ts +2 -3
  170. package/lib/emitters/zod.d.ts.map +1 -1
  171. package/lib/emitters/zod.js +115 -148
  172. package/lib/emitters/zod.js.map +1 -1
  173. package/lib/generate.d.ts +5 -4
  174. package/lib/generate.d.ts.map +1 -1
  175. package/lib/generate.js +22 -103
  176. package/lib/generate.js.map +1 -1
  177. package/lib/generators/cli/index.d.ts +13 -0
  178. package/lib/generators/cli/index.d.ts.map +1 -0
  179. package/lib/generators/cli/index.js +67 -0
  180. package/lib/generators/cli/index.js.map +1 -0
  181. package/lib/generators/compatibility.d.ts +10 -0
  182. package/lib/generators/compatibility.d.ts.map +1 -0
  183. package/lib/generators/compatibility.js +45 -0
  184. package/lib/generators/compatibility.js.map +1 -0
  185. package/lib/generators/go/index.d.ts +18 -0
  186. package/lib/generators/go/index.d.ts.map +1 -0
  187. package/lib/generators/go/index.js +838 -0
  188. package/lib/generators/go/index.js.map +1 -0
  189. package/lib/generators/index.d.ts +2 -2
  190. package/lib/generators/index.d.ts.map +1 -1
  191. package/lib/generators/index.js +31 -53
  192. package/lib/generators/index.js.map +1 -1
  193. package/lib/generators/meta.d.ts +14 -0
  194. package/lib/generators/meta.d.ts.map +1 -0
  195. package/lib/generators/meta.js +168 -0
  196. package/lib/generators/meta.js.map +1 -0
  197. package/lib/generators/{mock.d.ts → mock/index.d.ts} +2 -2
  198. package/lib/generators/mock/index.d.ts.map +1 -0
  199. package/lib/generators/{mock.js → mock/index.js} +4 -4
  200. package/lib/generators/mock/index.js.map +1 -0
  201. package/lib/generators/options.d.ts +7 -0
  202. package/lib/generators/options.d.ts.map +1 -0
  203. package/lib/generators/options.js +74 -0
  204. package/lib/generators/options.js.map +1 -0
  205. package/lib/generators/php/index.d.ts +18 -0
  206. package/lib/generators/php/index.d.ts.map +1 -0
  207. package/lib/generators/php/index.js +812 -0
  208. package/lib/generators/php/index.js.map +1 -0
  209. package/lib/generators/python/index.d.ts +21 -0
  210. package/lib/generators/python/index.d.ts.map +1 -0
  211. package/lib/generators/python/index.js +725 -0
  212. package/lib/generators/python/index.js.map +1 -0
  213. package/lib/generators/resolve.d.ts.map +1 -1
  214. package/lib/generators/resolve.js +73 -11
  215. package/lib/generators/resolve.js.map +1 -1
  216. package/lib/generators/{swr.d.ts → swr/index.d.ts} +3 -3
  217. package/lib/generators/swr/index.d.ts.map +1 -0
  218. package/lib/generators/{swr.js → swr/index.js} +5 -5
  219. package/lib/generators/swr/index.js.map +1 -0
  220. package/lib/generators/{tanstack-query.d.ts → tanstack-query/index.d.ts} +3 -3
  221. package/lib/generators/tanstack-query/index.d.ts.map +1 -0
  222. package/lib/generators/{tanstack-query.js → tanstack-query/index.js} +5 -5
  223. package/lib/generators/{tanstack-query.js.map → tanstack-query/index.js.map} +1 -1
  224. package/lib/generators/{transformers.d.ts → transformers/index.d.ts} +2 -2
  225. package/lib/generators/transformers/index.d.ts.map +1 -0
  226. package/lib/generators/{transformers.js → transformers/index.js} +4 -4
  227. package/lib/generators/transformers/index.js.map +1 -0
  228. package/lib/generators/types.d.ts +82 -4
  229. package/lib/generators/types.d.ts.map +1 -1
  230. package/lib/generators/typescript/index.d.ts +21 -0
  231. package/lib/generators/typescript/index.d.ts.map +1 -0
  232. package/lib/generators/typescript/index.js +80 -0
  233. package/lib/generators/typescript/index.js.map +1 -0
  234. package/lib/generators/{zod.d.ts → zod/index.d.ts} +2 -2
  235. package/lib/generators/zod/index.d.ts.map +1 -0
  236. package/lib/generators/{zod.js → zod/index.js} +4 -4
  237. package/lib/generators/zod/index.js.map +1 -0
  238. package/lib/index.d.ts +3 -0
  239. package/lib/index.d.ts.map +1 -1
  240. package/lib/index.js +7 -2
  241. package/lib/index.js.map +1 -1
  242. package/lib/intermediate-representation/build.d.ts.map +1 -1
  243. package/lib/intermediate-representation/build.js +31 -2
  244. package/lib/intermediate-representation/build.js.map +1 -1
  245. package/lib/intermediate-representation/model.d.ts +13 -1
  246. package/lib/intermediate-representation/model.d.ts.map +1 -1
  247. package/lib/intermediate-representation/model.js.map +1 -1
  248. package/lib/intermediate-representation/sanitize-identifiers.d.ts.map +1 -1
  249. package/lib/intermediate-representation/sanitize-identifiers.js +35 -13
  250. package/lib/intermediate-representation/sanitize-identifiers.js.map +1 -1
  251. package/lib/pipeline.d.ts +20 -0
  252. package/lib/pipeline.d.ts.map +1 -0
  253. package/lib/pipeline.js +199 -0
  254. package/lib/pipeline.js.map +1 -0
  255. package/lib/plugin.d.ts +1 -0
  256. package/lib/plugin.d.ts.map +1 -1
  257. package/lib/plugin.js +8 -7
  258. package/lib/plugin.js.map +1 -1
  259. package/lib/runtime/cli.d.ts +135 -0
  260. package/lib/runtime/cli.d.ts.map +1 -0
  261. package/lib/runtime/cli.js +558 -0
  262. package/lib/runtime/cli.js.map +1 -0
  263. package/lib/runtime-sources.d.ts +4 -0
  264. package/lib/runtime-sources.d.ts.map +1 -0
  265. package/lib/runtime-sources.js +9 -0
  266. package/lib/runtime-sources.js.map +1 -0
  267. package/lib/types.d.ts +35 -3
  268. package/lib/types.d.ts.map +1 -1
  269. package/package.json +10 -4
  270. package/lib/emitters/auth.d.ts +0 -14
  271. package/lib/emitters/auth.d.ts.map +0 -1
  272. package/lib/emitters/auth.js +0 -30
  273. package/lib/emitters/auth.js.map +0 -1
  274. package/lib/emitters/operation-aliases.d.ts +0 -27
  275. package/lib/emitters/operation-aliases.d.ts.map +0 -1
  276. package/lib/emitters/operation-aliases.js +0 -150
  277. package/lib/emitters/operation-aliases.js.map +0 -1
  278. package/lib/emitters/ts.d.ts +0 -42
  279. package/lib/emitters/ts.d.ts.map +0 -1
  280. package/lib/emitters/ts.js +0 -116
  281. package/lib/emitters/ts.js.map +0 -1
  282. package/lib/generators/mock.d.ts.map +0 -1
  283. package/lib/generators/mock.js.map +0 -1
  284. package/lib/generators/sdk.d.ts +0 -12
  285. package/lib/generators/sdk.d.ts.map +0 -1
  286. package/lib/generators/sdk.js +0 -26
  287. package/lib/generators/sdk.js.map +0 -1
  288. package/lib/generators/swr.d.ts.map +0 -1
  289. package/lib/generators/swr.js.map +0 -1
  290. package/lib/generators/tanstack-query.d.ts.map +0 -1
  291. package/lib/generators/transformers.d.ts.map +0 -1
  292. package/lib/generators/transformers.js.map +0 -1
  293. package/lib/generators/zod.d.ts.map +0 -1
  294. package/lib/generators/zod.js.map +0 -1
package/README.md CHANGED
@@ -27,7 +27,7 @@ import { generateClient } from '@redocly/client-generator';
27
27
  const result = await generateClient({
28
28
  api: './openapi.yaml', // file path or URL; OpenAPI 3.0/3.1/3.2 or Swagger 2.0
29
29
  output: './src/api/client.ts',
30
- generators: ['sdk', 'zod'],
30
+ generators: ['typescript', 'zod'],
31
31
  });
32
32
 
33
33
  console.log(`Wrote ${result.files.length} file(s), ${result.bytes} bytes.`);
@@ -55,43 +55,32 @@ With `runtime: 'package'` the generated client also imports its whole engine fro
55
55
  ### Write a custom generator
56
56
 
57
57
  A custom generator reads the same API model the built-ins consume, runs in the same pass, and returns files.
58
- Build real TypeScript with the emit toolkit from `@redocly/client-generator/generate` — the same `ts.factory` + printer the built-in generators use, so the schema→type mapping matches the sdk's exactly:
58
+ Emitters print text: `Printer` handles indentation, and `tsType` is the same schema→type renderer the built-in sdk uses, so the mapping (refs, arrays, unions, formats, parenthesization) matches the generated client exactly:
59
59
 
60
60
  ```ts
61
61
  // response-map-generator.ts
62
- import { defineGenerator } from '@redocly/client-generator';
63
- import { printStatements, schemaToTypeNode, ts } from '@redocly/client-generator/generate';
64
-
65
- const { factory } = ts;
62
+ import { defineGenerator, Printer } from '@redocly/client-generator';
63
+ import { tsType } from '@redocly/client-generator/generate';
66
64
 
67
65
  export default defineGenerator({
68
66
  name: 'response-map',
69
- requires: ['sdk'],
67
+ requires: ['typescript'],
70
68
  run({ model, outputPath }) {
69
+ const printer = new Printer();
71
70
  // One `ResponseShapes` entry per operation with a JSON success body.
72
- const members = model.services
73
- .flatMap((service) => service.operations)
74
- .flatMap((op) => {
75
- const success = op.successResponses.find((r) => r.contentType.includes('json'));
76
- if (!success) return [];
77
- return [
78
- factory.createPropertySignature(
79
- undefined,
80
- op.name,
81
- undefined,
82
- schemaToTypeNode(success.schema)
83
- ),
84
- ];
85
- });
86
- const alias = factory.createTypeAliasDeclaration(
87
- [factory.createModifier(ts.SyntaxKind.ExportKeyword)],
88
- 'ResponseShapes',
89
- undefined,
90
- factory.createTypeLiteralNode(members)
71
+ printer.block(
72
+ 'export type ResponseShapes = {',
73
+ () => {
74
+ for (const service of model.services) {
75
+ for (const op of service.operations) {
76
+ const success = op.successResponses.find((r) => r.contentType.includes('json'));
77
+ if (success) printer.line(`${op.name}: ${tsType(success.schema)};`);
78
+ }
79
+ }
80
+ },
81
+ '};'
91
82
  );
92
- return [
93
- { path: outputPath.replace(/\.ts$/, '.responses.ts'), content: printStatements([alias]) },
94
- ];
83
+ return [{ path: outputPath.replace(/\.ts$/, '.responses.ts'), content: printer.toString() }];
95
84
  },
96
85
  });
97
86
  ```
@@ -150,7 +139,10 @@ Authors a custom generator (`{ name, run }` plus optional `requires`/`errorModes
150
139
  function defineGenerator(generator: CustomGenerator): CustomGenerator;
151
140
  ```
152
141
 
153
- The `@redocly/client-generator/generate` entry also exports the emit toolkit the built-ins use (`ts`, `printStatements`, `parseStatements`, `operationSignature`, `schemaToTypeNode`, `pascalCase`, …), and the package root exports the IR types, so a custom generator emits TypeScript exactly as the first-party ones do — see the [`ast-toolkit-generator` example](https://github.com/Redocly/redocly-cli/tree/main/tests/e2e/generate-client/examples/ast-toolkit-generator).
142
+ The `@redocly/client-generator/generate` entry also exports the TypeScript renderers the built-ins use (`tsType`, `tsJsdoc`, `codeLiteral`, `operationSignature`, `pascalCase`, `safeIdent`).
143
+ The package root exports the IR types plus the language-neutral toolkit.
144
+ A custom generator emits TypeScript exactly as the first-party ones do.
145
+ See the [`typescript-types-generator` example](https://github.com/Redocly/redocly-cli/tree/main/tests/e2e/generate-client/examples/typescript-types-generator).
154
146
 
155
147
  ### `defineClientSetup`
156
148
 
@@ -0,0 +1,130 @@
1
+ # Writing custom client generators
2
+
3
+ A generator is a plain module: `(input) => GeneratedFile[]`. It receives the
4
+ language-agnostic API model and returns files — in ANY output language. It runs
5
+ in the same pass as the built-ins; select it by path in `redocly.yaml`:
6
+
7
+ ```yaml
8
+ client:
9
+ generators: [typescript, ./generators/my-generator.mjs]
10
+ ```
11
+
12
+ ## The contract
13
+
14
+ ```js
15
+ /** @type {import('@redocly/client-generator').CustomGenerator} */
16
+ export default {
17
+ name: 'my-generator',
18
+ run({ model, outputPath, outputMode, emit }) {
19
+ return [{ path: outputPath.replace(/\.ts$/, '.mine.txt'), content: '…' }];
20
+ },
21
+ // Optional: one idiomatic call snippet per operation for docs (x-codeSamples),
22
+ // collected into an overlay file when `client.codeSamples: true` is set.
23
+ sample(operation, { model, emit }) {
24
+ return { lang: 'python', source: '…' };
25
+ },
26
+ // Optional: the reference page for what `run` emits, written when `client.docs` (or
27
+ // --docs) is on. Same `{ path, content }` shape as `run`; `renderReferencePage` gives
28
+ // the standard layout and takes `sample` for its snippets. A generator documents itself.
29
+ docs({ model, outputPath, emit }) {
30
+ return [{ path: outputPath.replace(/\.ts$/, '.mine.md'), content: '…' }];
31
+ },
32
+ };
33
+ ```
34
+
35
+ ## Declaring options
36
+
37
+ A generator that needs configuration declares it as a schema; `run` then receives
38
+ `options` already validated, with defaults applied:
39
+
40
+ ```js
41
+ export default {
42
+ name: 'permissions-matrix',
43
+ options: {
44
+ type: 'object',
45
+ properties: { groupBy: { enum: ['tag', 'path'], default: 'tag' } },
46
+ additionalProperties: false,
47
+ },
48
+ run({ model, outputPath, options }) {
49
+ return [
50
+ { path: outputPath.replace(/\.ts$/, '.permissions.md'), content: render(options.groupBy) },
51
+ ];
52
+ },
53
+ };
54
+ ```
55
+
56
+ Users set them per generator name:
57
+
58
+ ```yaml
59
+ client:
60
+ generators: [typescript, ./generators/permissions-matrix.mjs]
61
+ options:
62
+ permissions-matrix:
63
+ groupBy: path
64
+ ```
65
+
66
+ The supported subset is a top-level `type: 'object'` with `properties`, `required`, and
67
+ `additionalProperties`; each property is a scalar (`string`/`number`/`boolean`), an
68
+ `enum`, or an array of scalars, and may carry a `default` and a `description`. Don't
69
+ validate options inside `run` — an unknown key, a wrong type, a value outside an `enum`,
70
+ or a missing `required` key already fails generation before `run` is called.
71
+
72
+ Rules: output is deterministic (same description → same bytes); never add
73
+ dependencies to the generated client; **never hand-edit generated output** —
74
+ edit this generator and regenerate. Emitted file paths must stay inside the
75
+ `--output` directory (subdirectories are fine) — escapes are rejected.
76
+ Optionally declare `requiresGenerator` — the `@redocly/client-generator` version
77
+ range you wrote this against (`'^1.2.0'`, `'~1.2.0'`, `'>=1.2.0'`, or an exact
78
+ version). A CLI outside the range then fails with the fix path instead of feeding
79
+ your generator an unexpected model shape. Ejected generators carry it
80
+ automatically; hand-written ones without it are taken as current.
81
+
82
+ ## The model (IR)
83
+
84
+ `model.services[].operations[]` — each operation carries `name`, `specName`,
85
+ `method`, `path`, `tags`, `pathParams`/`queryParams`/`headerParams`/`cookieParams`,
86
+ `requestBody`, `successResponses`/`errorResponses` (each with a `schema`), and
87
+ `security`. `model.schemas` holds the named schemas. Every schema is a
88
+ discriminated union on `kind`: `scalar`, `array`, `object`, `record`, `ref`,
89
+ `literal`, `enum`, `union` (optionally with a discriminator), `intersection`
90
+ (allOf), `null`, `unknown`, `omit`.
91
+
92
+ ## Helpers (import from '@redocly/client-generator')
93
+
94
+ | Helper | Use |
95
+ | ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
96
+ | `flattenAllOf(schema, model)` | The merged property view of allOf compositions — languages without intersection types render this. |
97
+ | `discriminatorCases(schema, model)` | `{ property, cases }` dispatch table for discriminated unions. |
98
+ | `isNullable(schema)` / `unwrapNullable(schema)` | Detect and strip `null` union members (`Optional[T]`, pointers, `Option<T>`). |
99
+ | `enumValues(schema)` | Values plus SCREAMING_SNAKE member-name suggestions. |
100
+ | `headerCoerceType(schema, model)` | Response-header coerce hint (`integer`/`number`/`boolean`/`string`) through refs, nullables, and allOf wrappers. |
101
+ | `casing` / `identifierFor(name, { style, reserved })` | camel/pascal/snake/screaming; keyword-safe identifiers (`RESERVED_WORDS.python/go/typescript` shipped). |
102
+ | `Printer` | Indentation-aware text builder — no manual whitespace bookkeeping. |
103
+ | `docText(description)` | Description as trimmed lines for any comment syntax. |
104
+ | `schemaAtPointer(schema, pointer, model)` | Resolve an RFC 6901 JSON pointer over a schema (through refs and allOf) — e.g. a pagination `items` pointer to its element type. |
105
+ | `paginationRuleFor(op, config)` | The pagination rule that applies to an operation (per-op config > extension > fitting convention), normalized. |
106
+ | `renderReferencePage(model, options)` | The Markdown reference page a generator's `docs` hook returns — your `sample` hook supplies its call snippets. |
107
+ | `NotSupportedError` | Throw it to reject an option the generator can't honor — the CLI prints the message as a user error, not a crash. |
108
+ | `AUTHORING_HELPER_NAMES` | The list of the above (introspection). |
109
+
110
+ Worked example: the built-in `python` generator
111
+ (`packages/client-generator/src/generators/python/index.ts` in the Redocly CLI repo) is
112
+ authored with exactly this toolkit and nothing else — models via `flattenAllOf`/
113
+ `enumValues`/`discriminatorCases`, all code through `Printer`, every name through
114
+ `identifierFor(..., RESERVED_WORDS.python)`.
115
+
116
+ A generator that emits TypeScript may additionally use the source-text renderers from
117
+ `@redocly/client-generator/generate` — `tsType` (schema → type), `tsJsdoc`, `codeLiteral`,
118
+ `operationSignature`, `pascalCase`, `safeIdent`. There is no AST toolkit and no
119
+ `typescript` dependency: every generator prints text through `Printer`.
120
+
121
+ ## The loop
122
+
123
+ 1. Edit the generator.
124
+ 2. Run `redocly generate-client`.
125
+ 3. Inspect `git diff` of the generated output.
126
+ 4. Repeat. Generated files are never hand-edited.
127
+
128
+ If you had to work around a **missing helper** or a wrong default, tell the user
129
+ and suggest filing an issue at https://github.com/Redocly/redocly-cli/issues —
130
+ include the generator snippet and the helper you expected to exist.