openapi-typesafe 1.0.0-rc.4 → 1.0.0-rc.5

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 (320) hide show
  1. package/CHANGELOG.md +141 -0
  2. package/dist/adapters/node-file-system/node-file-system.cjs +7 -0
  3. package/dist/adapters/node-file-system/node-file-system.cjs.map +1 -1
  4. package/dist/adapters/node-file-system/node-file-system.mjs +7 -0
  5. package/dist/adapters/node-file-system/node-file-system.mjs.map +1 -1
  6. package/dist/adapters/pipe-safe-writer/pipe-safe-writer.cjs +19 -0
  7. package/dist/adapters/pipe-safe-writer/pipe-safe-writer.cjs.map +1 -0
  8. package/dist/adapters/pipe-safe-writer/pipe-safe-writer.mjs +17 -0
  9. package/dist/adapters/pipe-safe-writer/pipe-safe-writer.mjs.map +1 -0
  10. package/dist/adapters/redocly-loader/bundled-mapping-copies.cjs +5 -32
  11. package/dist/adapters/redocly-loader/bundled-mapping-copies.cjs.map +1 -1
  12. package/dist/adapters/redocly-loader/bundled-mapping-copies.mjs +6 -33
  13. package/dist/adapters/redocly-loader/bundled-mapping-copies.mjs.map +1 -1
  14. package/dist/adapters/redocly-loader/component-schemas.cjs +17 -0
  15. package/dist/adapters/redocly-loader/component-schemas.cjs.map +1 -0
  16. package/dist/adapters/redocly-loader/component-schemas.mjs +14 -0
  17. package/dist/adapters/redocly-loader/component-schemas.mjs.map +1 -0
  18. package/dist/adapters/redocly-loader/mapping-schema-names.cjs +22 -0
  19. package/dist/adapters/redocly-loader/mapping-schema-names.cjs.map +1 -0
  20. package/dist/adapters/redocly-loader/mapping-schema-names.mjs +20 -0
  21. package/dist/adapters/redocly-loader/mapping-schema-names.mjs.map +1 -0
  22. package/dist/adapters/redocly-loader/query-named-components.cjs +137 -0
  23. package/dist/adapters/redocly-loader/query-named-components.cjs.map +1 -0
  24. package/dist/adapters/redocly-loader/query-named-components.mjs +134 -0
  25. package/dist/adapters/redocly-loader/query-named-components.mjs.map +1 -0
  26. package/dist/adapters/redocly-loader/redocly-schema-loader.cjs +26 -14
  27. package/dist/adapters/redocly-loader/redocly-schema-loader.cjs.map +1 -1
  28. package/dist/adapters/redocly-loader/redocly-schema-loader.mjs +26 -14
  29. package/dist/adapters/redocly-loader/redocly-schema-loader.mjs.map +1 -1
  30. package/dist/adapters/redocly-loader/ref-access.cjs +16 -3
  31. package/dist/adapters/redocly-loader/ref-access.cjs.map +1 -1
  32. package/dist/adapters/redocly-loader/ref-access.mjs +16 -3
  33. package/dist/adapters/redocly-loader/ref-access.mjs.map +1 -1
  34. package/dist/adapters/redocly-loader/ref-holders.cjs +52 -10
  35. package/dist/adapters/redocly-loader/ref-holders.cjs.map +1 -1
  36. package/dist/adapters/redocly-loader/ref-holders.mjs +52 -11
  37. package/dist/adapters/redocly-loader/ref-holders.mjs.map +1 -1
  38. package/dist/adapters/redocly-loader/remote-fetch.cjs +5 -3
  39. package/dist/adapters/redocly-loader/remote-fetch.cjs.map +1 -1
  40. package/dist/adapters/redocly-loader/remote-fetch.mjs +5 -3
  41. package/dist/adapters/redocly-loader/remote-fetch.mjs.map +1 -1
  42. package/dist/adapters/redocly-loader/remote-node-limit.cjs +17 -3
  43. package/dist/adapters/redocly-loader/remote-node-limit.cjs.map +1 -1
  44. package/dist/adapters/redocly-loader/remote-node-limit.mjs +17 -3
  45. package/dist/adapters/redocly-loader/remote-node-limit.mjs.map +1 -1
  46. package/dist/adapters/redocly-loader/schema-source.cjs +7 -2
  47. package/dist/adapters/redocly-loader/schema-source.cjs.map +1 -1
  48. package/dist/adapters/redocly-loader/schema-source.mjs +8 -3
  49. package/dist/adapters/redocly-loader/schema-source.mjs.map +1 -1
  50. package/dist/adapters/terminal-logger/terminal-logger.cjs +7 -3
  51. package/dist/adapters/terminal-logger/terminal-logger.cjs.map +1 -1
  52. package/dist/adapters/terminal-logger/terminal-logger.mjs +7 -3
  53. package/dist/adapters/terminal-logger/terminal-logger.mjs.map +1 -1
  54. package/dist/api/document-refs.cjs +16 -1
  55. package/dist/api/document-refs.cjs.map +1 -1
  56. package/dist/api/document-refs.mjs +16 -1
  57. package/dist/api/document-refs.mjs.map +1 -1
  58. package/dist/api/document-run.cjs +4 -10
  59. package/dist/api/document-run.cjs.map +1 -1
  60. package/dist/api/document-run.mjs +4 -10
  61. package/dist/api/document-run.mjs.map +1 -1
  62. package/dist/api/openapi-ts.cjs +1 -2
  63. package/dist/api/openapi-ts.cjs.map +1 -1
  64. package/dist/api/openapi-ts.mjs +2 -3
  65. package/dist/api/openapi-ts.mjs.map +1 -1
  66. package/dist/api/ref-limit-options.cjs +63 -0
  67. package/dist/api/ref-limit-options.cjs.map +1 -0
  68. package/dist/api/ref-limit-options.mjs +57 -0
  69. package/dist/api/ref-limit-options.mjs.map +1 -0
  70. package/dist/api/transform-schema.cjs +12 -5
  71. package/dist/api/transform-schema.cjs.map +1 -1
  72. package/dist/api/transform-schema.mjs +12 -5
  73. package/dist/api/transform-schema.mjs.map +1 -1
  74. package/dist/application/transform/declared-value-types.cjs +36 -0
  75. package/dist/application/transform/declared-value-types.cjs.map +1 -0
  76. package/dist/application/transform/declared-value-types.mjs +34 -0
  77. package/dist/application/transform/declared-value-types.mjs.map +1 -0
  78. package/dist/application/transform/document/document.cjs +3 -1
  79. package/dist/application/transform/document/document.cjs.map +1 -1
  80. package/dist/application/transform/document/document.mjs +3 -1
  81. package/dist/application/transform/document/document.mjs.map +1 -1
  82. package/dist/application/transform/document/exploded-query-values.cjs +63 -0
  83. package/dist/application/transform/document/exploded-query-values.cjs.map +1 -0
  84. package/dist/application/transform/document/exploded-query-values.mjs +59 -0
  85. package/dist/application/transform/document/exploded-query-values.mjs.map +1 -0
  86. package/dist/application/transform/document/header.cjs +11 -3
  87. package/dist/application/transform/document/header.cjs.map +1 -1
  88. package/dist/application/transform/document/header.mjs +10 -2
  89. package/dist/application/transform/document/header.mjs.map +1 -1
  90. package/dist/application/transform/document/parameter.cjs +1 -1
  91. package/dist/application/transform/document/parameter.cjs.map +1 -1
  92. package/dist/application/transform/document/parameter.mjs +1 -1
  93. package/dist/application/transform/document/parameter.mjs.map +1 -1
  94. package/dist/application/transform/document/parameters.cjs +23 -5
  95. package/dist/application/transform/document/parameters.cjs.map +1 -1
  96. package/dist/application/transform/document/parameters.mjs +23 -5
  97. package/dist/application/transform/document/parameters.mjs.map +1 -1
  98. package/dist/application/transform/document/path-item.cjs +4 -3
  99. package/dist/application/transform/document/path-item.cjs.map +1 -1
  100. package/dist/application/transform/document/path-item.mjs +4 -4
  101. package/dist/application/transform/document/path-item.mjs.map +1 -1
  102. package/dist/application/transform/document/path-template-key.cjs +84 -0
  103. package/dist/application/transform/document/path-template-key.cjs.map +1 -0
  104. package/dist/application/transform/document/path-template-key.mjs +79 -0
  105. package/dist/application/transform/document/path-template-key.mjs.map +1 -0
  106. package/dist/application/transform/document/path-template-refs.cjs +70 -0
  107. package/dist/application/transform/document/path-template-refs.cjs.map +1 -0
  108. package/dist/application/transform/document/path-template-refs.mjs +67 -0
  109. package/dist/application/transform/document/path-template-refs.mjs.map +1 -0
  110. package/dist/application/transform/document/paths-enum.cjs +7 -6
  111. package/dist/application/transform/document/paths-enum.cjs.map +1 -1
  112. package/dist/application/transform/document/paths-enum.mjs +7 -6
  113. package/dist/application/transform/document/paths-enum.mjs.map +1 -1
  114. package/dist/application/transform/document/paths.cjs +38 -66
  115. package/dist/application/transform/document/paths.cjs.map +1 -1
  116. package/dist/application/transform/document/paths.mjs +36 -66
  117. package/dist/application/transform/document/paths.mjs.map +1 -1
  118. package/dist/application/transform/document/webhooks.cjs +10 -3
  119. package/dist/application/transform/document/webhooks.cjs.map +1 -1
  120. package/dist/application/transform/document/webhooks.mjs +9 -2
  121. package/dist/application/transform/document/webhooks.mjs.map +1 -1
  122. package/dist/application/transform/footer/document-footer.cjs +6 -3
  123. package/dist/application/transform/footer/document-footer.cjs.map +1 -1
  124. package/dist/application/transform/footer/document-footer.mjs +6 -3
  125. package/dist/application/transform/footer/document-footer.mjs.map +1 -1
  126. package/dist/application/transform/helper-types.cjs +3 -3
  127. package/dist/application/transform/helper-types.cjs.map +1 -1
  128. package/dist/application/transform/helper-types.mjs +3 -3
  129. package/dist/application/transform/helper-types.mjs.map +1 -1
  130. package/dist/application/transform/ref-pointer/generated-location.cjs +4 -4
  131. package/dist/application/transform/ref-pointer/generated-location.cjs.map +1 -1
  132. package/dist/application/transform/ref-pointer/generated-location.mjs +1 -1
  133. package/dist/application/transform/ref-pointer/generated-location.mjs.map +1 -1
  134. package/dist/application/transform/ref-pointer/generated-shape.cjs +47 -66
  135. package/dist/application/transform/ref-pointer/generated-shape.cjs.map +1 -1
  136. package/dist/application/transform/ref-pointer/generated-shape.mjs +36 -50
  137. package/dist/application/transform/ref-pointer/generated-shape.mjs.map +1 -1
  138. package/dist/application/transform/ref-pointer/inline-cycles.cjs +2 -2
  139. package/dist/application/transform/ref-pointer/inline-cycles.cjs.map +1 -1
  140. package/dist/application/transform/ref-pointer/inline-cycles.mjs +1 -1
  141. package/dist/application/transform/ref-pointer/inline-cycles.mjs.map +1 -1
  142. package/dist/application/transform/ref-pointer/located-node.cjs +23 -0
  143. package/dist/application/transform/ref-pointer/located-node.cjs.map +1 -0
  144. package/dist/application/transform/ref-pointer/located-node.mjs +18 -0
  145. package/dist/application/transform/ref-pointer/located-node.mjs.map +1 -0
  146. package/dist/application/transform/ref-pointer/original-index.cjs +68 -13
  147. package/dist/application/transform/ref-pointer/original-index.cjs.map +1 -1
  148. package/dist/application/transform/ref-pointer/original-index.mjs +70 -15
  149. package/dist/application/transform/ref-pointer/original-index.mjs.map +1 -1
  150. package/dist/application/transform/ref-pointer/pointer-type.cjs +2 -2
  151. package/dist/application/transform/ref-pointer/pointer-type.cjs.map +1 -1
  152. package/dist/application/transform/ref-pointer/pointer-type.mjs +1 -1
  153. package/dist/application/transform/ref-pointer/pointer-type.mjs.map +1 -1
  154. package/dist/application/transform/ref-pointer/pointer-walk.cjs +54 -13
  155. package/dist/application/transform/ref-pointer/pointer-walk.cjs.map +1 -1
  156. package/dist/application/transform/ref-pointer/pointer-walk.mjs +46 -7
  157. package/dist/application/transform/ref-pointer/pointer-walk.mjs.map +1 -1
  158. package/dist/application/transform/ref-pointer/property-shape.cjs +52 -0
  159. package/dist/application/transform/ref-pointer/property-shape.cjs.map +1 -0
  160. package/dist/application/transform/ref-pointer/property-shape.mjs +45 -0
  161. package/dist/application/transform/ref-pointer/property-shape.mjs.map +1 -0
  162. package/dist/application/transform/ref-pointer/typed-walk.cjs +79 -21
  163. package/dist/application/transform/ref-pointer/typed-walk.cjs.map +1 -1
  164. package/dist/application/transform/ref-pointer/typed-walk.mjs +76 -18
  165. package/dist/application/transform/ref-pointer/typed-walk.mjs.map +1 -1
  166. package/dist/application/transform/schema/access-flags.cjs.map +1 -1
  167. package/dist/application/transform/schema/access-flags.mjs.map +1 -1
  168. package/dist/application/transform/schema/index-signature-type.cjs +21 -0
  169. package/dist/application/transform/schema/index-signature-type.cjs.map +1 -0
  170. package/dist/application/transform/schema/index-signature-type.mjs +19 -0
  171. package/dist/application/transform/schema/index-signature-type.mjs.map +1 -0
  172. package/dist/application/transform/schema/input-location.cjs +23 -0
  173. package/dist/application/transform/schema/input-location.cjs.map +1 -0
  174. package/dist/application/transform/schema/input-location.mjs +21 -0
  175. package/dist/application/transform/schema/input-location.mjs.map +1 -0
  176. package/dist/application/transform/schema/object-type.cjs +15 -5
  177. package/dist/application/transform/schema/object-type.cjs.map +1 -1
  178. package/dist/application/transform/schema/object-type.mjs +15 -5
  179. package/dist/application/transform/schema/object-type.mjs.map +1 -1
  180. package/dist/application/transform/schema/schema-transform.cjs +2 -1
  181. package/dist/application/transform/schema/schema-transform.cjs.map +1 -1
  182. package/dist/application/transform/schema/schema-transform.mjs +2 -1
  183. package/dist/application/transform/schema/schema-transform.mjs.map +1 -1
  184. package/dist/application/transform/schema/unevaluated-properties.cjs +2 -30
  185. package/dist/application/transform/schema/unevaluated-properties.cjs.map +1 -1
  186. package/dist/application/transform/schema/unevaluated-properties.mjs +3 -31
  187. package/dist/application/transform/schema/unevaluated-properties.mjs.map +1 -1
  188. package/dist/cli/api-logger.cjs +13 -0
  189. package/dist/cli/api-logger.cjs.map +1 -0
  190. package/dist/cli/api-logger.mjs +11 -0
  191. package/dist/cli/api-logger.mjs.map +1 -0
  192. package/dist/cli/api-settings.cjs +7 -15
  193. package/dist/cli/api-settings.cjs.map +1 -1
  194. package/dist/cli/api-settings.mjs +8 -16
  195. package/dist/cli/api-settings.mjs.map +1 -1
  196. package/dist/cli/cli-arguments.cjs +51 -11
  197. package/dist/cli/cli-arguments.cjs.map +1 -1
  198. package/dist/cli/cli-arguments.mjs +51 -11
  199. package/dist/cli/cli-arguments.mjs.map +1 -1
  200. package/dist/cli/cli-errors.cjs +17 -0
  201. package/dist/cli/cli-errors.cjs.map +1 -1
  202. package/dist/cli/cli-errors.mjs +17 -1
  203. package/dist/cli/cli-errors.mjs.map +1 -1
  204. package/dist/cli/cli-output.cjs +6 -4
  205. package/dist/cli/cli-output.cjs.map +1 -1
  206. package/dist/cli/cli-output.mjs +7 -5
  207. package/dist/cli/cli-output.mjs.map +1 -1
  208. package/dist/cli/main.cjs +10 -4
  209. package/dist/cli/main.cjs.map +1 -1
  210. package/dist/cli/main.mjs +10 -4
  211. package/dist/cli/main.mjs.map +1 -1
  212. package/dist/cli/redocly-apis.cjs +20 -9
  213. package/dist/cli/redocly-apis.cjs.map +1 -1
  214. package/dist/cli/redocly-apis.mjs +22 -11
  215. package/dist/cli/redocly-apis.mjs.map +1 -1
  216. package/dist/cli/ref-limits.cjs +15 -1
  217. package/dist/cli/ref-limits.cjs.map +1 -1
  218. package/dist/cli/ref-limits.mjs +15 -2
  219. package/dist/cli/ref-limits.mjs.map +1 -1
  220. package/dist/cli/run-cli.cjs +24 -7
  221. package/dist/cli/run-cli.cjs.map +1 -1
  222. package/dist/cli/run-cli.mjs +25 -8
  223. package/dist/cli/run-cli.mjs.map +1 -1
  224. package/dist/domain/cause-secrets.cjs +31 -16
  225. package/dist/domain/cause-secrets.cjs.map +1 -1
  226. package/dist/domain/cause-secrets.mjs +31 -16
  227. package/dist/domain/cause-secrets.mjs.map +1 -1
  228. package/dist/domain/errors.cjs +2 -0
  229. package/dist/domain/errors.cjs.map +1 -1
  230. package/dist/domain/errors.mjs +2 -0
  231. package/dist/domain/errors.mjs.map +1 -1
  232. package/dist/domain/location-text.cjs +9 -0
  233. package/dist/domain/location-text.cjs.map +1 -0
  234. package/dist/domain/location-text.mjs +7 -0
  235. package/dist/domain/location-text.mjs.map +1 -0
  236. package/dist/domain/openapi/document-places.cjs +55 -0
  237. package/dist/domain/openapi/document-places.cjs.map +1 -0
  238. package/dist/domain/openapi/document-places.mjs +53 -0
  239. package/dist/domain/openapi/document-places.mjs.map +1 -0
  240. package/dist/domain/output-fixes.cjs +8 -2
  241. package/dist/domain/output-fixes.cjs.map +1 -1
  242. package/dist/domain/output-fixes.mjs +8 -2
  243. package/dist/domain/output-fixes.mjs.map +1 -1
  244. package/dist/domain/terminal-text.cjs +31 -0
  245. package/dist/domain/terminal-text.cjs.map +1 -0
  246. package/dist/domain/terminal-text.mjs +29 -0
  247. package/dist/domain/terminal-text.mjs.map +1 -0
  248. package/dist/domain/warnings.cjs +1 -0
  249. package/dist/domain/warnings.cjs.map +1 -1
  250. package/dist/domain/warnings.mjs +1 -0
  251. package/dist/domain/warnings.mjs.map +1 -1
  252. package/dist/index.d.cts +26 -18
  253. package/dist/index.d.mts +26 -18
  254. package/dist/index.d.ts +26 -18
  255. package/package.json +1 -1
  256. package/src/adapters/node-file-system/node-file-system.ts +14 -1
  257. package/src/adapters/pipe-safe-writer/pipe-safe-writer.ts +29 -0
  258. package/src/adapters/redocly-loader/bundled-mapping-copies.ts +7 -42
  259. package/src/adapters/redocly-loader/component-schemas.ts +19 -0
  260. package/src/adapters/redocly-loader/mapping-schema-names.ts +25 -0
  261. package/src/adapters/redocly-loader/query-named-components.ts +201 -0
  262. package/src/adapters/redocly-loader/redocly-schema-loader.ts +46 -14
  263. package/src/adapters/redocly-loader/ref-access.ts +16 -3
  264. package/src/adapters/redocly-loader/ref-holders.ts +75 -14
  265. package/src/adapters/redocly-loader/remote-fetch.ts +11 -2
  266. package/src/adapters/redocly-loader/remote-node-limit.ts +44 -4
  267. package/src/adapters/redocly-loader/schema-source.ts +15 -3
  268. package/src/adapters/terminal-logger/terminal-logger.ts +10 -4
  269. package/src/api/document-refs.ts +29 -1
  270. package/src/api/document-run.ts +6 -16
  271. package/src/api/openapi-ts.ts +6 -3
  272. package/src/api/ref-limit-options.ts +76 -0
  273. package/src/api/transform-schema.ts +20 -5
  274. package/src/application/ports/file-system.ts +8 -0
  275. package/src/application/transform/declared-value-types.ts +42 -0
  276. package/src/application/transform/document/document.ts +4 -2
  277. package/src/application/transform/document/exploded-query-values.ts +88 -0
  278. package/src/application/transform/document/header.ts +25 -5
  279. package/src/application/transform/document/parameter.ts +4 -4
  280. package/src/application/transform/document/parameters.ts +45 -10
  281. package/src/application/transform/document/path-item.ts +3 -3
  282. package/src/application/transform/document/path-template-key.ts +152 -0
  283. package/src/application/transform/document/path-template-refs.ts +106 -0
  284. package/src/application/transform/document/paths-enum.ts +23 -9
  285. package/src/application/transform/document/paths.ts +53 -123
  286. package/src/application/transform/document/webhooks.ts +18 -5
  287. package/src/application/transform/footer/document-footer.ts +18 -8
  288. package/src/application/transform/helper-types.ts +4 -4
  289. package/src/application/transform/ref-pointer/generated-location.ts +1 -1
  290. package/src/application/transform/ref-pointer/generated-shape.ts +67 -78
  291. package/src/application/transform/ref-pointer/inline-cycles.ts +1 -1
  292. package/src/application/transform/ref-pointer/located-node.ts +38 -0
  293. package/src/application/transform/ref-pointer/original-index.ts +100 -17
  294. package/src/application/transform/ref-pointer/pointer-type.ts +1 -1
  295. package/src/application/transform/ref-pointer/pointer-walk.ts +82 -8
  296. package/src/application/transform/ref-pointer/property-shape.ts +97 -0
  297. package/src/application/transform/ref-pointer/typed-walk.ts +117 -25
  298. package/src/application/transform/schema/access-flags.ts +5 -2
  299. package/src/application/transform/schema/index-signature-type.ts +31 -0
  300. package/src/application/transform/schema/input-location.ts +35 -0
  301. package/src/application/transform/schema/object-type.ts +18 -8
  302. package/src/application/transform/schema/schema-transform.ts +2 -1
  303. package/src/application/transform/schema/unevaluated-properties.ts +4 -44
  304. package/src/cli/api-logger.ts +17 -0
  305. package/src/cli/api-settings.ts +12 -20
  306. package/src/cli/cli-arguments.ts +90 -14
  307. package/src/cli/cli-dependencies.ts +7 -2
  308. package/src/cli/cli-errors.ts +22 -0
  309. package/src/cli/cli-output.ts +11 -10
  310. package/src/cli/main.ts +10 -4
  311. package/src/cli/redocly-apis.ts +37 -17
  312. package/src/cli/ref-limits.ts +21 -2
  313. package/src/cli/run-cli.ts +39 -7
  314. package/src/domain/cause-secrets.ts +59 -25
  315. package/src/domain/errors.ts +2 -0
  316. package/src/domain/location-text.ts +12 -0
  317. package/src/domain/openapi/document-places.ts +78 -0
  318. package/src/domain/output-fixes.ts +9 -2
  319. package/src/domain/terminal-text.ts +44 -0
  320. package/src/domain/warnings.ts +1 -0
@@ -1,13 +1,19 @@
1
1
  import colors from "ansi-colors";
2
2
  import type { DebugGroup, Logger } from "../../application/ports/logger.js";
3
+ import { terminalSafeText } from "../../domain/terminal-text.js";
3
4
  import { redactUrlSecrets } from "../../domain/url-secrets.js";
4
5
 
5
6
  /**
6
7
  * The Logger port on a terminal, with the original output format: ` ⚠ ` warnings, ` ✘ `
7
8
  * errors, and `openapi-ts:<group>` debug lines filtered by `DEBUG` (ENGINEERING_PLAN 4.5). No
8
- * line shows the user info of a URL.
9
+ * line shows the secrets of a URL, and each message prints as one line: a message quotes the
10
+ * document, whose control characters could forge CI commands (terminal-text.ts).
9
11
  */
10
12
 
13
+ function shownText(message: string): string {
14
+ return terminalSafeText(redactUrlSecrets(message));
15
+ }
16
+
11
17
  /** Where the lines go: `console` in production, a recording sink in tests. */
12
18
  export interface TerminalSink {
13
19
  debug(line: string): void;
@@ -84,19 +90,19 @@ class TerminalLogger implements Logger {
84
90
  // The original wrote no space without a duration (`openapi-ts:lintIgnored …`).
85
91
  const time =
86
92
  durationMs === undefined ? " " : this.#palette.green(` ${formatDuration(durationMs)} `);
87
- const text = redactUrlSecrets(message);
93
+ const text = shownText(message);
88
94
  this.#options.sink.debug(` ${this.#palette.bold(name)}${time}${text}`);
89
95
  }
90
96
 
91
97
  warn(message: string): void {
92
98
  if (!this.#options.silent) {
93
- this.#options.sink.warn(this.#palette.yellow(` ⚠ ${redactUrlSecrets(message)}`));
99
+ this.#options.sink.warn(this.#palette.yellow(` ⚠ ${shownText(message)}`));
94
100
  }
95
101
  }
96
102
 
97
103
  error(message: string): void {
98
104
  if (!this.#options.silent) {
99
- this.#options.sink.error(this.#palette.red(` ✘ ${redactUrlSecrets(message)}`));
105
+ this.#options.sink.error(this.#palette.red(` ✘ ${shownText(message)}`));
100
106
  }
101
107
  }
102
108
  }
@@ -16,6 +16,34 @@ function isObject(value: unknown): value is Record<string, unknown> {
16
16
  return typeof value === "object" && value !== null;
17
17
  }
18
18
 
19
+ /**
20
+ * The child of a node under a pointer key. A pointer may pass through a `$ref`
21
+ * (`#/components/schemas/Wrap/properties/o/properties/v`, where `o` is a `$ref`): a key the node
22
+ * does not have is read from the node's target, as the generated types index it (ADR-026). A key
23
+ * the node has is read from it, so a pointer to a sibling of a `$ref` (OpenAPI 3.1) stays as
24
+ * written.
25
+ */
26
+ function childOf(
27
+ schema: unknown,
28
+ node: unknown,
29
+ key: string,
30
+ options: ResolveRefOptions & { logger: Logger },
31
+ ): unknown {
32
+ if (!isObject(node)) {
33
+ return undefined;
34
+ }
35
+ const { visited = [] } = options;
36
+ const target = node.$ref;
37
+ if (node[key] || typeof target !== "string" || visited.includes(target)) {
38
+ return node[key];
39
+ }
40
+ const resolved = resolveDocumentRef(schema, target, {
41
+ ...options,
42
+ visited: [...visited, target],
43
+ });
44
+ return isObject(resolved) ? resolved[key] : undefined;
45
+ }
46
+
19
47
  /** Resolve a `$ref` in a document; warns and returns undefined when it cannot. */
20
48
  export function resolveDocumentRef<T>(
21
49
  schema: unknown,
@@ -30,7 +58,7 @@ export function resolveDocumentRef<T>(
30
58
  }
31
59
  let node: unknown = schema;
32
60
  for (const key of pointer) {
33
- const child = isObject(node) ? node[key] : undefined;
61
+ const child = childOf(schema, node, key, { ...options, logger });
34
62
  if (!child) {
35
63
  logger.warn(warningText(`Could not resolve $ref "${$ref}"`, WARNING_CODES.REF_NOT_FOUND));
36
64
  return undefined;
@@ -1,10 +1,10 @@
1
1
  import path from "node:path";
2
- import { fileURLToPath, pathToFileURL } from "node:url";
2
+ import { pathToFileURL } from "node:url";
3
3
  import type { Config as RedoclyConfig } from "@redocly/openapi-core";
4
4
  import { sourceEmptyError } from "../adapters/redocly-loader/schema-load-errors.js";
5
5
  import type { DeprecationReporter } from "../application/ports/deprecation-reporter.js";
6
6
  import type { Logger } from "../application/ports/logger.js";
7
- import type { RefAccess, SchemaLoader } from "../application/ports/schema-loader.js";
7
+ import type { SchemaLoader } from "../application/ports/schema-loader.js";
8
8
  import { withStackOverflowReported } from "../application/stack-overflow.js";
9
9
  import {
10
10
  ineffectiveFixNotices,
@@ -19,6 +19,7 @@ import { warnDiscriminatorValueConflicts } from "./discriminator-conflict-warnin
19
19
  import { findDiscriminators } from "./discriminator-scan.js";
20
20
  import { resolveDocumentRef } from "./document-refs.js";
21
21
  import { warnIdenticalPathTemplates } from "./path-template-warnings.js";
22
+ import { refAccessOf } from "./ref-limit-options.js";
22
23
  import type { RunContext } from "./run-context.js";
23
24
  import { warnUnsupportedFeatures } from "./unsupported-feature-warnings.js";
24
25
 
@@ -68,19 +69,6 @@ function sourceCwd(options: GeneratorOptions): URL {
68
69
  return pathToFileURL(path.join(directory, path.sep));
69
70
  }
70
71
 
71
- /** The `$ref` limits of the options; a relative local root resolves against the cwd. */
72
- function refAccessOf(options: GeneratorOptions, cwd: URL): RefAccess {
73
- const { localRefRoot, remoteRefHosts } = options;
74
- const localRoot =
75
- typeof localRefRoot === "string"
76
- ? pathToFileURL(path.resolve(fileURLToPath(cwd), localRefRoot))
77
- : localRefRoot;
78
- return {
79
- ...(localRoot === undefined ? {} : { localRoot }),
80
- ...(remoteRefHosts === undefined ? {} : { remoteHosts: remoteRefHosts }),
81
- };
82
- }
83
-
84
72
  function callbackOptions(options: GeneratorOptions) {
85
73
  return {
86
74
  irCallbacks: options.irCallbacks === true,
@@ -126,7 +114,9 @@ function contextFlags(options: GeneratorOptions): ContextFlags {
126
114
  */
127
115
  function outputFixesOf(options: GeneratorOptions, logger: Logger): readonly OutputFix[] {
128
116
  const names = outputFixNamesOf(options.fix ?? []);
129
- for (const notice of ineffectiveFixNotices(names, (option) => options[option] === true)) {
117
+ // The options with their defaults: `defaultNonNullable` is on unless set to false.
118
+ const flags = contextFlags(options);
119
+ for (const notice of ineffectiveFixNotices(names, (option) => flags[option])) {
130
120
  logger.warn(warningText(notice, WARNING_CODES.FIX_NEEDS_OPTION));
131
121
  }
132
122
  return [...resolveOutputFixes(names)];
@@ -1,4 +1,5 @@
1
1
  import { createRedoclySchemaLoader } from "../adapters/redocly-loader/redocly-schema-loader.js";
2
+ import type { Logger } from "../application/ports/logger.js";
2
3
  import { withStackOverflowReported } from "../application/stack-overflow.js";
3
4
  import type { IrDeclaration } from "../domain/ir/nodes.js";
4
5
  import type { GeneratorOptions, SchemaSource } from "../types.js";
@@ -52,10 +53,12 @@ export async function generateDeclarations(
52
53
 
53
54
  /**
54
55
  * The default ports: the terminal logger, Node.js deprecation warnings, the given or default
55
- * Redocly config, Redocly loading.
56
+ * Redocly config, Redocly loading. A given `logger` takes every line, the loader's included.
56
57
  */
57
- export async function defaultGeneratorPorts(options: GeneratorOptions): Promise<GeneratorPorts> {
58
- const logger = defaultLogger(options.silent ?? false);
58
+ export async function defaultGeneratorPorts(
59
+ options: GeneratorOptions,
60
+ logger: Logger = defaultLogger(options.silent ?? false),
61
+ ): Promise<GeneratorPorts> {
59
62
  const redoc = options.redocly ?? (await defaultRedoclyConfig());
60
63
  const loader = createRedoclySchemaLoader({
61
64
  redoc,
@@ -0,0 +1,76 @@
1
+ import path from "node:path";
2
+ import { fileURLToPath, pathToFileURL } from "node:url";
3
+ import type { RefAccess } from "../application/ports/schema-loader.js";
4
+ import { ERROR_CODES, OpenAPITypesafeError } from "../domain/errors.js";
5
+ import { hasUrlScheme } from "../domain/location-text.js";
6
+ import type { GeneratorOptions } from "../types.js";
7
+
8
+ /**
9
+ * The `$ref` limits of the options (threat model R1, R2, ADR-017), checked before anything loads.
10
+ * A value of another type from JavaScript (`remoteRefHosts: "a.com"`, `localRefRoot: new
11
+ * URL("https://…")`) used to fail later as an error of the document.
12
+ */
13
+
14
+ const FILE_PROTOCOL = "file:";
15
+
16
+ /** What a value is, for the error; a URL is named by its protocol only, as it may hold secrets. */
17
+ function kindOf(value: unknown): string {
18
+ if (value instanceof URL) {
19
+ return `a ${value.protocol} URL`;
20
+ }
21
+ if (Array.isArray(value)) {
22
+ return "a list";
23
+ }
24
+ return value === null ? "null" : `a ${typeof value}`;
25
+ }
26
+
27
+ function optionInvalid(option: string, expected: string, got: string): OpenAPITypesafeError {
28
+ return new OpenAPITypesafeError({
29
+ code: ERROR_CODES.OPTION_INVALID,
30
+ message: `The ${option} option must be ${expected}; got ${got}.`,
31
+ details: { option },
32
+ });
33
+ }
34
+
35
+ const LOCAL_ROOT_EXPECTED = "a directory path or a file: URL";
36
+
37
+ /**
38
+ * A string as the CLI reads `--local-ref-root`: a URL when it has a scheme (`file:///specs/`),
39
+ * else a path that resolves against the cwd. Undefined when it does not parse as a URL.
40
+ */
41
+ function localRootUrlOf(value: string, cwd: URL): URL | undefined {
42
+ if (!hasUrlScheme(value)) {
43
+ return pathToFileURL(path.resolve(fileURLToPath(cwd), value));
44
+ }
45
+ return URL.parse(value, cwd.href) ?? undefined;
46
+ }
47
+
48
+ /** The local root as a `file:` URL; the option is set, so it is not undefined. */
49
+ function localRootOf(value: unknown, cwd: URL): URL {
50
+ const root = typeof value === "string" ? localRootUrlOf(value, cwd) : value;
51
+ if (root instanceof URL && root.protocol === FILE_PROTOCOL) {
52
+ return root;
53
+ }
54
+ const got = root === undefined ? "a string that is no valid URL" : kindOf(root);
55
+ throw optionInvalid("localRefRoot", LOCAL_ROOT_EXPECTED, got);
56
+ }
57
+
58
+ function remoteHostsOf(value: unknown): readonly string[] {
59
+ if (!Array.isArray(value)) {
60
+ throw optionInvalid("remoteRefHosts", "a list of hosts", kindOf(value));
61
+ }
62
+ const other = value.find((entry) => typeof entry !== "string");
63
+ if (other !== undefined) {
64
+ throw optionInvalid("remoteRefHosts", "a list of hosts", `a list with ${kindOf(other)}`);
65
+ }
66
+ return value;
67
+ }
68
+
69
+ /** The limits of the options; fails with `OPTION_INVALID` for a value of another type. */
70
+ export function refAccessOf(options: GeneratorOptions, cwd: URL): RefAccess {
71
+ const { localRefRoot, remoteRefHosts } = options;
72
+ return {
73
+ ...(localRefRoot === undefined ? {} : { localRoot: localRootOf(localRefRoot, cwd) }),
74
+ ...(remoteRefHosts === undefined ? {} : { remoteHosts: remoteHostsOf(remoteRefHosts) }),
75
+ };
76
+ }
@@ -6,6 +6,10 @@ import {
6
6
  withEnumsRenamed,
7
7
  } from "../application/transform/document/enum-names-given-back.js";
8
8
  import { operationKeysWritten } from "../application/transform/document/operation-keys-written.js";
9
+ import {
10
+ unmatchedTemplateKeys,
11
+ withTemplatePathRefs,
12
+ } from "../application/transform/document/path-template-refs.js";
9
13
  import { renameTakenAliases } from "../application/transform/document/root-type-aliases.js";
10
14
  import { RunFooter, type RunFooterNames } from "../application/transform/footer/document-footer.js";
11
15
  import { EnumNameClaims } from "../application/transform/footer/enum-name-claims.js";
@@ -144,6 +148,20 @@ function enumValuesChecked(
144
148
  return checked.footer;
145
149
  }
146
150
 
151
+ /** The document with its references into templated paths indexed by their templates. */
152
+ function withTemplateRefs(
153
+ { body, footer, closing }: TransformedDocument,
154
+ keys: ReadonlyMap<string, string>,
155
+ ): TransformedDocument {
156
+ return {
157
+ body: withTemplatePathRefs(body, keys),
158
+ footer: footer.map((entry) =>
159
+ isIrDeclaration(entry) ? (withTemplatePathRefs([entry], keys)[0] ?? entry) : entry,
160
+ ),
161
+ closing: withTemplatePathRefs(closing, keys),
162
+ };
163
+ }
164
+
147
165
  /** @internal Root types, the footer, then the closing declarations (Phase 5, step 6). */
148
166
  export function transformDocument(schema: OpenAPI3, ctx: RunContext): TransformedDocument {
149
167
  const document = schema as DocumentObject;
@@ -160,11 +178,8 @@ export function transformDocument(schema: OpenAPI3, ctx: RunContext): Transforme
160
178
  const rawClosing = documentClosing(document, context, rawFooter.some(isOperations));
161
179
  const parts = { body: rawBody, footer: rawFooter, closing: rawClosing };
162
180
  const { body, footer, closing } = withEnumNamesGivenBack(parts, names);
163
- return {
164
- body: renameTakenAliases(body, footer.filter(isIrDeclaration), closing),
165
- footer,
166
- closing,
167
- };
181
+ const named = { body: renameTakenAliases(body, footer.filter(isIrDeclaration), closing), footer };
182
+ return withTemplateRefs({ ...named, closing }, unmatchedTemplateKeys(document.paths, context));
168
183
  }
169
184
 
170
185
  /**
@@ -29,6 +29,14 @@ export interface DirectoryCheck {
29
29
  isDirectory(file: URL): boolean;
30
30
  }
31
31
 
32
+ /**
33
+ * Whether a `file:` location is an existing file. A schema argument with a `*` is a glob pattern
34
+ * only when no file has that name.
35
+ */
36
+ export interface FileCheck {
37
+ isFile(file: URL): boolean;
38
+ }
39
+
32
40
  /**
33
41
  * An output file that exists before its content is written: the claim created it, empty, when it
34
42
  * was missing, so the file system has given it its own identity.
@@ -0,0 +1,42 @@
1
+ import { irKeyword, irReference } from "../../domain/ir/constructors.js";
2
+ import type { IrMember, IrType } from "../../domain/ir/nodes.js";
3
+ import { HELPER_TYPES } from "./helper-types.js";
4
+ import type { FooterDeclarations } from "./schema/schema-context.js";
5
+
6
+ /** The footer part that declares the `PropertyValues` helper type. */
7
+ export type PropertyValuesFooter = Pick<FooterDeclarations, "requirePropertyValues">;
8
+
9
+ function memberValueTypes(member: IrMember): IrType[] {
10
+ if (member.kind === "indexSignature") {
11
+ return [member.type];
12
+ }
13
+ return member.isOptional ? [member.type, irKeyword("undefined")] : [member.type];
14
+ }
15
+
16
+ /**
17
+ * The value types a key of the type can have, read from its IR: the members of object types,
18
+ * through unions and intersections. A reference (a `$ref`, `Omit`, `WithRequired`) or a raw
19
+ * type is read with `PropertyValues`. Primitives, literals and arrays declare no keys.
20
+ */
21
+ export function declaredValueTypes(type: IrType, footer: PropertyValuesFooter): IrType[] {
22
+ switch (type.kind) {
23
+ case "object":
24
+ return type.members.flatMap(memberValueTypes);
25
+ case "union":
26
+ case "intersection":
27
+ return type.types.flatMap((member) => declaredValueTypes(member, footer));
28
+ case "reference":
29
+ case "indexedAccess":
30
+ case "raw":
31
+ footer.requirePropertyValues();
32
+ return [irReference(HELPER_TYPES.propertyValues.name, [type])];
33
+ case "keyword":
34
+ case "stringLiteral":
35
+ case "numberLiteral":
36
+ case "booleanLiteral":
37
+ case "array":
38
+ case "tuple":
39
+ case "readonly":
40
+ return [];
41
+ }
42
+ }
@@ -9,13 +9,14 @@ import type {
9
9
  } from "../../../domain/ir/nodes.js";
10
10
  import type { ComponentsObject } from "../../../domain/openapi/components-object.js";
11
11
  import type { DocumentObject } from "../../../domain/openapi/document-object.js";
12
- import type { PathsObject } from "../../../domain/openapi/path-item-object.js";
12
+ import type { OperationEntry, PathsObject } from "../../../domain/openapi/path-item-object.js";
13
13
  import { READ_WRITE_MARKER_TYPES } from "../helper-types.js";
14
14
  import type { TransformContext, TransformOptions } from "../transform-context.js";
15
15
  import { componentsType } from "./components.js";
16
16
  import { type DeclaredTypeLookup, declaredTypeLookup } from "./declared-types.js";
17
17
  import { defsRootType } from "./defs-root.js";
18
18
  import { keptDeprecatedComponents } from "./kept-deprecated.js";
19
+ import { isIncludedOperation } from "./path-item.js";
19
20
  import { pathMembers, pathsObjectType } from "./paths.js";
20
21
  import { apiPathsEnumDeclaration } from "./paths-enum.js";
21
22
  import { webhooksType } from "./webhooks.js";
@@ -210,7 +211,8 @@ export function documentClosing(
210
211
  declarations.push(emptyAlias(OPERATIONS_NAME));
211
212
  }
212
213
  if (context.options.makePathsEnum && document.paths) {
213
- declarations.push(apiPathsEnumDeclaration(document.paths));
214
+ const isIncluded = (operation: OperationEntry) => isIncludedOperation(operation, context);
215
+ declarations.push(apiPathsEnumDeclaration(document.paths, isIncluded));
214
216
  }
215
217
  return declarations;
216
218
  }
@@ -0,0 +1,88 @@
1
+ import type { IrMember, IrType } from "../../../domain/ir/nodes.js";
2
+ import type { Schema } from "../../../domain/openapi/schema-object.js";
3
+ import { transformSchema } from "../schema/schema-transform.js";
4
+ import type { TransformContext } from "../transform-context.js";
5
+ import { unionOf } from "../type-combinators.js";
6
+ import type { ResolvedEntry } from "./parameter-entries.js";
7
+
8
+ /**
9
+ * `exploded-query-objects` and index signatures. The keys of an exploded object are keys of
10
+ * `query`, next to the other parameters. TypeScript checks an index signature against every key
11
+ * of a value, so `{ page?: number } & { [key: string]: string }` admits no `page`, and no value
12
+ * with another parameter at all. An index signature of an exploded object therefore also takes
13
+ * the value types of the other keys of `query`: those keys carry their own values.
14
+ */
15
+
16
+ function refOf(value: unknown): string | undefined {
17
+ const ref = (value as { $ref?: unknown } | undefined)?.$ref;
18
+ return typeof ref === "string" ? ref : undefined;
19
+ }
20
+
21
+ /** A schema `$ref` chain followed to its end, with the pointer where that schema is declared. */
22
+ function declaredSchema(schema: Schema | undefined, ref: string, context: TransformContext) {
23
+ let declared = { schema, path: ref };
24
+ const seen = new Set<string>();
25
+ for (let next = refOf(schema); next !== undefined && !seen.has(next); ) {
26
+ seen.add(next);
27
+ declared = { schema: context.refs.resolve(next) as Schema | undefined, path: next };
28
+ next = refOf(declared.schema);
29
+ }
30
+ return declared;
31
+ }
32
+
33
+ /**
34
+ * The type of an exploded parameter whose own type is a reference (its `$ref`, or the `$ref` of
35
+ * its schema), written out where its schema is declared: the index signature of a referenced type
36
+ * cannot be changed. The location is the one `components` types it at, so the enums it declares
37
+ * are the ones the reference names. Null when no `$ref` is involved.
38
+ */
39
+ export function declaredObjectType(entry: ResolvedEntry, context: TransformContext): IrType | null {
40
+ const { original, resolved } = entry;
41
+ const schemaRef = refOf(resolved.schema);
42
+ if (schemaRef !== undefined) {
43
+ const { schema, path } = declaredSchema(resolved.schema, schemaRef, context);
44
+ return transformSchema(schema, { path, schema }, context);
45
+ }
46
+ const parameterRef = refOf(original);
47
+ return parameterRef === undefined
48
+ ? null
49
+ : transformSchema(resolved.schema, { path: parameterRef, schema: resolved }, context);
50
+ }
51
+
52
+ function isIndexSignature(member: IrMember): boolean {
53
+ return member.kind === "indexSignature";
54
+ }
55
+
56
+ /** Whether the type has an index signature in its objects, through unions and intersections. */
57
+ export function hasIndexSignature(type: IrType): boolean {
58
+ switch (type.kind) {
59
+ case "object":
60
+ return type.members.some(isIndexSignature);
61
+ case "union":
62
+ case "intersection":
63
+ return type.types.some(hasIndexSignature);
64
+ default:
65
+ return false;
66
+ }
67
+ }
68
+
69
+ function widenedMember(member: IrMember, values: readonly IrType[]): IrMember {
70
+ // `unknown` takes every value already.
71
+ const isUnknown = member.type.kind === "keyword" && member.type.keyword === "unknown";
72
+ return isIndexSignature(member) && !isUnknown
73
+ ? { ...member, type: unionOf([member.type, ...values]) }
74
+ : member;
75
+ }
76
+
77
+ /** The type with the values added to each index signature of its objects. */
78
+ export function withIndexValues(type: IrType, values: readonly IrType[]): IrType {
79
+ switch (type.kind) {
80
+ case "object":
81
+ return { ...type, members: type.members.map((member) => widenedMember(member, values)) };
82
+ case "union":
83
+ case "intersection":
84
+ return { ...type, types: type.types.map((member) => withIndexValues(member, values)) };
85
+ default:
86
+ return type;
87
+ }
88
+ }
@@ -2,6 +2,8 @@ import { irObject } from "../../../domain/ir/constructors.js";
2
2
  import type { IrType } from "../../../domain/ir/nodes.js";
3
3
  import type { HeaderObject } from "../../../domain/openapi/header-object.js";
4
4
  import { escapePointerSegment } from "../../../domain/openapi/json-pointer.js";
5
+ import type { ContentMap } from "../../../domain/openapi/media-type-object.js";
6
+ import { FIX } from "../../../domain/output-fixes.js";
5
7
  import { documentEntries } from "../document-entries.js";
6
8
  import type { SchemaContext, SchemaLocation } from "../schema/schema-context.js";
7
9
  import { transformSchema } from "../schema/schema-transform.js";
@@ -10,8 +12,10 @@ import { documentMember } from "./document-member.js";
10
12
  import { contentEntryType } from "./media-type.js";
11
13
 
12
14
  /**
13
- * A header's type (OpenAPI 3.1 §4.8.21): its schema, else an object with one property per media
14
- * type in `content`, else `unknown`.
15
+ * A header's type (OpenAPI 3.1 §4.8.21): its schema, else the type of its `content`, else
16
+ * `unknown`. `content` holds one media type whose schema describes the value; the original typed
17
+ * it as an object with one property per media type, which the `parameter-header-content` fix
18
+ * replaces by the media type's type, as for a parameter.
15
19
  */
16
20
  export function headerType(
17
21
  header: HeaderObject,
@@ -24,11 +28,27 @@ export function headerType(
24
28
  if (!header.content) {
25
29
  return UNKNOWN_TYPE;
26
30
  }
31
+ if (context.options.fixes.has(FIX.PARAMETER_HEADER_CONTENT)) {
32
+ // The first media type in document order, as for a parameter: `--alphabetize` sorts only
33
+ // the members it writes.
34
+ const first = Object.entries(header.content)[0];
35
+ return first === undefined ? UNKNOWN_TYPE : mediaType(...first, location, context);
36
+ }
27
37
  const members = documentEntries(header.content, context.options).map(([contentType, entry]) => {
28
- // Unlike other content maps, the original joins this path by hand: a missing path is `#`.
29
- const path = `${location.path ?? "#"}/${escapePointerSegment(contentType)}`;
30
- const type = contentEntryType(entry, { ...location, path }, context);
38
+ const type = mediaType(contentType, entry, location, context);
31
39
  return documentMember(contentType, type, entry, { isReadonly: context.options.immutable });
32
40
  });
33
41
  return irObject(members);
34
42
  }
43
+
44
+ /** The type of one media type of a header, at the path the original gave it. */
45
+ function mediaType(
46
+ contentType: string,
47
+ entry: ContentMap[string],
48
+ location: SchemaLocation,
49
+ context: SchemaContext,
50
+ ): IrType {
51
+ // Unlike other content maps, the original joins this path by hand: a missing path is `#`.
52
+ const path = `${location.path ?? "#"}/${escapePointerSegment(contentType)}`;
53
+ return contentEntryType(entry, { ...location, path }, context);
54
+ }
@@ -13,7 +13,7 @@ import { contentEntryType } from "./media-type.js";
13
13
  /**
14
14
  * A parameter typed by the schema of its one `content` media type. An `in: querystring` parameter
15
15
  * (OpenAPI 3.2) is the whole query string (ADR-020); without a media type it is `string`, the
16
- * query string as text. With the `parameter-content` fix, any other parameter with `content`
16
+ * query string as text. With the `parameter-header-content` fix, any other parameter with `content`
17
17
  * (OpenAPI 3.0 and later, for a JSON value in a query parameter) is typed the same way.
18
18
  */
19
19
  function contentParameterType(
@@ -34,14 +34,14 @@ function isTypedByContent(parameter: ParameterObject, context: SchemaContext): b
34
34
  return true;
35
35
  }
36
36
  const hasContent = parameter.schema === undefined && parameter.content !== undefined;
37
- return hasContent && context.options.fixes.has(FIX.PARAMETER_CONTENT);
37
+ return hasContent && context.options.fixes.has(FIX.PARAMETER_HEADER_CONTENT);
38
38
  }
39
39
 
40
40
  /**
41
41
  * A parameter's type (OpenAPI 3.2 §4.8.12). Without a schema it is `string`, not `unknown`:
42
42
  * query, path, header and cookie values all arrive as text. `content` is read for
43
- * `in: querystring`, and with the `parameter-content` fix for every parameter; without the fix
44
- * other parameters with `content` keep the original's `string`.
43
+ * `in: querystring`, and with the `parameter-header-content` fix for every parameter; without
44
+ * the fix other parameters with `content` keep the original's `string`.
45
45
  */
46
46
  export function parameterType(
47
47
  parameter: ParameterObject,
@@ -16,10 +16,12 @@ import {
16
16
  QUERYSTRING_LOCATION,
17
17
  } from "../../../domain/openapi/parameter-object.js";
18
18
  import { FIX } from "../../../domain/output-fixes.js";
19
+ import { declaredValueTypes } from "../declared-value-types.js";
19
20
  import type { SchemaLocation } from "../schema/schema-context.js";
20
21
  import { withSchemaComment } from "../schema-comment.js";
21
22
  import type { TransformContext } from "../transform-context.js";
22
23
  import { isExplodedQueryObject } from "./exploded-query.js";
24
+ import { declaredObjectType, hasIndexSignature, withIndexValues } from "./exploded-query-values.js";
23
25
  import { parameterType } from "./parameter.js";
24
26
  import {
25
27
  type ParameterEntry,
@@ -126,17 +128,50 @@ function isExplodedEntry(entry: ResolvedEntry, context: TransformContext): boole
126
128
  );
127
129
  }
128
130
 
131
+ /** An exploded object parameter with its type, typed in document order. */
132
+ interface ExplodedEntry {
133
+ readonly entry: ResolvedEntry;
134
+ readonly type: IrType;
135
+ }
136
+
129
137
  /**
130
- * An exploded object parameter's part of `query`: its object type. A parameter that may be left
131
- * out sends none of its keys then, so they are optional: `Partial<…>`.
138
+ * The object type of an exploded parameter, written out when it is a reference to a type with an
139
+ * index signature and `query` has other keys, which that signature must then admit. Without
140
+ * other keys the reference is kept.
132
141
  */
133
- function explodedPart(
134
- entry: ResolvedEntry,
135
- location: SchemaLocation,
142
+ function explodedObjectType(
143
+ { entry, type }: ExplodedEntry,
144
+ hasOtherKeys: boolean,
136
145
  context: TransformContext,
137
146
  ): IrType {
138
- const type = entryType(entry, location, context);
139
- return entry.resolved.required ? type : irReference("Partial", [type]);
147
+ if (!hasOtherKeys) {
148
+ return type;
149
+ }
150
+ const declared = declaredObjectType(entry, context);
151
+ return declared !== null && hasIndexSignature(declared) ? declared : type;
152
+ }
153
+
154
+ /**
155
+ * The exploded object parameters' parts of `query`: their object types, each index signature
156
+ * widened by the value types of the other keys (see `exploded-query-values.ts`). A parameter that
157
+ * may be left out sends none of its keys then, so they are optional: `Partial<…>`.
158
+ */
159
+ function explodedParts(
160
+ exploded: readonly ExplodedEntry[],
161
+ members: readonly IrPropertySignature[],
162
+ context: TransformContext,
163
+ ): IrType[] {
164
+ const hasOtherKeys = members.length > 0 || exploded.length > 1;
165
+ const types = exploded.map((part) => explodedObjectType(part, hasOtherKeys, context));
166
+ const others = (index: number) => [
167
+ ...(members.length > 0 ? [irObject(members)] : []),
168
+ ...types.filter((_type, other) => other !== index),
169
+ ];
170
+ return types.map((type, index) => {
171
+ const values = others(index).flatMap((other) => declaredValueTypes(other, context.footer));
172
+ const widened = withIndexValues(type, values);
173
+ return exploded[index]?.entry.resolved.required ? widened : irReference("Partial", [widened]);
174
+ });
140
175
  }
141
176
 
142
177
  /** A location's parameters as members, and the object types of its exploded parameters. */
@@ -153,11 +188,11 @@ function locationParts(
153
188
  context: TransformContext,
154
189
  ): LocationParts {
155
190
  const members: IrPropertySignature[] = [];
156
- const exploded: IrType[] = [];
191
+ const exploded: ExplodedEntry[] = [];
157
192
  let isOptional = true;
158
193
  for (const entry of entries) {
159
194
  if (isExplodedEntry(entry, context)) {
160
- exploded.push(explodedPart(entry, location, context));
195
+ exploded.push({ entry, type: entryType(entry, location, context) });
161
196
  isOptional &&= !entry.resolved.required;
162
197
  continue;
163
198
  }
@@ -165,7 +200,7 @@ function locationParts(
165
200
  members.push(member);
166
201
  isOptional &&= member.isOptional;
167
202
  }
168
- return { members, exploded, isOptional };
203
+ return { members, exploded: explodedParts(exploded, members, context), isOptional };
169
204
  }
170
205
 
171
206
  /** The members' object, intersected with the exploded parameters' types; `never` for none. */
@@ -29,7 +29,7 @@ export function operationKeyOf(operationId: string): string {
29
29
  }
30
30
 
31
31
  /** Whether the method has an operation, not removed by `--exclude-deprecated`. */
32
- function isIncluded(
32
+ export function isIncludedOperation(
33
33
  operation: OperationEntry | undefined,
34
34
  context: TransformContext,
35
35
  ): operation is OperationEntry {
@@ -94,7 +94,7 @@ function methodMember(
94
94
  context: TransformContext,
95
95
  ): IrPropertySignature {
96
96
  const operation = pathItem[method];
97
- if (!isIncluded(operation, context)) {
97
+ if (!isIncludedOperation(operation, context)) {
98
98
  const isReadonly = context.options.immutable;
99
99
  return irProperty(toPropertyName(method), irKeyword("never"), { isOptional: true, isReadonly });
100
100
  }
@@ -113,7 +113,7 @@ function additionalOperationsMember(
113
113
  ): IrPropertySignature | null {
114
114
  const entries = documentEntries(pathItem.additionalOperations ?? {}, context.options);
115
115
  const members = entries
116
- .filter(([, operation]) => isIncluded(operation, context))
116
+ .filter(([, operation]) => isIncludedOperation(operation, context))
117
117
  .map(([method, operation]) => {
118
118
  const path = appendPointer(location.path, [ADDITIONAL_OPERATIONS_KEY, method]);
119
119
  return operationMember({ name: method, operation, path }, pathItem, location, context);