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,78 @@
1
+ import type { IrDeclaration } from "../../../domain/ir/nodes.js";
2
+
3
+ /**
4
+ * The TypeScript global types that generated code can refer to: in references (`Record<string,
5
+ * never>`, `Omit<…>`, the `Blob` of `--binary-as-blob`) and in the text of footer helpers
6
+ * (`Exclude<…>`, `Readonly<…>`). A module declaration of the same name shadows the global: every
7
+ * use of it then fails to compile, or silently names the module's type (`Blob`).
8
+ */
9
+ const GLOBAL_TYPE_NAMES: ReadonlySet<string> = new Set([
10
+ "Array",
11
+ "Blob",
12
+ "Exclude",
13
+ "Extract",
14
+ "NonNullable",
15
+ "Omit",
16
+ "Partial",
17
+ "Pick",
18
+ "Readonly",
19
+ "ReadonlyArray",
20
+ "Record",
21
+ "Required",
22
+ ]);
23
+
24
+ const GENERIC_USE = /\b([A-Z][A-Za-z]*)\s*</g;
25
+
26
+ /** `origin` holds the outside node a raw type was printed from; it is not part of the output. */
27
+ const SKIPPED_KEYS: ReadonlySet<string> = new Set(["origin", "comments"]);
28
+
29
+ function isObject(value: unknown): value is Readonly<Record<string, unknown>> {
30
+ return typeof value === "object" && value !== null;
31
+ }
32
+
33
+ function addTextUses(text: string, used: Set<string>): void {
34
+ for (const match of text.matchAll(GENERIC_USE)) {
35
+ const name = match[1] ?? "";
36
+ if (GLOBAL_TYPE_NAMES.has(name)) {
37
+ used.add(name);
38
+ }
39
+ }
40
+ }
41
+
42
+ function addNodeUses(node: Readonly<Record<string, unknown>>, used: Set<string>): void {
43
+ if (node.kind === "reference" && typeof node.name === "string") {
44
+ if (GLOBAL_TYPE_NAMES.has(node.name)) {
45
+ used.add(node.name);
46
+ }
47
+ } else if (
48
+ (node.kind === "raw" || node.kind === "rawDeclaration") &&
49
+ typeof node.text === "string"
50
+ ) {
51
+ addTextUses(node.text, used);
52
+ }
53
+ }
54
+
55
+ function visit(value: unknown, used: Set<string>): void {
56
+ if (Array.isArray(value)) {
57
+ for (const item of value) {
58
+ visit(item, used);
59
+ }
60
+ return;
61
+ }
62
+ if (!isObject(value)) {
63
+ return;
64
+ }
65
+ addNodeUses(value, used);
66
+ for (const [key, child] of Object.entries(value)) {
67
+ if (!SKIPPED_KEYS.has(key)) {
68
+ visit(child, used);
69
+ }
70
+ }
71
+ }
72
+
73
+ /** The TypeScript global type names that the declarations refer to. */
74
+ export function globalTypeReferences(declarations: readonly IrDeclaration[]): Set<string> {
75
+ const used = new Set<string>();
76
+ visit(declarations, used);
77
+ return used;
78
+ }
@@ -0,0 +1,87 @@
1
+ import type {
2
+ IrArrayType,
3
+ IrReadonlyType,
4
+ IrTupleElement,
5
+ IrTupleType,
6
+ IrType,
7
+ } from "../../../domain/ir/nodes.js";
8
+ import { type Assignability, everyOf } from "./assignability.js";
9
+
10
+ /**
11
+ * How the `--path-params-as-types` overlap check relates two lists: arrays, tuples and their
12
+ * `readonly` forms. `relate` compares two item types with the whole check.
13
+ */
14
+
15
+ type Relate = (source: IrType, target: IrType) => Assignability;
16
+
17
+ /** The types of the `list` category of `assignability.ts`. */
18
+ export type IrListType = IrArrayType | IrTupleType | IrReadonlyType;
19
+
20
+ type IrMutableListType = IrArrayType | IrTupleType;
21
+
22
+ const UNDEFINED: IrType = { kind: "keyword", keyword: "undefined" };
23
+
24
+ /**
25
+ * The type of a tuple element's items: a rest element `...T[]` stands for its items, and an
26
+ * optional element `T?` may be `undefined` (`[string?]` is no `string[]` under `strict`).
27
+ */
28
+ function elementType(element: IrTupleElement): IrType {
29
+ if (element.isRest && element.type.kind === "array") {
30
+ return element.type.element;
31
+ }
32
+ return element.isOptional ? { kind: "union", types: [element.type, UNDEFINED] } : element.type;
33
+ }
34
+
35
+ function isRequired(element: IrTupleElement): boolean {
36
+ return !element.isRest && !element.isOptional;
37
+ }
38
+
39
+ /** A tuple with a required element rejects an array, which may be empty. */
40
+ function arrayToTuple(target: IrTupleType): Assignability {
41
+ return target.elements.some(isRequired) ? "rejected" : "undecided";
42
+ }
43
+
44
+ /** Tuples of the same shape compare element by element; fixed lengths must be the same. */
45
+ function relateTuples(source: IrTupleType, target: IrTupleType, relate: Relate): Assignability {
46
+ if (source.elements.length !== target.elements.length) {
47
+ const isFixed = source.elements.every(isRequired) && target.elements.every(isRequired);
48
+ return isFixed ? "rejected" : "undecided";
49
+ }
50
+ return everyOf(source.elements, (element, index) => {
51
+ const other = target.elements[index] as IrTupleElement;
52
+ if (element.isRest !== other.isRest) {
53
+ return "undecided";
54
+ }
55
+ // An optional element may be missing where the target requires one.
56
+ if (element.isOptional && !other.isOptional) {
57
+ return "rejected";
58
+ }
59
+ return relate(element.type, other.type);
60
+ });
61
+ }
62
+
63
+ function relateMutableLists(
64
+ source: IrMutableListType,
65
+ target: IrMutableListType,
66
+ relate: Relate,
67
+ ): Assignability {
68
+ if (source.kind === "array") {
69
+ return target.kind === "array" ? relate(source.element, target.element) : arrayToTuple(target);
70
+ }
71
+ if (target.kind === "array") {
72
+ return everyOf(source.elements, (element) => relate(elementType(element), target.element));
73
+ }
74
+ return relateTuples(source, target, relate);
75
+ }
76
+
77
+ /** Whether one list type fits another. */
78
+ export function relateLists(source: IrListType, target: IrListType, relate: Relate): Assignability {
79
+ if (source.kind === "readonly") {
80
+ // A readonly array is no mutable array (TS4104); to a readonly target it is the same type.
81
+ return target.kind === "readonly" ? relate(source.type, target.type) : "rejected";
82
+ }
83
+ if (target.kind === "readonly") {
84
+ return relate(source, target.type);
85
+ }
86
+ return relateMutableLists(source, target, relate);
87
+ }
@@ -1,4 +1,10 @@
1
- import { irKeyword, irObject, irProperty } from "../../../domain/ir/constructors.js";
1
+ import {
2
+ irIntersection,
3
+ irKeyword,
4
+ irObject,
5
+ irProperty,
6
+ irReference,
7
+ } from "../../../domain/ir/constructors.js";
2
8
  import type { IrPropertyName, IrPropertySignature, IrType } from "../../../domain/ir/nodes.js";
3
9
  import { toPropertyName } from "../../../domain/ir/property-name.js";
4
10
  import { createRef } from "../../../domain/openapi/json-pointer.js";
@@ -10,10 +16,12 @@ import {
10
16
  QUERYSTRING_LOCATION,
11
17
  } from "../../../domain/openapi/parameter-object.js";
12
18
  import type { SchemaObject } from "../../../domain/openapi/schema-object.js";
19
+ import { OutputFix } from "../../../domain/output-fixes.js";
13
20
  import { refType } from "../ref-type.js";
14
21
  import type { SchemaLocation } from "../schema/schema-context.js";
15
22
  import { withSchemaComment } from "../schema-comment.js";
16
23
  import type { TransformContext } from "../transform-context.js";
24
+ import { isExplodedQueryObject } from "./exploded-query.js";
17
25
  import { parameterType } from "./parameter.js";
18
26
 
19
27
  const PATH_TEMPLATE_RE = /\{([^}]+)\}/g;
@@ -154,6 +162,66 @@ function querystringMember(
154
162
  return withSchemaComment(property, querystring.resolved);
155
163
  }
156
164
 
165
+ /** With `exploded-query-objects`, whether the parameter's object keys are keys of `query`. */
166
+ function isExplodedEntry(entry: ResolvedEntry, context: TransformContext): boolean {
167
+ return (
168
+ context.options.fixes.has(OutputFix.EXPLODED_QUERY_OBJECTS) &&
169
+ isExplodedQueryObject(entry.resolved, context.refs)
170
+ );
171
+ }
172
+
173
+ /**
174
+ * An exploded object parameter's part of `query`: its object type. A parameter that may be left
175
+ * out sends none of its keys then, so they are optional: `Partial<…>`.
176
+ */
177
+ function explodedPart(
178
+ entry: ResolvedEntry,
179
+ location: SchemaLocation,
180
+ context: TransformContext,
181
+ ): IrType {
182
+ const type = entryType(entry, location, context);
183
+ return entry.resolved.required ? type : irReference("Partial", [type]);
184
+ }
185
+
186
+ /** A location's parameters as members, and the object types of its exploded parameters. */
187
+ interface LocationParts {
188
+ readonly members: readonly IrPropertySignature[];
189
+ readonly exploded: readonly IrType[];
190
+ readonly isOptional: boolean;
191
+ }
192
+
193
+ /** Each parameter in document order, so the declarations they add keep their order. */
194
+ function locationParts(
195
+ entries: readonly ResolvedEntry[],
196
+ location: SchemaLocation,
197
+ context: TransformContext,
198
+ ): LocationParts {
199
+ const members: IrPropertySignature[] = [];
200
+ const exploded: IrType[] = [];
201
+ let isOptional = true;
202
+ for (const entry of entries) {
203
+ if (isExplodedEntry(entry, context)) {
204
+ exploded.push(explodedPart(entry, location, context));
205
+ isOptional &&= !entry.resolved.required;
206
+ continue;
207
+ }
208
+ const member = parameterMember(entry, location, context);
209
+ members.push(member);
210
+ isOptional &&= member.isOptional;
211
+ }
212
+ return { members, exploded, isOptional };
213
+ }
214
+
215
+ /** The members' object, intersected with the exploded parameters' types; `never` for none. */
216
+ function locationType({ members, exploded }: LocationParts): IrType {
217
+ const types = [...(members.length > 0 ? [irObject(members)] : []), ...exploded];
218
+ const [first] = types;
219
+ if (first === undefined) {
220
+ return irKeyword("never");
221
+ }
222
+ return types.length === 1 ? first : irIntersection(types);
223
+ }
224
+
157
225
  /** One location's parameters; optional when all of them are, and `never` when there are none. */
158
226
  function locationMember(
159
227
  parameterIn: ParameterLocation,
@@ -161,17 +229,14 @@ function locationMember(
161
229
  location: SchemaLocation,
162
230
  context: TransformContext,
163
231
  ): IrPropertySignature {
164
- const members = entries
165
- .filter((entry): entry is ResolvedEntry => entry.resolved?.in === parameterIn)
166
- .map((entry) => parameterMember(entry, location, context));
167
- return irProperty(
168
- toPropertyName(parameterIn),
169
- members.length > 0 ? irObject(members) : irKeyword("never"),
170
- {
171
- isOptional: members.every((member) => member.isOptional),
172
- isReadonly: context.options.immutable,
173
- },
232
+ const located = entries.filter(
233
+ (entry): entry is ResolvedEntry => entry.resolved?.in === parameterIn,
174
234
  );
235
+ const parts = locationParts(located, location, context);
236
+ return irProperty(toPropertyName(parameterIn), locationType(parts), {
237
+ isOptional: parts.isOptional,
238
+ isReadonly: context.options.immutable,
239
+ });
175
240
  }
176
241
 
177
242
  /**
@@ -6,6 +6,8 @@
6
6
  * TS2413). These functions find the signatures that cover another key the checker rejects.
7
7
  */
8
8
 
9
+ import { isAssignableToTemplate, type TemplateShape } from "./template-key-match.js";
10
+
9
11
  /** A placeholder of a template literal type, by the type it stands for. */
10
12
  export type PathPlaceholder = "string" | "number" | "boolean";
11
13
 
@@ -21,89 +23,176 @@ export type PathKey =
21
23
 
22
24
  const BOOLEAN_TEXTS: readonly string[] = ["true", "false"];
23
25
 
24
- /** The text one placeholder stands for in a sample of a template key. */
25
- const SAMPLE_TEXTS: Readonly<Record<PathPlaceholder, string>> = {
26
- string: "",
27
- number: "0",
28
- boolean: "true",
29
- };
30
-
31
- /** TypeScript's rule for `${number}`: a text that converts to a finite number. */
32
- function isNumberText(text: string): boolean {
33
- return text !== "" && Number.isFinite(Number(text));
26
+ /** The shapes after one more part; a boolean placeholder doubles them, as TypeScript does. */
27
+ function withPart(shapes: readonly TemplateShape[], part: PathKeyPart): TemplateShape[] {
28
+ return shapes.flatMap((shape): TemplateShape[] => {
29
+ const lead = shape.texts.slice(0, -1);
30
+ // A shape has one text more than it has placeholders, so it always has a last text.
31
+ const last = shape.texts[shape.texts.length - 1] as string;
32
+ if (part.kind === "text") {
33
+ return [{ texts: [...lead, last + part.text], types: shape.types }];
34
+ }
35
+ if (part.type === "boolean") {
36
+ return BOOLEAN_TEXTS.map((text) => ({ texts: [...lead, last + text], types: shape.types }));
37
+ }
38
+ return [{ texts: [...shape.texts, ""], types: [...shape.types, part.type] }];
39
+ });
34
40
  }
35
41
 
36
- function isPlaceholderText(type: PathPlaceholder, text: string): boolean {
37
- if (type === "number") {
38
- return isNumberText(text);
42
+ /**
43
+ * The key types TypeScript makes of a key: a literal is one text; a template is one shape, or a
44
+ * union of shapes when `${boolean}` is in it (`` `/a/${boolean}` `` is `"/a/true" | "/a/false"`).
45
+ */
46
+ function shapesOf(key: PathKey): readonly TemplateShape[] {
47
+ if (key.kind === "literal") {
48
+ return [{ texts: [key.url], types: [] }];
39
49
  }
40
- return type === "boolean" ? BOOLEAN_TEXTS.includes(text) : true;
50
+ return key.parts.reduce<TemplateShape[]>(withPart, [{ texts: [""], types: [] }]);
41
51
  }
42
52
 
43
- /** Whether `text` from `start` on is one of the strings the parts from `index` on describe. */
44
- function matchesFrom(
45
- text: string,
46
- start: number,
47
- parts: readonly PathKeyPart[],
48
- index: number,
53
+ /**
54
+ * Whether a signature checks the covered key: every type of the covered key is assignable to one
55
+ * of the signature's types. A shape without placeholders (a boolean-only template) is a string
56
+ * literal, which is no signature, so the targets leave it out.
57
+ */
58
+ function isCoveredBy(
59
+ sources: readonly TemplateShape[],
60
+ targets: readonly TemplateShape[],
49
61
  ): boolean {
50
- const part = parts[index];
51
- if (part === undefined) {
52
- return start === text.length;
53
- }
54
- if (part.kind === "text") {
55
- return (
56
- text.startsWith(part.text, start) &&
57
- matchesFrom(text, start + part.text.length, parts, index + 1)
58
- );
59
- }
60
- for (let end = start; end <= text.length; end += 1) {
61
- if (
62
- isPlaceholderText(part.type, text.slice(start, end)) &&
63
- matchesFrom(text, end, parts, index + 1)
64
- ) {
65
- return true;
62
+ return (
63
+ sources.length > 0 &&
64
+ sources.every((source) => targets.some((target) => isAssignableToTemplate(source, target)))
65
+ );
66
+ }
67
+
68
+ /** Whether TypeScript rejects the key at `covered` for the signature at `covering`. */
69
+ export type CoverRejection = (covering: number, covered: number) => boolean;
70
+
71
+ /** Without type information every covered key counts. */
72
+ const EVERY_COVER_REJECTED: CoverRejection = () => true;
73
+
74
+ /**
75
+ * The key types TypeScript checks against a signature: a literal's, or a template's signature
76
+ * types. A boolean-only template is no signature (TS1337), so the checker neither checks it nor
77
+ * checks anything against it; a repeated one is still reported (TS2374, see `repeatedTemplates`).
78
+ */
79
+ function checkedShapes(key: PathKey): readonly TemplateShape[] {
80
+ const shapes = shapesOf(key);
81
+ return key.kind === "literal" ? shapes : shapes.filter((shape) => shape.types.length > 0);
82
+ }
83
+
84
+ /** A checked key: the key, the shapes TypeScript checks, and its template identity. */
85
+ interface CheckedKey {
86
+ readonly key: PathKey;
87
+ readonly shapes: readonly TemplateShape[];
88
+ /** The key types written as one string, for templates; null for a literal. */
89
+ readonly template: string | null;
90
+ }
91
+
92
+ function checkedKeyOf(key: PathKey): CheckedKey {
93
+ const template = key.kind === "template" ? JSON.stringify(shapesOf(key)) : null;
94
+ return { key, shapes: checkedShapes(key), template };
95
+ }
96
+
97
+ /**
98
+ * The templates written more than once, as paths that differ only in parameter names give
99
+ * (`/a/{id}`, `/a/{name}`). OpenAPI forbids such paths, and one object with both signatures fails
100
+ * to compile whatever their types (TS2374), so each always counts as covering the other.
101
+ */
102
+ function repeatedTemplates(checked: readonly CheckedKey[]): ReadonlySet<string> {
103
+ const seen = new Set<string>();
104
+ const repeated = new Set<string>();
105
+ for (const { template } of checked) {
106
+ if (template !== null) {
107
+ (seen.has(template) ? repeated : seen).add(template);
66
108
  }
67
109
  }
68
- return false;
110
+ return repeated;
69
111
  }
70
112
 
71
113
  /**
72
- * One string of the key. If a template key is covered, every string of it is, so a sample that is
73
- * not covered proves the key is not; a sample that is covered may over-report, which only moves a
74
- * signature that did not need it (the types stay the same, see `pathsObjectType`).
114
+ * The checked keys by the first text of each of their shapes, sorted. A key type is assignable to
115
+ * a template only when its first text starts with the template's first text (both branches of
116
+ * `isAssignableToTemplate`), so the keys a template can cover are one run of this list. Without
117
+ * it the check compares every pair of keys, which takes minutes for 10 000 paths.
75
118
  */
76
- function sampleOf(key: PathKey): string {
77
- if (key.kind === "literal") {
78
- return key.url;
119
+ interface StartTextIndex {
120
+ readonly starts: readonly string[];
121
+ readonly keyIndexes: readonly number[];
122
+ }
123
+
124
+ /** Code unit order: in it the strings that start with one prefix are adjacent. */
125
+ function compareCodeUnits(left: string, right: string): number {
126
+ if (left === right) {
127
+ return 0;
79
128
  }
80
- return key.parts
81
- .map((part) => (part.kind === "text" ? part.text : SAMPLE_TEXTS[part.type]))
82
- .join("");
129
+ return left < right ? -1 : 1;
83
130
  }
84
131
 
85
- /** Whether TypeScript rejects the key at `covered` for the signature at `covering`. */
86
- export type CoverRejection = (covering: number, covered: number) => boolean;
132
+ function startTextIndexOf(checked: readonly CheckedKey[]): StartTextIndex {
133
+ const entries = checked.flatMap(({ shapes }, keyIndex) =>
134
+ shapes.map((shape) => ({ start: shape.texts[0] as string, keyIndex })),
135
+ );
136
+ entries.sort((left, right) => compareCodeUnits(left.start, right.start));
137
+ return {
138
+ starts: entries.map((entry) => entry.start),
139
+ keyIndexes: entries.map((entry) => entry.keyIndex),
140
+ };
141
+ }
87
142
 
88
- /** Without type information every covered key counts. */
89
- const EVERY_COVER_REJECTED: CoverRejection = () => true;
143
+ /** The indexes of the keys with a shape whose first text starts with one of the prefixes. */
144
+ function keysStartingWith(index: StartTextIndex, prefixes: readonly string[]): Set<number> {
145
+ const found = new Set<number>();
146
+ for (const prefix of prefixes) {
147
+ let low = 0;
148
+ let high = index.starts.length;
149
+ while (low < high) {
150
+ const middle = (low + high) >> 1;
151
+ if ((index.starts[middle] as string) < prefix) {
152
+ low = middle + 1;
153
+ } else {
154
+ high = middle;
155
+ }
156
+ }
157
+ for (let at = low; at < index.starts.length; at += 1) {
158
+ if (!(index.starts[at] as string).startsWith(prefix)) {
159
+ break;
160
+ }
161
+ found.add(index.keyIndexes[at] as number);
162
+ }
163
+ }
164
+ return found;
165
+ }
90
166
 
91
- /** The indexes of the template keys that cover another key of the list, where it is rejected. */
167
+ /**
168
+ * The indexes of the template keys that cover another key of the list, where it is rejected, or
169
+ * that repeat another template key. A template covers the keys TypeScript checks against it, the
170
+ * ones its own matching accepts (see `template-key-match.ts`), not every string it stands for:
171
+ * `` `/a/b/${number}` `` is no `` `/${string}/${number}` `` to the checker, which does not
172
+ * backtrack, so that pair is never checked.
173
+ */
92
174
  export function coveringTemplateKeys(
93
175
  keys: readonly PathKey[],
94
176
  isRejected: CoverRejection = EVERY_COVER_REJECTED,
95
177
  ): ReadonlySet<number> {
178
+ const checked = keys.map(checkedKeyOf);
179
+ const repeated = repeatedTemplates(checked);
180
+ const startTexts = startTextIndexOf(checked);
96
181
  const covering = new Set<number>();
97
- keys.forEach((key, index) => {
98
- if (key.kind !== "template") {
182
+ checked.forEach(({ template, shapes: targets }, index) => {
183
+ if (template === null) {
99
184
  return;
100
185
  }
101
- const isCovering = keys.some(
102
- (other, otherIndex) =>
103
- otherIndex !== index &&
104
- matchesFrom(sampleOf(other), 0, key.parts, 0) &&
105
- isRejected(index, otherIndex),
106
- );
186
+ const prefixes = targets.map((target) => target.texts[0] as string);
187
+ const candidates = [...keysStartingWith(startTexts, prefixes)].sort((a, b) => a - b);
188
+ const isCovering =
189
+ repeated.has(template) ||
190
+ candidates.some(
191
+ (otherIndex) =>
192
+ otherIndex !== index &&
193
+ isCoveredBy((checked[otherIndex] as CheckedKey).shapes, targets) &&
194
+ isRejected(index, otherIndex),
195
+ );
107
196
  if (isCovering) {
108
197
  covering.add(index);
109
198
  }
@@ -0,0 +1,90 @@
1
+ import type { PathKeyPart, PathPlaceholder } from "./path-key-overlaps.js";
2
+
3
+ /**
4
+ * The `--path-params-as-types` key as the original builds it: each `{name}` match, in URL order,
5
+ * replaces the first occurrence of its text in the key so far. A parameter named `number`,
6
+ * `string` or `boolean` can so hit the `${number}` an earlier parameter got: of
7
+ * `/a/{id}/b/{number}` the original makes `/a/$${number}/b/{number}`. That output compiles, so it
8
+ * stays the default (ADR-021); the `path-param-type-names` fix gives every match its own
9
+ * placeholder.
10
+ */
11
+
12
+ /** One `{name}` of the URL and the placeholder of its parameter's type. */
13
+ export interface PathParameterMatch {
14
+ readonly match: string;
15
+ readonly type: PathPlaceholder;
16
+ }
17
+
18
+ /**
19
+ * One UTF-16 code unit of the key so far (`indexOf` counts those): URL text, or a unit of the
20
+ * placeholder that the match at `owner` inserted.
21
+ */
22
+ interface KeyUnit {
23
+ readonly unit: string;
24
+ readonly owner: number | null;
25
+ }
26
+
27
+ const URL_TEXT = null;
28
+
29
+ /** `${type}`: the text of a template literal type placeholder. */
30
+ export function placeholderText(type: PathPlaceholder): string {
31
+ return `\${${type}}`;
32
+ }
33
+
34
+ /** The key's units after each match replaced the first occurrence of its text. */
35
+ function replacedUnits(url: string, matches: readonly PathParameterMatch[]): readonly KeyUnit[] {
36
+ const units: KeyUnit[] = url.split("").map((unit) => ({ unit, owner: URL_TEXT }));
37
+ matches.forEach(({ match, type }, owner) => {
38
+ const at = units
39
+ .map(({ unit }) => unit)
40
+ .join("")
41
+ .indexOf(match);
42
+ if (at >= 0) {
43
+ const inserted = placeholderText(type)
44
+ .split("")
45
+ .map((unit) => ({ unit, owner }));
46
+ units.splice(at, match.length, ...inserted);
47
+ }
48
+ });
49
+ return units;
50
+ }
51
+
52
+ /** The placeholders, by match, that no later match cut into. */
53
+ function intactPlaceholders(
54
+ units: readonly KeyUnit[],
55
+ matches: readonly PathParameterMatch[],
56
+ ): ReadonlyMap<number, PathPlaceholder> {
57
+ const intact = new Map<number, PathPlaceholder>();
58
+ matches.forEach(({ type }, owner) => {
59
+ const left = units.filter((unit) => unit.owner === owner).length;
60
+ if (left === placeholderText(type).length) {
61
+ intact.set(owner, type);
62
+ }
63
+ });
64
+ return intact;
65
+ }
66
+
67
+ /**
68
+ * The key as parts. What is left of a placeholder that a later match cut into (the `$` of
69
+ * `$${number}`) is text, as TypeScript reads it.
70
+ */
71
+ export function replacedTemplateParts(
72
+ url: string,
73
+ matches: readonly PathParameterMatch[],
74
+ ): PathKeyPart[] {
75
+ const units = replacedUnits(url, matches);
76
+ const intact = intactPlaceholders(units, matches);
77
+ const parts: PathKeyPart[] = [];
78
+ let text = "";
79
+ units.forEach(({ unit, owner }, index) => {
80
+ const type = owner === URL_TEXT ? undefined : intact.get(owner);
81
+ if (type === undefined) {
82
+ text += unit;
83
+ } else if (units[index - 1]?.owner !== owner) {
84
+ parts.push({ kind: "text", text }, { kind: "placeholder", type });
85
+ text = "";
86
+ }
87
+ });
88
+ parts.push({ kind: "text", text });
89
+ return parts;
90
+ }