openapi-typesafe 1.0.0-rc.0 → 1.0.0-rc.1

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 (404) hide show
  1. package/dist/adapters/redocly-loader/redocly-config-source.cjs +21 -2
  2. package/dist/adapters/redocly-loader/redocly-config-source.cjs.map +1 -1
  3. package/dist/adapters/redocly-loader/redocly-config-source.mjs +22 -3
  4. package/dist/adapters/redocly-loader/redocly-config-source.mjs.map +1 -1
  5. package/dist/adapters/redocly-loader/redocly-schema-loader.cjs +15 -5
  6. package/dist/adapters/redocly-loader/redocly-schema-loader.cjs.map +1 -1
  7. package/dist/adapters/redocly-loader/redocly-schema-loader.mjs +15 -5
  8. package/dist/adapters/redocly-loader/redocly-schema-loader.mjs.map +1 -1
  9. package/dist/adapters/type-ast-bridge/ir-to-typescript.cjs +3 -0
  10. package/dist/adapters/type-ast-bridge/ir-to-typescript.cjs.map +1 -1
  11. package/dist/adapters/type-ast-bridge/ir-to-typescript.mjs +3 -0
  12. package/dist/adapters/type-ast-bridge/ir-to-typescript.mjs.map +1 -1
  13. package/dist/adapters/type-ast-bridge/typescript-to-ir.cjs +15 -9
  14. package/dist/adapters/type-ast-bridge/typescript-to-ir.cjs.map +1 -1
  15. package/dist/adapters/type-ast-bridge/typescript-to-ir.mjs +15 -9
  16. package/dist/adapters/type-ast-bridge/typescript-to-ir.mjs.map +1 -1
  17. package/dist/api/discriminator-conflict-warnings.cjs +25 -0
  18. package/dist/api/discriminator-conflict-warnings.cjs.map +1 -0
  19. package/dist/api/discriminator-conflict-warnings.mjs +23 -0
  20. package/dist/api/discriminator-conflict-warnings.mjs.map +1 -0
  21. package/dist/api/discriminator-enum-names.cjs +19 -0
  22. package/dist/api/discriminator-enum-names.cjs.map +1 -0
  23. package/dist/api/discriminator-enum-names.mjs +17 -0
  24. package/dist/api/discriminator-enum-names.mjs.map +1 -0
  25. package/dist/api/discriminator-enum-parent.cjs +59 -0
  26. package/dist/api/discriminator-enum-parent.cjs.map +1 -0
  27. package/dist/api/discriminator-enum-parent.mjs +57 -0
  28. package/dist/api/discriminator-enum-parent.mjs.map +1 -0
  29. package/dist/api/discriminator-enum.cjs +25 -0
  30. package/dist/api/discriminator-enum.cjs.map +1 -1
  31. package/dist/api/discriminator-enum.mjs +25 -1
  32. package/dist/api/discriminator-enum.mjs.map +1 -1
  33. package/dist/api/discriminator-other-owners.cjs +67 -0
  34. package/dist/api/discriminator-other-owners.cjs.map +1 -0
  35. package/dist/api/discriminator-other-owners.mjs +64 -0
  36. package/dist/api/discriminator-other-owners.mjs.map +1 -0
  37. package/dist/api/discriminator-patch.cjs +88 -0
  38. package/dist/api/discriminator-patch.cjs.map +1 -0
  39. package/dist/api/discriminator-patch.mjs +82 -0
  40. package/dist/api/discriminator-patch.mjs.map +1 -0
  41. package/dist/api/discriminator-scan.cjs +50 -65
  42. package/dist/api/discriminator-scan.cjs.map +1 -1
  43. package/dist/api/discriminator-scan.mjs +44 -59
  44. package/dist/api/discriminator-scan.mjs.map +1 -1
  45. package/dist/api/document-run.cjs +18 -3
  46. package/dist/api/document-run.cjs.map +1 -1
  47. package/dist/api/document-run.mjs +19 -4
  48. package/dist/api/document-run.mjs.map +1 -1
  49. package/dist/api/enum-values-warnings.cjs +16 -0
  50. package/dist/api/enum-values-warnings.cjs.map +1 -0
  51. package/dist/api/enum-values-warnings.mjs +14 -0
  52. package/dist/api/enum-values-warnings.mjs.map +1 -0
  53. package/dist/api/path-template-warnings.cjs +21 -0
  54. package/dist/api/path-template-warnings.cjs.map +1 -0
  55. package/dist/api/path-template-warnings.mjs +19 -0
  56. package/dist/api/path-template-warnings.mjs.map +1 -0
  57. package/dist/api/transform-context.cjs +3 -1
  58. package/dist/api/transform-context.cjs.map +1 -1
  59. package/dist/api/transform-context.mjs +3 -1
  60. package/dist/api/transform-context.mjs.map +1 -1
  61. package/dist/api/transform-schema.cjs +52 -3
  62. package/dist/api/transform-schema.cjs.map +1 -1
  63. package/dist/api/transform-schema.mjs +53 -4
  64. package/dist/api/transform-schema.mjs.map +1 -1
  65. package/dist/api/unsupported-feature-warnings.cjs +6 -1
  66. package/dist/api/unsupported-feature-warnings.cjs.map +1 -1
  67. package/dist/api/unsupported-feature-warnings.mjs +6 -1
  68. package/dist/api/unsupported-feature-warnings.mjs.map +1 -1
  69. package/dist/application/print/type-printer.cjs +5 -0
  70. package/dist/application/print/type-printer.cjs.map +1 -1
  71. package/dist/application/print/type-printer.mjs +5 -0
  72. package/dist/application/print/type-printer.mjs.map +1 -1
  73. package/dist/application/transform/document/assignability.cjs +144 -0
  74. package/dist/application/transform/document/assignability.cjs.map +1 -0
  75. package/dist/application/transform/document/assignability.mjs +138 -0
  76. package/dist/application/transform/document/assignability.mjs.map +1 -0
  77. package/dist/application/transform/document/components.cjs +2 -2
  78. package/dist/application/transform/document/components.cjs.map +1 -1
  79. package/dist/application/transform/document/components.mjs +4 -4
  80. package/dist/application/transform/document/components.mjs.map +1 -1
  81. package/dist/application/transform/document/declared-types.cjs +66 -0
  82. package/dist/application/transform/document/declared-types.cjs.map +1 -0
  83. package/dist/application/transform/document/declared-types.mjs +63 -0
  84. package/dist/application/transform/document/declared-types.mjs.map +1 -0
  85. package/dist/application/transform/document/document.cjs +32 -9
  86. package/dist/application/transform/document/document.cjs.map +1 -1
  87. package/dist/application/transform/document/document.mjs +34 -11
  88. package/dist/application/transform/document/document.mjs.map +1 -1
  89. package/dist/application/transform/document/exploded-query.cjs +46 -0
  90. package/dist/application/transform/document/exploded-query.cjs.map +1 -0
  91. package/dist/application/transform/document/exploded-query.mjs +43 -0
  92. package/dist/application/transform/document/exploded-query.mjs.map +1 -0
  93. package/dist/application/transform/document/global-type-references.cjs +63 -0
  94. package/dist/application/transform/document/global-type-references.cjs.map +1 -0
  95. package/dist/application/transform/document/global-type-references.mjs +61 -0
  96. package/dist/application/transform/document/global-type-references.mjs.map +1 -0
  97. package/dist/application/transform/document/list-assignability.cjs +54 -0
  98. package/dist/application/transform/document/list-assignability.cjs.map +1 -0
  99. package/dist/application/transform/document/list-assignability.mjs +52 -0
  100. package/dist/application/transform/document/list-assignability.mjs.map +1 -0
  101. package/dist/application/transform/document/parameters.cjs +40 -8
  102. package/dist/application/transform/document/parameters.cjs.map +1 -1
  103. package/dist/application/transform/document/parameters.mjs +41 -9
  104. package/dist/application/transform/document/parameters.mjs.map +1 -1
  105. package/dist/application/transform/document/path-key-overlaps.cjs +83 -33
  106. package/dist/application/transform/document/path-key-overlaps.cjs.map +1 -1
  107. package/dist/application/transform/document/path-key-overlaps.mjs +83 -33
  108. package/dist/application/transform/document/path-key-overlaps.mjs.map +1 -1
  109. package/dist/application/transform/document/path-template-replace.cjs +48 -0
  110. package/dist/application/transform/document/path-template-replace.cjs.map +1 -0
  111. package/dist/application/transform/document/path-template-replace.mjs +45 -0
  112. package/dist/application/transform/document/path-template-replace.mjs.map +1 -0
  113. package/dist/application/transform/document/paths.cjs +54 -28
  114. package/dist/application/transform/document/paths.cjs.map +1 -1
  115. package/dist/application/transform/document/paths.mjs +53 -29
  116. package/dist/application/transform/document/paths.mjs.map +1 -1
  117. package/dist/application/transform/document/root-type-aliases.cjs +15 -14
  118. package/dist/application/transform/document/root-type-aliases.cjs.map +1 -1
  119. package/dist/application/transform/document/root-type-aliases.mjs +15 -13
  120. package/dist/application/transform/document/root-type-aliases.mjs.map +1 -1
  121. package/dist/application/transform/document/template-key-match.cjs +128 -0
  122. package/dist/application/transform/document/template-key-match.cjs.map +1 -0
  123. package/dist/application/transform/document/template-key-match.mjs +125 -0
  124. package/dist/application/transform/document/template-key-match.mjs.map +1 -0
  125. package/dist/application/transform/document/type-assignability.cjs +192 -0
  126. package/dist/application/transform/document/type-assignability.cjs.map +1 -0
  127. package/dist/application/transform/document/type-assignability.mjs +190 -0
  128. package/dist/application/transform/document/type-assignability.mjs.map +1 -0
  129. package/dist/application/transform/document/type-key.cjs +70 -0
  130. package/dist/application/transform/document/type-key.cjs.map +1 -0
  131. package/dist/application/transform/document/type-key.mjs +68 -0
  132. package/dist/application/transform/document/type-key.mjs.map +1 -0
  133. package/dist/application/transform/document/type-resolution.cjs +131 -0
  134. package/dist/application/transform/document/type-resolution.cjs.map +1 -0
  135. package/dist/application/transform/document/type-resolution.mjs +125 -0
  136. package/dist/application/transform/document/type-resolution.mjs.map +1 -0
  137. package/dist/application/transform/footer/document-footer.cjs +66 -4
  138. package/dist/application/transform/footer/document-footer.cjs.map +1 -1
  139. package/dist/application/transform/footer/document-footer.mjs +67 -5
  140. package/dist/application/transform/footer/document-footer.mjs.map +1 -1
  141. package/dist/application/transform/footer/enum-naming.cjs +17 -0
  142. package/dist/application/transform/footer/enum-naming.cjs.map +1 -1
  143. package/dist/application/transform/footer/enum-naming.mjs +17 -1
  144. package/dist/application/transform/footer/enum-naming.mjs.map +1 -1
  145. package/dist/application/transform/footer/enum-values-check.cjs +129 -0
  146. package/dist/application/transform/footer/enum-values-check.cjs.map +1 -0
  147. package/dist/application/transform/footer/enum-values-check.mjs +127 -0
  148. package/dist/application/transform/footer/enum-values-check.mjs.map +1 -0
  149. package/dist/application/transform/footer/evaluation.cjs +33 -0
  150. package/dist/application/transform/footer/evaluation.cjs.map +1 -0
  151. package/dist/application/transform/footer/evaluation.mjs +26 -0
  152. package/dist/application/transform/footer/evaluation.mjs.map +1 -0
  153. package/dist/application/transform/footer/shared-enum-names.cjs +60 -0
  154. package/dist/application/transform/footer/shared-enum-names.cjs.map +1 -0
  155. package/dist/application/transform/footer/shared-enum-names.mjs +57 -0
  156. package/dist/application/transform/footer/shared-enum-names.mjs.map +1 -0
  157. package/dist/application/transform/footer/type-evaluation.cjs +246 -0
  158. package/dist/application/transform/footer/type-evaluation.cjs.map +1 -0
  159. package/dist/application/transform/footer/type-evaluation.mjs +244 -0
  160. package/dist/application/transform/footer/type-evaluation.mjs.map +1 -0
  161. package/dist/application/transform/footer/type-rules.cjs +172 -0
  162. package/dist/application/transform/footer/type-rules.cjs.map +1 -0
  163. package/dist/application/transform/footer/type-rules.mjs +166 -0
  164. package/dist/application/transform/footer/type-rules.mjs.map +1 -0
  165. package/dist/application/transform/ref-pointer/generated-location.cjs +138 -0
  166. package/dist/application/transform/ref-pointer/generated-location.cjs.map +1 -0
  167. package/dist/application/transform/ref-pointer/generated-location.mjs +136 -0
  168. package/dist/application/transform/ref-pointer/generated-location.mjs.map +1 -0
  169. package/dist/application/transform/ref-pointer/generated-shape.cjs +171 -0
  170. package/dist/application/transform/ref-pointer/generated-shape.cjs.map +1 -0
  171. package/dist/application/transform/ref-pointer/generated-shape.mjs +162 -0
  172. package/dist/application/transform/ref-pointer/generated-shape.mjs.map +1 -0
  173. package/dist/application/transform/ref-pointer/inline-cycles.cjs +44 -0
  174. package/dist/application/transform/ref-pointer/inline-cycles.cjs.map +1 -0
  175. package/dist/application/transform/ref-pointer/inline-cycles.mjs +42 -0
  176. package/dist/application/transform/ref-pointer/inline-cycles.mjs.map +1 -0
  177. package/dist/application/transform/ref-pointer/original-index.cjs +62 -0
  178. package/dist/application/transform/ref-pointer/original-index.cjs.map +1 -0
  179. package/dist/application/transform/ref-pointer/original-index.mjs +60 -0
  180. package/dist/application/transform/ref-pointer/original-index.mjs.map +1 -0
  181. package/dist/application/transform/ref-pointer/pointer-type.cjs +60 -0
  182. package/dist/application/transform/ref-pointer/pointer-type.cjs.map +1 -0
  183. package/dist/application/transform/ref-pointer/pointer-type.mjs +57 -0
  184. package/dist/application/transform/ref-pointer/pointer-type.mjs.map +1 -0
  185. package/dist/application/transform/ref-pointer/pointer-walk.cjs +66 -0
  186. package/dist/application/transform/ref-pointer/pointer-walk.cjs.map +1 -0
  187. package/dist/application/transform/ref-pointer/pointer-walk.mjs +56 -0
  188. package/dist/application/transform/ref-pointer/pointer-walk.mjs.map +1 -0
  189. package/dist/application/transform/ref-pointer/typed-walk.cjs +124 -0
  190. package/dist/application/transform/ref-pointer/typed-walk.cjs.map +1 -0
  191. package/dist/application/transform/ref-pointer/typed-walk.mjs +122 -0
  192. package/dist/application/transform/ref-pointer/typed-walk.mjs.map +1 -0
  193. package/dist/application/transform/ref-type.cjs +7 -2
  194. package/dist/application/transform/ref-type.cjs.map +1 -1
  195. package/dist/application/transform/ref-type.mjs +3 -2
  196. package/dist/application/transform/ref-type.mjs.map +1 -1
  197. package/dist/application/transform/schema/access-flags.cjs +38 -0
  198. package/dist/application/transform/schema/access-flags.cjs.map +1 -0
  199. package/dist/application/transform/schema/access-flags.mjs +36 -0
  200. package/dist/application/transform/schema/access-flags.mjs.map +1 -0
  201. package/dist/application/transform/schema/array-type.cjs +32 -1
  202. package/dist/application/transform/schema/array-type.cjs.map +1 -1
  203. package/dist/application/transform/schema/array-type.mjs +33 -2
  204. package/dist/application/transform/schema/array-type.mjs.map +1 -1
  205. package/dist/application/transform/schema/composition-type.cjs +50 -15
  206. package/dist/application/transform/schema/composition-type.cjs.map +1 -1
  207. package/dist/application/transform/schema/composition-type.mjs +50 -15
  208. package/dist/application/transform/schema/composition-type.mjs.map +1 -1
  209. package/dist/application/transform/schema/core-type.cjs +3 -1
  210. package/dist/application/transform/schema/core-type.cjs.map +1 -1
  211. package/dist/application/transform/schema/core-type.mjs +3 -1
  212. package/dist/application/transform/schema/core-type.mjs.map +1 -1
  213. package/dist/application/transform/schema/discriminated-union.cjs +30 -0
  214. package/dist/application/transform/schema/discriminated-union.cjs.map +1 -0
  215. package/dist/application/transform/schema/discriminated-union.mjs +27 -0
  216. package/dist/application/transform/schema/discriminated-union.mjs.map +1 -0
  217. package/dist/application/transform/schema/discriminator-property.cjs +3 -2
  218. package/dist/application/transform/schema/discriminator-property.cjs.map +1 -1
  219. package/dist/application/transform/schema/discriminator-property.mjs +3 -2
  220. package/dist/application/transform/schema/discriminator-property.mjs.map +1 -1
  221. package/dist/application/transform/schema/enum-rule.cjs +66 -10
  222. package/dist/application/transform/schema/enum-rule.cjs.map +1 -1
  223. package/dist/application/transform/schema/enum-rule.mjs +67 -12
  224. package/dist/application/transform/schema/enum-rule.mjs.map +1 -1
  225. package/dist/application/transform/schema/inherited-discriminator.cjs +29 -0
  226. package/dist/application/transform/schema/inherited-discriminator.cjs.map +1 -0
  227. package/dist/application/transform/schema/inherited-discriminator.mjs +26 -0
  228. package/dist/application/transform/schema/inherited-discriminator.mjs.map +1 -0
  229. package/dist/application/transform/schema/object-type.cjs +38 -25
  230. package/dist/application/transform/schema/object-type.cjs.map +1 -1
  231. package/dist/application/transform/schema/object-type.mjs +39 -27
  232. package/dist/application/transform/schema/object-type.mjs.map +1 -1
  233. package/dist/application/transform/schema/required-variants.cjs +45 -0
  234. package/dist/application/transform/schema/required-variants.cjs.map +1 -0
  235. package/dist/application/transform/schema/required-variants.mjs +42 -0
  236. package/dist/application/transform/schema/required-variants.mjs.map +1 -0
  237. package/dist/application/transform/schema/schema-transform.cjs +2 -2
  238. package/dist/application/transform/schema/schema-transform.cjs.map +1 -1
  239. package/dist/application/transform/schema/schema-transform.mjs +2 -2
  240. package/dist/application/transform/schema/schema-transform.mjs.map +1 -1
  241. package/dist/application/transform/schema-comment.cjs +51 -5
  242. package/dist/application/transform/schema-comment.cjs.map +1 -1
  243. package/dist/application/transform/schema-comment.mjs +50 -6
  244. package/dist/application/transform/schema-comment.mjs.map +1 -1
  245. package/dist/application/transform/type-combinators.mjs +1 -1
  246. package/dist/cli/cli-arguments.cjs +11 -3
  247. package/dist/cli/cli-arguments.cjs.map +1 -1
  248. package/dist/cli/cli-arguments.mjs +11 -3
  249. package/dist/cli/cli-arguments.mjs.map +1 -1
  250. package/dist/cli/cli-errors.cjs +3 -2
  251. package/dist/cli/cli-errors.cjs.map +1 -1
  252. package/dist/cli/cli-errors.mjs +3 -2
  253. package/dist/cli/cli-errors.mjs.map +1 -1
  254. package/dist/cli/cli-output.cjs +8 -3
  255. package/dist/cli/cli-output.cjs.map +1 -1
  256. package/dist/cli/cli-output.mjs +9 -4
  257. package/dist/cli/cli-output.mjs.map +1 -1
  258. package/dist/cli/main.cjs +5 -0
  259. package/dist/cli/main.cjs.map +1 -1
  260. package/dist/cli/main.mjs +5 -0
  261. package/dist/cli/main.mjs.map +1 -1
  262. package/dist/cli/redocly-apis.cjs +7 -3
  263. package/dist/cli/redocly-apis.cjs.map +1 -1
  264. package/dist/cli/redocly-apis.mjs +8 -4
  265. package/dist/cli/redocly-apis.mjs.map +1 -1
  266. package/dist/cli/ref-limits.cjs +1 -1
  267. package/dist/cli/ref-limits.cjs.map +1 -1
  268. package/dist/cli/ref-limits.mjs +2 -2
  269. package/dist/cli/ref-limits.mjs.map +1 -1
  270. package/dist/cli/run-cli.cjs +15 -2
  271. package/dist/cli/run-cli.cjs.map +1 -1
  272. package/dist/cli/run-cli.mjs +16 -3
  273. package/dist/cli/run-cli.mjs.map +1 -1
  274. package/dist/domain/errors.cjs +2 -0
  275. package/dist/domain/errors.cjs.map +1 -1
  276. package/dist/domain/errors.mjs +2 -0
  277. package/dist/domain/errors.mjs.map +1 -1
  278. package/dist/domain/ir/constructors.cjs +5 -1
  279. package/dist/domain/ir/constructors.cjs.map +1 -1
  280. package/dist/domain/ir/constructors.mjs +5 -2
  281. package/dist/domain/ir/constructors.mjs.map +1 -1
  282. package/dist/domain/openapi/admitted-values.cjs +75 -0
  283. package/dist/domain/openapi/admitted-values.cjs.map +1 -0
  284. package/dist/domain/openapi/admitted-values.mjs +72 -0
  285. package/dist/domain/openapi/admitted-values.mjs.map +1 -0
  286. package/dist/domain/openapi/discriminated-union.cjs +68 -0
  287. package/dist/domain/openapi/discriminated-union.cjs.map +1 -0
  288. package/dist/domain/openapi/discriminated-union.mjs +62 -0
  289. package/dist/domain/openapi/discriminated-union.mjs.map +1 -0
  290. package/dist/domain/openapi/discriminator-conflicts.cjs +143 -0
  291. package/dist/domain/openapi/discriminator-conflicts.cjs.map +1 -0
  292. package/dist/domain/openapi/discriminator-conflicts.mjs +141 -0
  293. package/dist/domain/openapi/discriminator-conflicts.mjs.map +1 -0
  294. package/dist/domain/openapi/parameter-object.cjs.map +1 -1
  295. package/dist/domain/openapi/parameter-object.mjs.map +1 -1
  296. package/dist/domain/openapi/path-templates.cjs +23 -0
  297. package/dist/domain/openapi/path-templates.cjs.map +1 -0
  298. package/dist/domain/openapi/path-templates.mjs +21 -0
  299. package/dist/domain/openapi/path-templates.mjs.map +1 -0
  300. package/dist/domain/output-fixes.cjs +36 -1
  301. package/dist/domain/output-fixes.cjs.map +1 -1
  302. package/dist/domain/output-fixes.mjs +36 -1
  303. package/dist/domain/output-fixes.mjs.map +1 -1
  304. package/dist/domain/warnings.cjs +15 -0
  305. package/dist/domain/warnings.cjs.map +1 -0
  306. package/dist/domain/warnings.mjs +12 -0
  307. package/dist/domain/warnings.mjs.map +1 -0
  308. package/dist/index.d.cts +29 -0
  309. package/dist/index.d.mts +29 -0
  310. package/dist/index.d.ts +29 -0
  311. package/dist/transform/components-object.cjs +2 -1
  312. package/dist/transform/components-object.cjs.map +1 -1
  313. package/dist/transform/components-object.mjs +2 -1
  314. package/dist/transform/components-object.mjs.map +1 -1
  315. package/package.json +1 -1
  316. package/src/adapters/redocly-loader/redocly-config-source.ts +41 -6
  317. package/src/adapters/redocly-loader/redocly-schema-loader.ts +22 -6
  318. package/src/adapters/type-ast-bridge/ir-to-typescript.ts +3 -0
  319. package/src/adapters/type-ast-bridge/typescript-to-ir.ts +26 -12
  320. package/src/api/discriminator-conflict-warnings.ts +43 -0
  321. package/src/api/discriminator-enum-names.ts +26 -0
  322. package/src/api/discriminator-enum-parent.ts +129 -0
  323. package/src/api/discriminator-enum.ts +39 -0
  324. package/src/api/discriminator-other-owners.ts +97 -0
  325. package/src/api/discriminator-patch.ts +147 -0
  326. package/src/api/discriminator-scan.ts +78 -90
  327. package/src/api/document-run.ts +24 -6
  328. package/src/api/enum-values-warnings.ts +21 -0
  329. package/src/api/path-template-warnings.ts +27 -0
  330. package/src/api/transform-context.ts +7 -1
  331. package/src/api/transform-schema.ts +88 -4
  332. package/src/api/unsupported-feature-warnings.ts +8 -1
  333. package/src/application/ports/redocly-config-source.ts +5 -1
  334. package/src/application/print/type-printer.ts +6 -0
  335. package/src/application/transform/document/assignability.ts +203 -0
  336. package/src/application/transform/document/components.ts +6 -6
  337. package/src/application/transform/document/declared-types.ts +93 -0
  338. package/src/application/transform/document/document.ts +71 -13
  339. package/src/application/transform/document/exploded-query.ts +91 -0
  340. package/src/application/transform/document/global-type-references.ts +78 -0
  341. package/src/application/transform/document/list-assignability.ts +87 -0
  342. package/src/application/transform/document/parameters.ts +76 -11
  343. package/src/application/transform/document/path-key-overlaps.ts +148 -59
  344. package/src/application/transform/document/path-template-replace.ts +90 -0
  345. package/src/application/transform/document/paths.ts +99 -37
  346. package/src/application/transform/document/root-type-aliases.ts +29 -28
  347. package/src/application/transform/document/template-key-match.ts +183 -0
  348. package/src/application/transform/document/type-assignability.ts +268 -0
  349. package/src/application/transform/document/type-key.ts +80 -0
  350. package/src/application/transform/document/type-resolution.ts +163 -0
  351. package/src/application/transform/footer/document-footer.ts +91 -6
  352. package/src/application/transform/footer/enum-naming.ts +29 -0
  353. package/src/application/transform/footer/enum-values-check.ts +171 -0
  354. package/src/application/transform/footer/evaluation.ts +47 -0
  355. package/src/application/transform/footer/shared-enum-names.ts +86 -0
  356. package/src/application/transform/footer/type-evaluation.ts +294 -0
  357. package/src/application/transform/footer/type-rules.ts +269 -0
  358. package/src/application/transform/ref-pointer/generated-location.ts +211 -0
  359. package/src/application/transform/ref-pointer/generated-shape.ts +275 -0
  360. package/src/application/transform/ref-pointer/inline-cycles.ts +56 -0
  361. package/src/application/transform/ref-pointer/original-index.ts +95 -0
  362. package/src/application/transform/ref-pointer/pointer-type.ts +92 -0
  363. package/src/application/transform/ref-pointer/pointer-walk.ts +96 -0
  364. package/src/application/transform/ref-pointer/typed-walk.ts +192 -0
  365. package/src/application/transform/ref-type.ts +11 -6
  366. package/src/application/transform/schema/access-flags.ts +63 -0
  367. package/src/application/transform/schema/array-type.ts +66 -5
  368. package/src/application/transform/schema/composition-type.ts +96 -24
  369. package/src/application/transform/schema/core-type.ts +8 -4
  370. package/src/application/transform/schema/discriminated-union.ts +58 -0
  371. package/src/application/transform/schema/discriminator-property.ts +8 -3
  372. package/src/application/transform/schema/enum-rule.ts +136 -13
  373. package/src/application/transform/schema/inherited-discriminator.ts +54 -0
  374. package/src/application/transform/schema/object-type.ts +51 -33
  375. package/src/application/transform/schema/required-variants.ts +88 -0
  376. package/src/application/transform/schema/schema-context.ts +26 -0
  377. package/src/application/transform/schema/schema-transform.ts +2 -2
  378. package/src/application/transform/schema-comment.ts +84 -4
  379. package/src/cli/cli-arguments.ts +13 -3
  380. package/src/cli/cli-dependencies.ts +5 -0
  381. package/src/cli/cli-errors.ts +4 -2
  382. package/src/cli/cli-output.ts +18 -4
  383. package/src/cli/main.ts +6 -0
  384. package/src/cli/redocly-apis.ts +8 -4
  385. package/src/cli/ref-limits.ts +2 -2
  386. package/src/cli/run-cli.ts +20 -3
  387. package/src/domain/errors.ts +2 -0
  388. package/src/domain/ir/constructors.ts +6 -1
  389. package/src/domain/ir/nodes.ts +2 -1
  390. package/src/domain/openapi/admitted-values.ts +113 -0
  391. package/src/domain/openapi/discriminated-union.ts +109 -0
  392. package/src/domain/openapi/discriminator-conflicts.ts +251 -0
  393. package/src/domain/openapi/parameter-object.ts +4 -0
  394. package/src/domain/openapi/path-templates.ts +31 -0
  395. package/src/domain/openapi/schema-object.ts +2 -0
  396. package/src/domain/output-fixes.ts +43 -2
  397. package/src/domain/warnings.ts +20 -0
  398. package/src/transform/components-object.ts +3 -1
  399. package/src/types/generator-options.ts +11 -0
  400. package/dist/application/transform/document/path-item-conflicts.cjs +0 -51
  401. package/dist/application/transform/document/path-item-conflicts.cjs.map +0 -1
  402. package/dist/application/transform/document/path-item-conflicts.mjs +0 -49
  403. package/dist/application/transform/document/path-item-conflicts.mjs.map +0 -1
  404. package/src/application/transform/document/path-item-conflicts.ts +0 -79
@@ -0,0 +1,268 @@
1
+ import { irBoolean } from "../../../domain/ir/constructors.js";
2
+ import type {
3
+ IrIndexSignature,
4
+ IrObjectType,
5
+ IrPropertySignature,
6
+ IrType,
7
+ IrUnionType,
8
+ } from "../../../domain/ir/nodes.js";
9
+ import {
10
+ type Assignability,
11
+ crossCategoryRelation,
12
+ everyOf,
13
+ primitiveRelation,
14
+ someOf,
15
+ } from "./assignability.js";
16
+ import type { DeclaredTypeLookup } from "./declared-types.js";
17
+ import { type IrListType, relateLists } from "./list-assignability.js";
18
+ import { createTypeKey } from "./type-key.js";
19
+ import {
20
+ indexSignaturesOf,
21
+ nameOf,
22
+ propertiesOf,
23
+ readType,
24
+ TypeResolution,
25
+ } from "./type-resolution.js";
26
+
27
+ /**
28
+ * Whether TypeScript accepts one type where another is expected (`strict`, without
29
+ * `exactOptionalPropertyTypes`), for the `--path-params-as-types` overlap check. It follows the
30
+ * checker's rules for the types the generator writes: unions, intersections, objects with optional
31
+ * properties and index signatures, weak types, arrays, tuples, literals and keywords, and the
32
+ * references to declarations of the document. A pair it does not model (a utility type, raw text)
33
+ * is undecided unless both are the same type, and an undecided pair keeps the original output.
34
+ */
35
+
36
+ /** Results by source type, then by target type. */
37
+ type PairResults = Map<IrType, Map<IrType, Assignability>>;
38
+
39
+ function resultOf(results: PairResults, source: IrType, target: IrType): Assignability | undefined {
40
+ return results.get(source)?.get(target);
41
+ }
42
+
43
+ function setResult(
44
+ results: PairResults,
45
+ pair: { readonly source: IrType; readonly target: IrType },
46
+ value: Assignability,
47
+ ): void {
48
+ const byTarget = results.get(pair.source) ?? new Map<IrType, Assignability>();
49
+ byTarget.set(pair.target, value);
50
+ results.set(pair.source, byTarget);
51
+ }
52
+
53
+ /** A weak type has only optional properties; TypeScript rejects a source sharing none (TS2559). */
54
+ function isWeakTypeRejected(source: IrObjectType, target: IrObjectType): boolean {
55
+ const targetProperties = [...propertiesOf(target).values()];
56
+ const isWeak =
57
+ targetProperties.length > 0 &&
58
+ indexSignaturesOf(target).length === 0 &&
59
+ targetProperties.every((property) => property.isOptional);
60
+ const sourceNames = [...propertiesOf(source).keys()];
61
+ return (
62
+ isWeak &&
63
+ sourceNames.length > 0 &&
64
+ !sourceNames.some((name) => targetProperties.some((property) => nameOf(property) === name))
65
+ );
66
+ }
67
+
68
+ function isStringIndex(signature: IrIndexSignature): boolean {
69
+ return signature.parameterType.kind === "keyword" && signature.parameterType.keyword === "string";
70
+ }
71
+
72
+ /** `boolean` as the checker stores it: the union `true | false`. */
73
+ const BOOLEAN_MEMBERS: readonly IrType[] = [irBoolean(true), irBoolean(false)];
74
+
75
+ /**
76
+ * The members a source is compared by, one by one, or null for a source compared as a whole.
77
+ * `boolean` counts as `true | false` against a union, which it fits though it fits neither member
78
+ * alone; `string` and `number` are no finite unions, so no literal union holds all their values.
79
+ */
80
+ function sourceUnionMembers(source: IrType, target: IrType): readonly IrType[] | null {
81
+ if (source.kind === "union") {
82
+ return source.types;
83
+ }
84
+ const isBoolean = source.kind === "keyword" && source.keyword === "boolean";
85
+ return isBoolean && target.kind === "union" ? BOOLEAN_MEMBERS : null;
86
+ }
87
+
88
+ /** The memoized check over the declarations of one generated file. */
89
+ class AssignabilityCheck {
90
+ private readonly inProgress: PairResults = new Map();
91
+ private readonly results: PairResults = new Map();
92
+ private readonly typeKeyOf = createTypeKey();
93
+ private readonly resolution: TypeResolution;
94
+ /** How often a pair being compared was assumed related, and how deep the comparison is. */
95
+ private assumptions = 0;
96
+ private depth = 0;
97
+
98
+ constructor(lookup: DeclaredTypeLookup) {
99
+ this.resolution = new TypeResolution(lookup);
100
+ }
101
+
102
+ relate(source: IrType, target: IrType): Assignability {
103
+ const pair = {
104
+ source: this.resolution.resolved(source),
105
+ target: this.resolution.resolved(target),
106
+ };
107
+ if (
108
+ pair.source === pair.target ||
109
+ this.typeKeyOf(pair.source) === this.typeKeyOf(pair.target)
110
+ ) {
111
+ return "assignable";
112
+ }
113
+ const known = resultOf(this.results, pair.source, pair.target);
114
+ if (known !== undefined) {
115
+ return known;
116
+ }
117
+ // A pair being compared is assumed related, as the checker does for recursive types.
118
+ if (resultOf(this.inProgress, pair.source, pair.target) !== undefined) {
119
+ this.assumptions += 1;
120
+ return "assignable";
121
+ }
122
+ const assumedBefore = this.assumptions;
123
+ setResult(this.inProgress, pair, "assignable");
124
+ this.depth += 1;
125
+ const result = this.compare(pair.source, pair.target);
126
+ this.depth -= 1;
127
+ this.inProgress.get(pair.source)?.delete(pair.target);
128
+ // "Assignable" that rests on an assumption about an outer pair holds only if that pair does,
129
+ // so it is kept only once no outer pair is open. The assumption can only make a pair more
130
+ // related, so a rejection or an undecided pair reached under it is safe to keep.
131
+ if (result !== "assignable" || this.assumptions === assumedBefore || this.depth === 0) {
132
+ setResult(this.results, pair, result);
133
+ }
134
+ return result;
135
+ }
136
+
137
+ private compare(source: IrType, target: IrType): Assignability {
138
+ if (target.kind === "keyword" && target.keyword === "unknown") {
139
+ return "assignable";
140
+ }
141
+ if (source.kind === "keyword" && source.keyword === "never") {
142
+ return "assignable";
143
+ }
144
+ const sourceMembers = sourceUnionMembers(source, target);
145
+ if (sourceMembers !== null) {
146
+ return everyOf(sourceMembers, (member) => this.relate(member, target));
147
+ }
148
+ if (target.kind === "intersection") {
149
+ return everyOf(target.types, (member) => this.relateToIntersectionPart(source, member));
150
+ }
151
+ if (target.kind === "union") {
152
+ return this.relateToUnion(source, target);
153
+ }
154
+ if (source.kind === "intersection") {
155
+ // A part that fits is enough; the parts as one type are not modelled (see `merged`).
156
+ const fits = source.types.some((part) => this.relate(part, target) === "assignable");
157
+ return fits ? "assignable" : "undecided";
158
+ }
159
+ return this.compareSingle(source, target);
160
+ }
161
+
162
+ /**
163
+ * A part of an intersection the check cannot merge into one object. The checker skips the weak
164
+ * type check for such a part, so a rejection that may rest on it decides nothing.
165
+ */
166
+ private relateToIntersectionPart(source: IrType, part: IrType): Assignability {
167
+ const result = this.relate(source, part);
168
+ const from = this.resolution.resolved(source);
169
+ const to = this.resolution.resolved(part);
170
+ const isWeakRejection =
171
+ from.kind === "object" && to.kind === "object" && isWeakTypeRejected(from, to);
172
+ return result === "rejected" && isWeakRejection ? "undecided" : result;
173
+ }
174
+
175
+ /**
176
+ * An object may fit a union of objects that none of its members accepts alone, through a
177
+ * discriminant property (`{ k: "a" | "b" }` fits `{ k: "a" } | { k: "b" }`); not modelled.
178
+ */
179
+ private relateToUnion(source: IrType, target: IrUnionType): Assignability {
180
+ const result = someOf(target.types, (member) => this.relate(source, member));
181
+ const objectMembers = target.types.filter(
182
+ (member) => this.resolution.resolved(member).kind === "object",
183
+ );
184
+ const mayBeDiscriminated = source.kind === "object" && objectMembers.length > 1;
185
+ return result === "rejected" && mayBeDiscriminated ? "undecided" : result;
186
+ }
187
+
188
+ private compareSingle(source: IrType, target: IrType): Assignability {
189
+ const crossCategory = crossCategoryRelation(source, target);
190
+ if (crossCategory !== null) {
191
+ return crossCategory;
192
+ }
193
+ switch (source.kind) {
194
+ case "array":
195
+ case "tuple":
196
+ case "readonly":
197
+ return relateLists(source, target as IrListType, (from, to) => this.relate(from, to));
198
+ case "object":
199
+ return this.relateObjects(source, target as IrObjectType);
200
+ default:
201
+ return primitiveRelation(source, target);
202
+ }
203
+ }
204
+
205
+ private relateObjects(source: IrObjectType, target: IrObjectType): Assignability {
206
+ if (isWeakTypeRejected(source, target)) {
207
+ return "rejected";
208
+ }
209
+ const sourceProperties = propertiesOf(source);
210
+ const indexes = everyOf(indexSignaturesOf(target), (signature) =>
211
+ this.relateToIndex(source, signature),
212
+ );
213
+ if (indexes === "rejected") {
214
+ return indexes;
215
+ }
216
+ const properties = everyOf([...propertiesOf(target).values()], (property) =>
217
+ this.relateProperty(sourceProperties.get(nameOf(property)), property),
218
+ );
219
+ return everyOf([indexes, properties], (result) => result);
220
+ }
221
+
222
+ /**
223
+ * A string index signature of the target. A string index of the source is compared with it;
224
+ * without one, every property and index signature of the source is. An optional property is
225
+ * compared without the `undefined` its optionality adds, as the checker does.
226
+ */
227
+ private relateToIndex(source: IrObjectType, signature: IrIndexSignature): Assignability {
228
+ if (!isStringIndex(signature)) {
229
+ return "undecided";
230
+ }
231
+ const indexes = indexSignaturesOf(source);
232
+ const own = indexes.find(isStringIndex);
233
+ if (own !== undefined) {
234
+ return this.relate(own.type, signature.type);
235
+ }
236
+ const types = [
237
+ ...[...propertiesOf(source).values()].map((property) => property.type),
238
+ ...indexes.map((index) => index.type),
239
+ ];
240
+ return everyOf(types, (type) => this.relate(type, signature.type));
241
+ }
242
+
243
+ /**
244
+ * A property of the target. A missing optional one is not checked, not even against a string
245
+ * index of the source; a missing or optional source property fails a required one (TS2741).
246
+ */
247
+ private relateProperty(
248
+ source: IrPropertySignature | undefined,
249
+ target: IrPropertySignature,
250
+ ): Assignability {
251
+ if (source === undefined) {
252
+ return target.isOptional ? "assignable" : "rejected";
253
+ }
254
+ if (source.isOptional && !target.isOptional) {
255
+ return "rejected";
256
+ }
257
+ return this.relate(readType(source), readType(target));
258
+ }
259
+ }
260
+
261
+ /** What the check proves about a source type where a target is expected. */
262
+ export type AssignabilityProof = (source: IrType, target: IrType) => Assignability;
263
+
264
+ /** One memoized check, for all the comparisons of a `paths` type. */
265
+ export function assignabilityProof(lookup: DeclaredTypeLookup): AssignabilityProof {
266
+ const check = new AssignabilityCheck(lookup);
267
+ return (source, target) => check.relate(source, target);
268
+ }
@@ -0,0 +1,80 @@
1
+ import { assertNever } from "../../../domain/assert-never.js";
2
+ import type { IrMember, IrPropertyName, IrTupleElement, IrType } from "../../../domain/ir/nodes.js";
3
+
4
+ /**
5
+ * A text that is the same for two types exactly when they are written the same, comments left
6
+ * out: the overlap check accepts a type it does not model when it is the same as the other.
7
+ */
8
+
9
+ function nameKey(name: IrPropertyName): string {
10
+ return JSON.stringify(name.kind === "string" ? name.value : name.text);
11
+ }
12
+
13
+ function memberKey(member: IrMember, typeKeyOf: TypeKey): string {
14
+ if (member.kind === "indexSignature") {
15
+ const readonly = member.isReadonly ? "readonly " : "";
16
+ return `${readonly}[${typeKeyOf(member.parameterType)}]:${typeKeyOf(member.type)}`;
17
+ }
18
+ const readonly = member.isReadonly ? "readonly " : "";
19
+ const optional = member.isOptional ? "?" : "";
20
+ return `${readonly}${nameKey(member.name)}${optional}:${typeKeyOf(member.type)}`;
21
+ }
22
+
23
+ function listKey(types: readonly IrType[], typeKeyOf: TypeKey): string {
24
+ return types.map(typeKeyOf).join(",");
25
+ }
26
+
27
+ function elementKey(element: IrTupleElement, typeKeyOf: TypeKey): string {
28
+ const prefix = element.isRest ? "..." : "";
29
+ return `${prefix}${typeKeyOf(element.type)}${element.isOptional ? "?" : ""}`;
30
+ }
31
+
32
+ function computeKey(type: IrType, typeKeyOf: TypeKey): string {
33
+ switch (type.kind) {
34
+ case "keyword":
35
+ return type.keyword;
36
+ case "stringLiteral":
37
+ return JSON.stringify(type.value);
38
+ case "numberLiteral":
39
+ case "booleanLiteral":
40
+ return String(type.value);
41
+ case "reference":
42
+ return `${type.name}<${listKey(type.typeArguments, typeKeyOf)}>`;
43
+ case "array":
44
+ return `(${typeKeyOf(type.element)})[]`;
45
+ case "tuple":
46
+ return `[${type.elements.map((element) => elementKey(element, typeKeyOf)).join(",")}]`;
47
+ case "union":
48
+ return `union(${listKey(type.types, typeKeyOf)})`;
49
+ case "intersection":
50
+ return `intersection(${listKey(type.types, typeKeyOf)})`;
51
+ case "object":
52
+ return `{${type.members.map((member) => memberKey(member, typeKeyOf)).join(";")}}`;
53
+ case "indexedAccess":
54
+ return `${typeKeyOf(type.object)}[${typeKeyOf(type.index)}]`;
55
+ case "readonly":
56
+ return `readonly(${typeKeyOf(type.type)})`;
57
+ case "raw":
58
+ return `raw(${type.text})`;
59
+ default:
60
+ return assertNever(type);
61
+ }
62
+ }
63
+
64
+ /** The key of a type. */
65
+ export type TypeKey = (type: IrType) => string;
66
+
67
+ /** A key function that computes each node's key once; one per comparison, not shared. */
68
+ export function createTypeKey(): TypeKey {
69
+ const keys = new WeakMap<IrType, string>();
70
+ const typeKeyOf: TypeKey = (type) => {
71
+ const known = keys.get(type);
72
+ if (known !== undefined) {
73
+ return known;
74
+ }
75
+ const key = computeKey(type, typeKeyOf);
76
+ keys.set(type, key);
77
+ return key;
78
+ };
79
+ return typeKeyOf;
80
+ }
@@ -0,0 +1,163 @@
1
+ import type {
2
+ IrIndexSignature,
3
+ IrMember,
4
+ IrObjectType,
5
+ IrPropertySignature,
6
+ IrType,
7
+ } from "../../../domain/ir/nodes.js";
8
+ import type { DeclaredTypeLookup } from "./declared-types.js";
9
+
10
+ /**
11
+ * The types the `--path-params-as-types` overlap check compares, as TypeScript sees them:
12
+ * references followed to the declarations of the document, `T["key"]` and `T[number]` read from
13
+ * `T`, and an intersection of object types merged into one object.
14
+ */
15
+
16
+ const UNDEFINED: IrType = { kind: "keyword", keyword: "undefined" };
17
+
18
+ export function nameOf(member: IrPropertySignature): string {
19
+ return member.name.kind === "string" ? member.name.value : member.name.text;
20
+ }
21
+
22
+ export function propertiesOf(type: IrObjectType): Map<string, IrPropertySignature> {
23
+ const properties = type.members.filter(
24
+ (member: IrMember): member is IrPropertySignature => member.kind === "property",
25
+ );
26
+ return new Map(properties.map((property) => [nameOf(property), property]));
27
+ }
28
+
29
+ export function indexSignaturesOf(type: IrObjectType): IrIndexSignature[] {
30
+ return type.members.filter(
31
+ (member: IrMember): member is IrIndexSignature => member.kind === "indexSignature",
32
+ );
33
+ }
34
+
35
+ /** The type read from a property: an optional one may be `undefined`. */
36
+ export function readType(property: IrPropertySignature): IrType {
37
+ return property.isOptional ? { kind: "union", types: [property.type, UNDEFINED] } : property.type;
38
+ }
39
+
40
+ /**
41
+ * `T[number]` for an array `T`, as the generator writes for an item of an array response: the
42
+ * element type, readonly or not. A tuple's is the union of its elements, which is not modelled.
43
+ */
44
+ function arrayElement(type: IrType): IrType | null {
45
+ const list = type.kind === "readonly" ? type.type : type;
46
+ return list.kind === "array" ? list.element : null;
47
+ }
48
+
49
+ /** One property for two of the same name in an intersection. */
50
+ function mergedProperty(
51
+ first: IrPropertySignature,
52
+ second: IrPropertySignature,
53
+ ): IrPropertySignature {
54
+ return {
55
+ ...first,
56
+ type: { kind: "intersection", types: [first.type, second.type] },
57
+ isOptional: first.isOptional && second.isOptional,
58
+ isReadonly: first.isReadonly || second.isReadonly,
59
+ };
60
+ }
61
+
62
+ /** Resolves types once each, for one comparison. */
63
+ export class TypeResolution {
64
+ private readonly lookup: DeclaredTypeLookup;
65
+ private readonly resolvedTypes = new Map<IrType, IrType>();
66
+
67
+ constructor(lookup: DeclaredTypeLookup) {
68
+ this.lookup = lookup;
69
+ }
70
+
71
+ /** The type a reference names, followed through references to references; computed once. */
72
+ resolved(type: IrType): IrType {
73
+ const known = this.resolvedTypes.get(type);
74
+ if (known !== undefined) {
75
+ return known;
76
+ }
77
+ // A type that reaches itself while it is resolved stays as it is written there.
78
+ this.resolvedTypes.set(type, type);
79
+ const seen = new Set<IrType>();
80
+ let current = type;
81
+ while (!seen.has(current)) {
82
+ seen.add(current);
83
+ const named = this.lookup(current) ?? this.indexedProperty(current);
84
+ if (named === null) {
85
+ break;
86
+ }
87
+ current = named;
88
+ }
89
+ const result = current.kind === "intersection" ? (this.merged(current) ?? current) : current;
90
+ this.resolvedTypes.set(type, result);
91
+ return result;
92
+ }
93
+
94
+ /**
95
+ * `T["key"]` read from the object type `T` resolves to, which the lookup does not do for an
96
+ * object it reached through another lookup (`paths["/a/{id}"]["get"]`). An optional property
97
+ * reads as `T | undefined`; `T[number]` is the element of the array `T`.
98
+ */
99
+ private indexedProperty(type: IrType): IrType | null {
100
+ if (type.kind !== "indexedAccess") {
101
+ return null;
102
+ }
103
+ const { index } = type;
104
+ if (index.kind === "keyword" && index.keyword === "number") {
105
+ return arrayElement(this.resolved(type.object));
106
+ }
107
+ const object = this.resolved(type.object);
108
+ if (index.kind !== "stringLiteral" || object.kind !== "object") {
109
+ return null;
110
+ }
111
+ const property = propertiesOf(object).get(index.value);
112
+ return property === undefined ? null : readType(property);
113
+ }
114
+
115
+ /**
116
+ * An intersection of object types as the one object TypeScript makes of it: a property in
117
+ * several parts has the intersection of their types, and is optional only when it is in each.
118
+ * Null when a part is no object type.
119
+ */
120
+ private merged(type: IrType): IrObjectType | null {
121
+ const parts = this.objectParts(type);
122
+ if (parts === null) {
123
+ return null;
124
+ }
125
+ const properties = new Map<string, IrPropertySignature>();
126
+ const indexes: IrIndexSignature[] = [];
127
+ for (const member of parts.flatMap((part) => part.members)) {
128
+ if (member.kind === "indexSignature") {
129
+ indexes.push(member);
130
+ continue;
131
+ }
132
+ const name = nameOf(member);
133
+ const other = properties.get(name);
134
+ properties.set(name, other === undefined ? member : mergedProperty(other, member));
135
+ }
136
+ return { kind: "object", members: [...properties.values(), ...indexes] };
137
+ }
138
+
139
+ /**
140
+ * The object types an intersection is made of, nested ones flattened; null for any other, and
141
+ * for an intersection that contains itself, which TypeScript does not merge either.
142
+ */
143
+ private objectParts(
144
+ type: IrType,
145
+ visiting: ReadonlySet<IrType> = new Set(),
146
+ ): IrObjectType[] | null {
147
+ if (type.kind === "object") {
148
+ return [type];
149
+ }
150
+ if (type.kind !== "intersection" || visiting.has(type)) {
151
+ return null;
152
+ }
153
+ const parts: IrObjectType[] = [];
154
+ for (const part of type.types) {
155
+ const objects = this.objectParts(this.resolved(part), new Set([...visiting, type]));
156
+ if (objects === null) {
157
+ return null;
158
+ }
159
+ parts.push(...objects);
160
+ }
161
+ return parts;
162
+ }
163
+ }
@@ -1,5 +1,10 @@
1
1
  import { irReference } from "../../../domain/ir/constructors.js";
2
- import type { IrDeclaration, IrPropertySignature, IrType } from "../../../domain/ir/nodes.js";
2
+ import type {
3
+ IrDeclaration,
4
+ IrEnumDeclaration,
5
+ IrPropertySignature,
6
+ IrType,
7
+ } from "../../../domain/ir/nodes.js";
3
8
  import { HELPER_TYPES, type HelperType } from "../helper-types.js";
4
9
  import type {
5
10
  EnumDeclarationRequest,
@@ -7,7 +12,13 @@ import type {
7
12
  EnumValuesRequest,
8
13
  } from "../schema/schema-context.js";
9
14
  import type { DocumentFooter } from "../transform-context.js";
10
- import { EnumNaming, enumDeclaration } from "./enum-naming.js";
15
+ import {
16
+ type DeclaredMembers,
17
+ EnumNaming,
18
+ enumDeclaration,
19
+ withoutDeclaredMembers,
20
+ } from "./enum-naming.js";
21
+ import { FIRST_SCHEMA_NAMES, type SharedEnumNames } from "./shared-enum-names.js";
11
22
 
12
23
  /** Where footer declarations go, in order: the run's `ctx.injectFooter`, open to user code. */
13
24
  export interface FooterSink {
@@ -43,6 +54,9 @@ function metadataKey(value: string, metadata: EnumMemberMetadata | undefined): s
43
54
  return `${metadata?.name ?? value}:${metadata?.description || ""}`;
44
55
  }
45
56
 
57
+ /** Separates a repeated `--enum-values` array name from its number, as root type aliases do. */
58
+ const ENUM_VALUES_NUMBER_SEPARATOR = "_";
59
+
46
60
  /** The whole declaration: an equal key means the same enum, member for member. */
47
61
  function declarationKey(name: string, request: EnumDeclarationRequest): string {
48
62
  return JSON.stringify([name, request.values, request.metadata]);
@@ -58,18 +72,34 @@ export class RunFooter implements DocumentFooter {
58
72
  private readonly enumNamesByKey = new Map<string, string>();
59
73
  /** Declarations added already: the same one twice fails with "Duplicate identifier". */
60
74
  private readonly declaredEnums = new Set<string>();
75
+ /** The member names of each declared enum, by value. */
76
+ private readonly membersByEnum = new Map<string, Map<string | number, string>>();
77
+ /** The members of each declared enum name, by member name. */
78
+ private readonly declaredMembers = new Map<string, Map<string, string | number>>();
79
+ /** `--enum-values` arrays added already: their declared name by name, type and values. */
80
+ private readonly declaredEnumValues = new Map<string, string>();
81
+ /** The declared `--enum-values` array names. */
82
+ private readonly enumValuesNames = new Set<string>();
61
83
  private readonly sink: FooterSink;
62
84
  private readonly isDedupingEnums: boolean;
85
+ private readonly sharedNames: SharedEnumNames;
63
86
 
64
- constructor(sink: FooterSink, isDedupingEnums: boolean) {
87
+ constructor(
88
+ sink: FooterSink,
89
+ isDedupingEnums: boolean,
90
+ sharedNames: SharedEnumNames = FIRST_SCHEMA_NAMES,
91
+ ) {
65
92
  this.sink = sink;
66
93
  this.isDedupingEnums = isDedupingEnums;
94
+ this.sharedNames = sharedNames;
67
95
  }
68
96
 
69
97
  addEnum(request: EnumDeclarationRequest): string {
70
98
  // The name is built before the cache lookup: it advances the naming state either way.
71
- const name = this.naming.enumName(request.name);
99
+ const ownName = this.naming.enumName(request.name);
72
100
  const key = this.isDedupingEnums ? dedupeKey(request) : null;
101
+ // Every schema of a shared enum goes to `sharedNames`, the later ones too.
102
+ const name = key === null ? ownName : this.sharedNames.nameOf(key, ownName, request);
73
103
  const known = key === null ? undefined : this.enumNamesByKey.get(key);
74
104
  if (known !== undefined) {
75
105
  return known;
@@ -82,15 +112,56 @@ export class RunFooter implements DocumentFooter {
82
112
  if (declared !== null) {
83
113
  this.declaredEnums.add(declared);
84
114
  }
85
- this.sink.add(enumDeclaration(name, request, this.naming));
115
+ const declaration = this.withoutRepeats(enumDeclaration(name, request, this.naming));
116
+ if (declaration !== null) {
117
+ this.record(declaration);
118
+ this.sink.add(declaration);
119
+ }
86
120
  if (key !== null) {
87
121
  this.enumNamesByKey.set(key, name);
88
122
  }
89
123
  return name;
90
124
  }
91
125
 
126
+ /** The declaration without members an earlier one of the name has; null when none is left. */
127
+ private withoutRepeats(declaration: IrEnumDeclaration): IrEnumDeclaration | null {
128
+ const earlier: DeclaredMembers | undefined = this.declaredMembers.get(declaration.name);
129
+ if (earlier === undefined) {
130
+ return declaration;
131
+ }
132
+ const merged = withoutDeclaredMembers(declaration, earlier);
133
+ // Two empty declarations merge into one empty enum, which compiles, so the second stays.
134
+ return merged.members.length === 0 && declaration.members.length > 0 ? null : merged;
135
+ }
136
+
137
+ /** The members of a declaration; the first member of a value names it. */
138
+ private record(declaration: IrEnumDeclaration): void {
139
+ const byName = this.declaredMembers.get(declaration.name) ?? new Map();
140
+ const byValue = this.membersByEnum.get(declaration.name) ?? new Map();
141
+ for (const member of declaration.members) {
142
+ byName.set(member.name, member.value);
143
+ if (!byValue.has(member.value)) {
144
+ byValue.set(member.value, member.name);
145
+ }
146
+ }
147
+ this.declaredMembers.set(declaration.name, byName);
148
+ this.membersByEnum.set(declaration.name, byValue);
149
+ }
150
+
151
+ enumMembers(name: string): ReadonlyMap<string | number, string> {
152
+ return this.membersByEnum.get(name) ?? new Map();
153
+ }
154
+
92
155
  addEnumValues(request: EnumValuesRequest): void {
93
- const name = this.naming.arrayName(request.name);
156
+ const baseName = this.naming.arrayName(request.name);
157
+ // The same array twice fails with "Cannot redeclare"; a schema typed twice adds it twice.
158
+ const declared = JSON.stringify([baseName, request.type, request.values]);
159
+ if (this.declaredEnumValues.has(declared)) {
160
+ return;
161
+ }
162
+ const name = this.freeEnumValuesName(baseName);
163
+ this.declaredEnumValues.set(declared, name);
164
+ this.enumValuesNames.add(name);
94
165
  this.require(HELPER_TYPES.flattenedDeepRequired);
95
166
  this.require(HELPER_TYPES.readonlyArray);
96
167
  this.sink.add({
@@ -103,6 +174,20 @@ export class RunFooter implements DocumentFooter {
103
174
  });
104
175
  }
105
176
 
177
+ /**
178
+ * Another array may have the name: `Discount/type` and `discount/type`, or a property and an
179
+ * inline `allOf` item of one schema, typed at one location. A redeclared `const` does not
180
+ * compile, so a later one gets the next number (`discountTypeValues_2`). Every unnumbered name
181
+ * ends with `Values`, so a number never takes a name that a later array needs.
182
+ */
183
+ private freeEnumValuesName(name: string): string {
184
+ let candidate = name;
185
+ for (let count = 2; this.enumValuesNames.has(candidate); count++) {
186
+ candidate = `${name}${ENUM_VALUES_NUMBER_SEPARATOR}${count}`;
187
+ }
188
+ return candidate;
189
+ }
190
+
106
191
  requireWithRequired(): void {
107
192
  this.require(HELPER_TYPES.withRequired);
108
193
  }