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

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 (366) hide show
  1. package/CHANGELOG.md +214 -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-document-names.cjs +50 -0
  23. package/dist/adapters/redocly-loader/query-document-names.cjs.map +1 -0
  24. package/dist/adapters/redocly-loader/query-document-names.mjs +48 -0
  25. package/dist/adapters/redocly-loader/query-document-names.mjs.map +1 -0
  26. package/dist/adapters/redocly-loader/query-named-components.cjs +129 -0
  27. package/dist/adapters/redocly-loader/query-named-components.cjs.map +1 -0
  28. package/dist/adapters/redocly-loader/query-named-components.mjs +126 -0
  29. package/dist/adapters/redocly-loader/query-named-components.mjs.map +1 -0
  30. package/dist/adapters/redocly-loader/redocly-config-source.cjs +3 -0
  31. package/dist/adapters/redocly-loader/redocly-config-source.cjs.map +1 -1
  32. package/dist/adapters/redocly-loader/redocly-config-source.mjs +4 -1
  33. package/dist/adapters/redocly-loader/redocly-config-source.mjs.map +1 -1
  34. package/dist/adapters/redocly-loader/redocly-schema-loader.cjs +39 -18
  35. package/dist/adapters/redocly-loader/redocly-schema-loader.cjs.map +1 -1
  36. package/dist/adapters/redocly-loader/redocly-schema-loader.mjs +40 -19
  37. package/dist/adapters/redocly-loader/redocly-schema-loader.mjs.map +1 -1
  38. package/dist/adapters/redocly-loader/ref-access.cjs +25 -4
  39. package/dist/adapters/redocly-loader/ref-access.cjs.map +1 -1
  40. package/dist/adapters/redocly-loader/ref-access.mjs +26 -5
  41. package/dist/adapters/redocly-loader/ref-access.mjs.map +1 -1
  42. package/dist/adapters/redocly-loader/ref-holders.cjs +52 -10
  43. package/dist/adapters/redocly-loader/ref-holders.cjs.map +1 -1
  44. package/dist/adapters/redocly-loader/ref-holders.mjs +52 -11
  45. package/dist/adapters/redocly-loader/ref-holders.mjs.map +1 -1
  46. package/dist/adapters/redocly-loader/remote-fetch.cjs +20 -3
  47. package/dist/adapters/redocly-loader/remote-fetch.cjs.map +1 -1
  48. package/dist/adapters/redocly-loader/remote-fetch.mjs +20 -4
  49. package/dist/adapters/redocly-loader/remote-fetch.mjs.map +1 -1
  50. package/dist/adapters/redocly-loader/remote-node-limit.cjs +17 -3
  51. package/dist/adapters/redocly-loader/remote-node-limit.cjs.map +1 -1
  52. package/dist/adapters/redocly-loader/remote-node-limit.mjs +17 -3
  53. package/dist/adapters/redocly-loader/remote-node-limit.mjs.map +1 -1
  54. package/dist/adapters/redocly-loader/schema-source.cjs +7 -2
  55. package/dist/adapters/redocly-loader/schema-source.cjs.map +1 -1
  56. package/dist/adapters/redocly-loader/schema-source.mjs +8 -3
  57. package/dist/adapters/redocly-loader/schema-source.mjs.map +1 -1
  58. package/dist/adapters/terminal-logger/terminal-logger.cjs +7 -3
  59. package/dist/adapters/terminal-logger/terminal-logger.cjs.map +1 -1
  60. package/dist/adapters/terminal-logger/terminal-logger.mjs +7 -3
  61. package/dist/adapters/terminal-logger/terminal-logger.mjs.map +1 -1
  62. package/dist/api/cwd-option.cjs +58 -0
  63. package/dist/api/cwd-option.cjs.map +1 -0
  64. package/dist/api/cwd-option.mjs +52 -0
  65. package/dist/api/cwd-option.mjs.map +1 -0
  66. package/dist/api/default-ports.cjs +15 -7
  67. package/dist/api/default-ports.cjs.map +1 -1
  68. package/dist/api/default-ports.mjs +14 -8
  69. package/dist/api/default-ports.mjs.map +1 -1
  70. package/dist/api/document-refs.cjs +16 -1
  71. package/dist/api/document-refs.cjs.map +1 -1
  72. package/dist/api/document-refs.mjs +16 -1
  73. package/dist/api/document-refs.mjs.map +1 -1
  74. package/dist/api/document-run.cjs +6 -26
  75. package/dist/api/document-run.cjs.map +1 -1
  76. package/dist/api/document-run.mjs +5 -21
  77. package/dist/api/document-run.mjs.map +1 -1
  78. package/dist/api/legacy-utils.cjs +1 -1
  79. package/dist/api/legacy-utils.cjs.map +1 -1
  80. package/dist/api/legacy-utils.mjs +1 -1
  81. package/dist/api/legacy-utils.mjs.map +1 -1
  82. package/dist/api/openapi-ts.cjs +1 -2
  83. package/dist/api/openapi-ts.cjs.map +1 -1
  84. package/dist/api/openapi-ts.mjs +2 -3
  85. package/dist/api/openapi-ts.mjs.map +1 -1
  86. package/dist/api/option-errors.cjs +24 -0
  87. package/dist/api/option-errors.cjs.map +1 -0
  88. package/dist/api/option-errors.mjs +21 -0
  89. package/dist/api/option-errors.mjs.map +1 -0
  90. package/dist/api/ref-limit-options.cjs +58 -0
  91. package/dist/api/ref-limit-options.cjs.map +1 -0
  92. package/dist/api/ref-limit-options.mjs +52 -0
  93. package/dist/api/ref-limit-options.mjs.map +1 -0
  94. package/dist/api/transform-schema.cjs +12 -5
  95. package/dist/api/transform-schema.cjs.map +1 -1
  96. package/dist/api/transform-schema.mjs +12 -5
  97. package/dist/api/transform-schema.mjs.map +1 -1
  98. package/dist/application/transform/declared-value-types.cjs +36 -0
  99. package/dist/application/transform/declared-value-types.cjs.map +1 -0
  100. package/dist/application/transform/declared-value-types.mjs +34 -0
  101. package/dist/application/transform/declared-value-types.mjs.map +1 -0
  102. package/dist/application/transform/document/document.cjs +3 -1
  103. package/dist/application/transform/document/document.cjs.map +1 -1
  104. package/dist/application/transform/document/document.mjs +3 -1
  105. package/dist/application/transform/document/document.mjs.map +1 -1
  106. package/dist/application/transform/document/exploded-query-values.cjs +63 -0
  107. package/dist/application/transform/document/exploded-query-values.cjs.map +1 -0
  108. package/dist/application/transform/document/exploded-query-values.mjs +59 -0
  109. package/dist/application/transform/document/exploded-query-values.mjs.map +1 -0
  110. package/dist/application/transform/document/header.cjs +11 -3
  111. package/dist/application/transform/document/header.cjs.map +1 -1
  112. package/dist/application/transform/document/header.mjs +10 -2
  113. package/dist/application/transform/document/header.mjs.map +1 -1
  114. package/dist/application/transform/document/parameter.cjs +1 -1
  115. package/dist/application/transform/document/parameter.cjs.map +1 -1
  116. package/dist/application/transform/document/parameter.mjs +1 -1
  117. package/dist/application/transform/document/parameter.mjs.map +1 -1
  118. package/dist/application/transform/document/parameters.cjs +23 -5
  119. package/dist/application/transform/document/parameters.cjs.map +1 -1
  120. package/dist/application/transform/document/parameters.mjs +23 -5
  121. package/dist/application/transform/document/parameters.mjs.map +1 -1
  122. package/dist/application/transform/document/path-item.cjs +4 -3
  123. package/dist/application/transform/document/path-item.cjs.map +1 -1
  124. package/dist/application/transform/document/path-item.mjs +4 -4
  125. package/dist/application/transform/document/path-item.mjs.map +1 -1
  126. package/dist/application/transform/document/path-template-key.cjs +84 -0
  127. package/dist/application/transform/document/path-template-key.cjs.map +1 -0
  128. package/dist/application/transform/document/path-template-key.mjs +79 -0
  129. package/dist/application/transform/document/path-template-key.mjs.map +1 -0
  130. package/dist/application/transform/document/path-template-refs.cjs +80 -0
  131. package/dist/application/transform/document/path-template-refs.cjs.map +1 -0
  132. package/dist/application/transform/document/path-template-refs.mjs +77 -0
  133. package/dist/application/transform/document/path-template-refs.mjs.map +1 -0
  134. package/dist/application/transform/document/paths-enum.cjs +7 -6
  135. package/dist/application/transform/document/paths-enum.cjs.map +1 -1
  136. package/dist/application/transform/document/paths-enum.mjs +7 -6
  137. package/dist/application/transform/document/paths-enum.mjs.map +1 -1
  138. package/dist/application/transform/document/paths.cjs +38 -66
  139. package/dist/application/transform/document/paths.cjs.map +1 -1
  140. package/dist/application/transform/document/paths.mjs +36 -66
  141. package/dist/application/transform/document/paths.mjs.map +1 -1
  142. package/dist/application/transform/document/webhooks.cjs +10 -3
  143. package/dist/application/transform/document/webhooks.cjs.map +1 -1
  144. package/dist/application/transform/document/webhooks.mjs +9 -2
  145. package/dist/application/transform/document/webhooks.mjs.map +1 -1
  146. package/dist/application/transform/footer/document-footer.cjs +6 -3
  147. package/dist/application/transform/footer/document-footer.cjs.map +1 -1
  148. package/dist/application/transform/footer/document-footer.mjs +6 -3
  149. package/dist/application/transform/footer/document-footer.mjs.map +1 -1
  150. package/dist/application/transform/helper-types.cjs +3 -3
  151. package/dist/application/transform/helper-types.cjs.map +1 -1
  152. package/dist/application/transform/helper-types.mjs +3 -3
  153. package/dist/application/transform/helper-types.mjs.map +1 -1
  154. package/dist/application/transform/ref-pointer/generated-location.cjs +4 -4
  155. package/dist/application/transform/ref-pointer/generated-location.cjs.map +1 -1
  156. package/dist/application/transform/ref-pointer/generated-location.mjs +1 -1
  157. package/dist/application/transform/ref-pointer/generated-location.mjs.map +1 -1
  158. package/dist/application/transform/ref-pointer/generated-shape.cjs +63 -64
  159. package/dist/application/transform/ref-pointer/generated-shape.cjs.map +1 -1
  160. package/dist/application/transform/ref-pointer/generated-shape.mjs +57 -53
  161. package/dist/application/transform/ref-pointer/generated-shape.mjs.map +1 -1
  162. package/dist/application/transform/ref-pointer/inline-cycles.cjs +2 -2
  163. package/dist/application/transform/ref-pointer/inline-cycles.cjs.map +1 -1
  164. package/dist/application/transform/ref-pointer/inline-cycles.mjs +1 -1
  165. package/dist/application/transform/ref-pointer/inline-cycles.mjs.map +1 -1
  166. package/dist/application/transform/ref-pointer/located-node.cjs +23 -0
  167. package/dist/application/transform/ref-pointer/located-node.cjs.map +1 -0
  168. package/dist/application/transform/ref-pointer/located-node.mjs +18 -0
  169. package/dist/application/transform/ref-pointer/located-node.mjs.map +1 -0
  170. package/dist/application/transform/ref-pointer/original-index.cjs +69 -14
  171. package/dist/application/transform/ref-pointer/original-index.cjs.map +1 -1
  172. package/dist/application/transform/ref-pointer/original-index.mjs +71 -16
  173. package/dist/application/transform/ref-pointer/original-index.mjs.map +1 -1
  174. package/dist/application/transform/ref-pointer/pointer-type.cjs +2 -2
  175. package/dist/application/transform/ref-pointer/pointer-type.cjs.map +1 -1
  176. package/dist/application/transform/ref-pointer/pointer-type.mjs +1 -1
  177. package/dist/application/transform/ref-pointer/pointer-type.mjs.map +1 -1
  178. package/dist/application/transform/ref-pointer/pointer-walk.cjs +54 -13
  179. package/dist/application/transform/ref-pointer/pointer-walk.cjs.map +1 -1
  180. package/dist/application/transform/ref-pointer/pointer-walk.mjs +46 -7
  181. package/dist/application/transform/ref-pointer/pointer-walk.mjs.map +1 -1
  182. package/dist/application/transform/ref-pointer/property-shape.cjs +57 -0
  183. package/dist/application/transform/ref-pointer/property-shape.cjs.map +1 -0
  184. package/dist/application/transform/ref-pointer/property-shape.mjs +49 -0
  185. package/dist/application/transform/ref-pointer/property-shape.mjs.map +1 -0
  186. package/dist/application/transform/ref-pointer/typed-walk.cjs +90 -22
  187. package/dist/application/transform/ref-pointer/typed-walk.cjs.map +1 -1
  188. package/dist/application/transform/ref-pointer/typed-walk.mjs +87 -19
  189. package/dist/application/transform/ref-pointer/typed-walk.mjs.map +1 -1
  190. package/dist/application/transform/schema/access-flags.cjs.map +1 -1
  191. package/dist/application/transform/schema/access-flags.mjs.map +1 -1
  192. package/dist/application/transform/schema/index-signature-type.cjs +21 -0
  193. package/dist/application/transform/schema/index-signature-type.cjs.map +1 -0
  194. package/dist/application/transform/schema/index-signature-type.mjs +19 -0
  195. package/dist/application/transform/schema/index-signature-type.mjs.map +1 -0
  196. package/dist/application/transform/schema/input-location.cjs +23 -0
  197. package/dist/application/transform/schema/input-location.cjs.map +1 -0
  198. package/dist/application/transform/schema/input-location.mjs +21 -0
  199. package/dist/application/transform/schema/input-location.mjs.map +1 -0
  200. package/dist/application/transform/schema/object-type.cjs +15 -5
  201. package/dist/application/transform/schema/object-type.cjs.map +1 -1
  202. package/dist/application/transform/schema/object-type.mjs +15 -5
  203. package/dist/application/transform/schema/object-type.mjs.map +1 -1
  204. package/dist/application/transform/schema/schema-transform.cjs +2 -1
  205. package/dist/application/transform/schema/schema-transform.cjs.map +1 -1
  206. package/dist/application/transform/schema/schema-transform.mjs +2 -1
  207. package/dist/application/transform/schema/schema-transform.mjs.map +1 -1
  208. package/dist/application/transform/schema/unevaluated-properties.cjs +2 -30
  209. package/dist/application/transform/schema/unevaluated-properties.cjs.map +1 -1
  210. package/dist/application/transform/schema/unevaluated-properties.mjs +3 -31
  211. package/dist/application/transform/schema/unevaluated-properties.mjs.map +1 -1
  212. package/dist/cli/api-logger.cjs +13 -0
  213. package/dist/cli/api-logger.cjs.map +1 -0
  214. package/dist/cli/api-logger.mjs +11 -0
  215. package/dist/cli/api-logger.mjs.map +1 -0
  216. package/dist/cli/api-settings.cjs +10 -17
  217. package/dist/cli/api-settings.cjs.map +1 -1
  218. package/dist/cli/api-settings.mjs +10 -17
  219. package/dist/cli/api-settings.mjs.map +1 -1
  220. package/dist/cli/api-writes.cjs +7 -1
  221. package/dist/cli/api-writes.cjs.map +1 -1
  222. package/dist/cli/api-writes.mjs +7 -1
  223. package/dist/cli/api-writes.mjs.map +1 -1
  224. package/dist/cli/cli-arguments.cjs +32 -74
  225. package/dist/cli/cli-arguments.cjs.map +1 -1
  226. package/dist/cli/cli-arguments.mjs +28 -70
  227. package/dist/cli/cli-arguments.mjs.map +1 -1
  228. package/dist/cli/cli-errors.cjs +18 -0
  229. package/dist/cli/cli-errors.cjs.map +1 -1
  230. package/dist/cli/cli-errors.mjs +18 -1
  231. package/dist/cli/cli-errors.mjs.map +1 -1
  232. package/dist/cli/cli-flag-names.cjs +77 -0
  233. package/dist/cli/cli-flag-names.cjs.map +1 -0
  234. package/dist/cli/cli-flag-names.mjs +64 -0
  235. package/dist/cli/cli-flag-names.mjs.map +1 -0
  236. package/dist/cli/cli-output.cjs +13 -6
  237. package/dist/cli/cli-output.cjs.map +1 -1
  238. package/dist/cli/cli-output.mjs +14 -7
  239. package/dist/cli/cli-output.mjs.map +1 -1
  240. package/dist/cli/main.cjs +11 -5
  241. package/dist/cli/main.cjs.map +1 -1
  242. package/dist/cli/main.mjs +11 -5
  243. package/dist/cli/main.mjs.map +1 -1
  244. package/dist/cli/redocly-apis.cjs +33 -12
  245. package/dist/cli/redocly-apis.cjs.map +1 -1
  246. package/dist/cli/redocly-apis.mjs +35 -14
  247. package/dist/cli/redocly-apis.mjs.map +1 -1
  248. package/dist/cli/ref-limits.cjs +15 -1
  249. package/dist/cli/ref-limits.cjs.map +1 -1
  250. package/dist/cli/ref-limits.mjs +15 -2
  251. package/dist/cli/ref-limits.mjs.map +1 -1
  252. package/dist/cli/run-cli.cjs +24 -7
  253. package/dist/cli/run-cli.cjs.map +1 -1
  254. package/dist/cli/run-cli.mjs +25 -8
  255. package/dist/cli/run-cli.mjs.map +1 -1
  256. package/dist/cli/written-flags.cjs +83 -0
  257. package/dist/cli/written-flags.cjs.map +1 -0
  258. package/dist/cli/written-flags.mjs +77 -0
  259. package/dist/cli/written-flags.mjs.map +1 -0
  260. package/dist/domain/cause-secrets.cjs +31 -16
  261. package/dist/domain/cause-secrets.cjs.map +1 -1
  262. package/dist/domain/cause-secrets.mjs +31 -16
  263. package/dist/domain/cause-secrets.mjs.map +1 -1
  264. package/dist/domain/errors.cjs +3 -0
  265. package/dist/domain/errors.cjs.map +1 -1
  266. package/dist/domain/errors.mjs +3 -0
  267. package/dist/domain/errors.mjs.map +1 -1
  268. package/dist/domain/location-text.cjs +15 -0
  269. package/dist/domain/location-text.cjs.map +1 -0
  270. package/dist/domain/location-text.mjs +13 -0
  271. package/dist/domain/location-text.mjs.map +1 -0
  272. package/dist/domain/openapi/document-places.cjs +55 -0
  273. package/dist/domain/openapi/document-places.cjs.map +1 -0
  274. package/dist/domain/openapi/document-places.mjs +53 -0
  275. package/dist/domain/openapi/document-places.mjs.map +1 -0
  276. package/dist/domain/output-fixes.cjs +8 -2
  277. package/dist/domain/output-fixes.cjs.map +1 -1
  278. package/dist/domain/output-fixes.mjs +8 -2
  279. package/dist/domain/output-fixes.mjs.map +1 -1
  280. package/dist/domain/terminal-text.cjs +31 -0
  281. package/dist/domain/terminal-text.cjs.map +1 -0
  282. package/dist/domain/terminal-text.mjs +29 -0
  283. package/dist/domain/terminal-text.mjs.map +1 -0
  284. package/dist/domain/warnings.cjs +1 -0
  285. package/dist/domain/warnings.cjs.map +1 -1
  286. package/dist/domain/warnings.mjs +1 -0
  287. package/dist/domain/warnings.mjs.map +1 -1
  288. package/dist/index.d.cts +29 -18
  289. package/dist/index.d.mts +29 -18
  290. package/dist/index.d.ts +29 -18
  291. package/package.json +1 -1
  292. package/src/adapters/node-file-system/node-file-system.ts +14 -1
  293. package/src/adapters/pipe-safe-writer/pipe-safe-writer.ts +29 -0
  294. package/src/adapters/redocly-loader/bundled-mapping-copies.ts +7 -42
  295. package/src/adapters/redocly-loader/component-schemas.ts +19 -0
  296. package/src/adapters/redocly-loader/mapping-schema-names.ts +25 -0
  297. package/src/adapters/redocly-loader/query-document-names.ts +79 -0
  298. package/src/adapters/redocly-loader/query-named-components.ts +200 -0
  299. package/src/adapters/redocly-loader/redocly-config-source.ts +11 -1
  300. package/src/adapters/redocly-loader/redocly-schema-loader.ts +63 -17
  301. package/src/adapters/redocly-loader/ref-access.ts +32 -5
  302. package/src/adapters/redocly-loader/ref-holders.ts +75 -14
  303. package/src/adapters/redocly-loader/remote-fetch.ts +37 -2
  304. package/src/adapters/redocly-loader/remote-node-limit.ts +44 -4
  305. package/src/adapters/redocly-loader/schema-source.ts +15 -3
  306. package/src/adapters/terminal-logger/terminal-logger.ts +10 -4
  307. package/src/api/cwd-option.ts +76 -0
  308. package/src/api/default-ports.ts +33 -10
  309. package/src/api/document-refs.ts +29 -1
  310. package/src/api/document-run.ts +7 -32
  311. package/src/api/legacy-utils.ts +1 -1
  312. package/src/api/openapi-ts.ts +20 -4
  313. package/src/api/option-errors.ts +22 -0
  314. package/src/api/ref-limit-options.ts +69 -0
  315. package/src/api/transform-schema.ts +20 -5
  316. package/src/application/ports/file-system.ts +8 -0
  317. package/src/application/ports/redocly-config-source.ts +5 -0
  318. package/src/application/transform/declared-value-types.ts +42 -0
  319. package/src/application/transform/document/document.ts +4 -2
  320. package/src/application/transform/document/exploded-query-values.ts +88 -0
  321. package/src/application/transform/document/header.ts +25 -5
  322. package/src/application/transform/document/parameter.ts +4 -4
  323. package/src/application/transform/document/parameters.ts +45 -10
  324. package/src/application/transform/document/path-item.ts +3 -3
  325. package/src/application/transform/document/path-template-key.ts +152 -0
  326. package/src/application/transform/document/path-template-refs.ts +127 -0
  327. package/src/application/transform/document/paths-enum.ts +23 -9
  328. package/src/application/transform/document/paths.ts +53 -123
  329. package/src/application/transform/document/webhooks.ts +18 -5
  330. package/src/application/transform/footer/document-footer.ts +18 -8
  331. package/src/application/transform/helper-types.ts +4 -4
  332. package/src/application/transform/ref-pointer/generated-location.ts +1 -1
  333. package/src/application/transform/ref-pointer/generated-shape.ts +115 -83
  334. package/src/application/transform/ref-pointer/inline-cycles.ts +1 -1
  335. package/src/application/transform/ref-pointer/located-node.ts +38 -0
  336. package/src/application/transform/ref-pointer/original-index.ts +106 -20
  337. package/src/application/transform/ref-pointer/pointer-type.ts +1 -1
  338. package/src/application/transform/ref-pointer/pointer-walk.ts +82 -8
  339. package/src/application/transform/ref-pointer/property-shape.ts +106 -0
  340. package/src/application/transform/ref-pointer/typed-walk.ts +143 -29
  341. package/src/application/transform/schema/access-flags.ts +5 -2
  342. package/src/application/transform/schema/index-signature-type.ts +31 -0
  343. package/src/application/transform/schema/input-location.ts +35 -0
  344. package/src/application/transform/schema/object-type.ts +18 -8
  345. package/src/application/transform/schema/schema-transform.ts +2 -1
  346. package/src/application/transform/schema/unevaluated-properties.ts +4 -44
  347. package/src/cli/api-logger.ts +17 -0
  348. package/src/cli/api-settings.ts +12 -20
  349. package/src/cli/api-writes.ts +11 -3
  350. package/src/cli/cli-arguments.ts +57 -92
  351. package/src/cli/cli-dependencies.ts +7 -2
  352. package/src/cli/cli-errors.ts +23 -0
  353. package/src/cli/cli-flag-names.ts +99 -0
  354. package/src/cli/cli-output.ts +31 -16
  355. package/src/cli/main.ts +12 -5
  356. package/src/cli/redocly-apis.ts +56 -20
  357. package/src/cli/ref-limits.ts +21 -2
  358. package/src/cli/run-cli.ts +39 -7
  359. package/src/cli/written-flags.ts +131 -0
  360. package/src/domain/cause-secrets.ts +59 -25
  361. package/src/domain/errors.ts +3 -0
  362. package/src/domain/location-text.ts +27 -0
  363. package/src/domain/openapi/document-places.ts +78 -0
  364. package/src/domain/output-fixes.ts +9 -2
  365. package/src/domain/terminal-text.ts +44 -0
  366. package/src/domain/warnings.ts +1 -0
@@ -109,12 +109,40 @@ function fetchWithBodyLimit(fetchImpl: RemoteFetch, bodyLimit: RemoteBodyLimit):
109
109
  return async (input, init) => bufferedResponse(await fetchImpl(input, init), input, bodyLimit);
110
110
  }
111
111
 
112
+ /**
113
+ * The URL each remote document was read from in the end, when redirects moved it. A relative
114
+ * `$ref` resolves against the URL the document was retrieved from (RFC 3986, section 5.1.3), so
115
+ * `/latest` that redirects to `/static/v1/root.yaml` loads `./pet.yaml` from `/static/v1/`.
116
+ * Redocly reads only the body of the response, so the fetch records the URL here.
117
+ */
118
+ export class RedirectedUrls {
119
+ readonly #finalUrls = new Map<string, string>();
120
+
121
+ record(requested: string, final: string): void {
122
+ if (requested !== final) {
123
+ this.#finalUrls.set(requested, final);
124
+ }
125
+ }
126
+
127
+ /** The URL the document of `requested` came from; `requested` when it was not redirected. */
128
+ finalUrlOf(requested: string): string {
129
+ return this.#finalUrls.get(requested) ?? requested;
130
+ }
131
+ }
132
+
112
133
  /** What every hop of a remote fetch is checked against. */
113
134
  export interface RedirectPolicy {
114
135
  /** `resolve.http.headers` of the Redocly config; chosen again for the URL of every hop. */
115
136
  readonly headers: readonly ConfiguredHeader[];
116
137
  /** `--remote-ref-hosts`; undefined allows every host. */
117
138
  readonly hosts: readonly string[] | undefined;
139
+ /**
140
+ * The URL of the root document, if it is remote. The developer chose it, so neither it nor its
141
+ * redirects are limited to the hosts (ADR-017): a spec URL that redirects to a CDN loads.
142
+ */
143
+ readonly rootUrl: string | undefined;
144
+ /** Where the final URL of each redirected document is recorded. */
145
+ readonly redirects: RedirectedUrls;
118
146
  }
119
147
 
120
148
  /**
@@ -130,18 +158,20 @@ export function fetchWithCheckedRedirects(
130
158
  policy: RedirectPolicy,
131
159
  ): RemoteFetch {
132
160
  return async (input, init = {}) => {
161
+ const hosts = input === policy.rootUrl ? undefined : policy.hosts;
133
162
  let url = new URL(input);
134
163
  for (let hop = 0; hop <= MAX_REDIRECTS; hop += 1) {
135
- if (hop > 0 && policy.hosts !== undefined && !isHostAllowed(url, policy.hosts)) {
164
+ if (hop > 0 && hosts !== undefined && !isHostAllowed(url, hosts)) {
136
165
  throw refForbiddenError(url.href, {
137
166
  reason: "host-not-allowed",
138
- allowedHosts: [...policy.hosts],
167
+ allowedHosts: [...hosts],
139
168
  });
140
169
  }
141
170
  const headers = await headersFor(url, policy.headers);
142
171
  const response = await fetchImpl(url.href, { ...init, headers, redirect: "manual" });
143
172
  const location = response.headers.get("location");
144
173
  if (!REDIRECT_STATUSES.has(response.status) || location === null) {
174
+ policy.redirects.record(input, url.href);
145
175
  return response;
146
176
  }
147
177
  url = new URL(location, url);
@@ -190,8 +220,11 @@ export interface RemoteFetchOptions {
190
220
  /** Time limit of one fetch and its body, unless the Redocly config has its own `customFetch`. */
191
221
  readonly timeoutMs: number;
192
222
  readonly access: RefAccess;
223
+ /** The root document's URL, whose redirects are not limited to the hosts. */
224
+ readonly rootUrl: string | undefined;
193
225
  readonly maxParallelFetches: number;
194
226
  readonly bodyLimits: RemoteBodyLimits;
227
+ readonly redirects: RedirectedUrls;
195
228
  }
196
229
 
197
230
  /**
@@ -216,6 +249,8 @@ export function resolveConfigWithAccess(
216
249
  const checked = fetchWithCheckedRedirects(base, {
217
250
  headers: resolve.http.headers,
218
251
  hosts: options.access.remoteHosts,
252
+ rootUrl: options.rootUrl,
253
+ redirects: options.redirects,
219
254
  });
220
255
  // The limit is outside the redirects, so all hops of one document share one slot.
221
256
  const customFetch = fetchWithConcurrencyLimit(checked, options.maxParallelFetches);
@@ -13,12 +13,52 @@ import { refForbiddenError } from "./schema-load-errors.js";
13
13
  /** The extensions that Redocly 1.34's `BaseResolver.parseDocument` parses as YAML or JSON. */
14
14
  const PARSED_EXTENSIONS: ReadonlySet<string> = new Set([".json", ".yml", ".yaml"]);
15
15
  const PARSED_MIME_TYPE = /(json|yaml|openapi)/;
16
+ /**
17
+ * The text formats that a `$ref` loads as text, as a `description` from a Markdown file. A URL
18
+ * keeps its document as text only when its extension or type names one of them: the last segment
19
+ * of an API's path often has a dot that is no file type (`/schemas/v1.0`, `/schemas/pet.v2`).
20
+ */
21
+ const TEXT_EXTENSIONS: ReadonlySet<string> = new Set([".md", ".markdown", ".txt", ".html", ".htm"]);
22
+ const TEXT_MIME_TYPE = /^text\/(markdown|x-markdown|html)\b/;
23
+ const REMOTE_URL_RE = /^https?:\/\//;
24
+ const PATH_SEPARATOR_RE = /[/\\]/;
25
+ const EXTENSION_START = ".";
26
+
27
+ /** The extension of the last segment of a path, with its dot; empty when it has none. */
28
+ function extensionOf(path: string): string {
29
+ const name = path.split(PATH_SEPARATOR_RE).at(-1) ?? "";
30
+ const start = name.lastIndexOf(EXTENSION_START);
31
+ return start === -1 ? "" : name.slice(start);
32
+ }
33
+
34
+ /**
35
+ * The path of a document: the path of a URL, without its query and fragment, or the file path.
36
+ * Redocly took the extension of the whole URL, so `pet.yaml?token=…` had none it knew.
37
+ */
38
+ function documentPath(absoluteRef: string): string {
39
+ return REMOTE_URL_RE.test(absoluteRef) && URL.canParse(absoluteRef)
40
+ ? new URL(absoluteRef).pathname
41
+ : absoluteRef;
42
+ }
16
43
 
17
- /** Whether Redocly parses this source, or keeps its text: the same rule as its own resolver. */
44
+ /**
45
+ * Whether a source is parsed as YAML or JSON, or kept as text (a Markdown `description`, say).
46
+ * It is parsed when it is the root, when its type or the extension of its path says JSON or YAML,
47
+ * and when it is a URL whose path and type name no text format: an API serves its documents at
48
+ * paths like `/schemas/pet` or `/schemas/v1.0`, often as `text/plain`, and Redocly kept those as
49
+ * text, which failed as a schema. A local file is kept as text unless its name says JSON or YAML,
50
+ * as Redocly keeps it.
51
+ */
18
52
  export function isParsedSource(source: Source, isRoot: boolean): boolean {
19
- const { absoluteRef, mimeType } = source;
20
- const extension = absoluteRef.slice(absoluteRef.lastIndexOf("."));
21
- return isRoot || PARSED_EXTENSIONS.has(extension) || PARSED_MIME_TYPE.test(mimeType ?? "");
53
+ const { absoluteRef, mimeType = "" } = source;
54
+ const extension = extensionOf(documentPath(absoluteRef));
55
+ const isTextUrl = TEXT_EXTENSIONS.has(extension) || TEXT_MIME_TYPE.test(mimeType);
56
+ return (
57
+ isRoot ||
58
+ PARSED_EXTENSIONS.has(extension) ||
59
+ PARSED_MIME_TYPE.test(mimeType) ||
60
+ (REMOTE_URL_RE.test(absoluteRef) && !isTextUrl)
61
+ );
22
62
  }
23
63
 
24
64
  /** The parser's event for a node it starts to build. */
@@ -4,7 +4,7 @@ import { fileURLToPath } from "node:url";
4
4
  import {
5
5
  type BaseResolver,
6
6
  type Document,
7
- makeDocumentFromString,
7
+ parseYaml,
8
8
  ResolveError,
9
9
  Source,
10
10
  YamlParseError,
@@ -74,12 +74,24 @@ function parseJsonText(text: string, absoluteRef: string): Document {
74
74
  }
75
75
  }
76
76
 
77
+ /**
78
+ * How a parser message names document text that is no file. The document itself is named as a
79
+ * file in the working directory, so that its relative `$ref`s resolve there, but no such file
80
+ * exists, and a message that named it sent the user looking for it.
81
+ */
82
+ const TEXT_ORIGIN_NAMES: Readonly<Record<TextOrigin, string>> = {
83
+ string: "the schema string",
84
+ stream: "the schema stream",
85
+ };
86
+
77
87
  function parseYamlText(text: string, absoluteRef: string, origin: TextOrigin): Document {
88
+ const source = new Source(absoluteRef, text);
78
89
  let document: Document;
79
90
  try {
80
- document = makeDocumentFromString(text, absoluteRef);
91
+ document = { source, parsed: parseYaml(text, { filename: TEXT_ORIGIN_NAMES[origin] }) };
81
92
  } catch (cause) {
82
- throw parseFailedError("yaml", absoluteRef, cause);
93
+ // As Redocly's own parse: the error keeps the first line, not the parser's code excerpt.
94
+ throw parseFailedError("yaml", absoluteRef, new YamlParseError(cause as Error, source));
83
95
  }
84
96
  // Text that is only a YAML string scalar, such as "./openapi.yaml", was meant as a file path.
85
97
  if (typeof document.parsed === "string") {
@@ -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
  }
@@ -0,0 +1,76 @@
1
+ import path from "node:path";
2
+ import { fileURLToPath, pathToFileURL } from "node:url";
3
+ import { hasUrlScheme } from "../domain/location-text.js";
4
+ import type { GeneratorOptions } from "../types.js";
5
+ import { processCwd } from "./default-ports.js";
6
+ import { kindOf, optionInvalid } from "./option-errors.js";
7
+
8
+ /**
9
+ * The `cwd` option: the directory that relative `$ref`s of an inline source resolve against
10
+ * (ADR-023). A string is read as `localRefRoot` is: a URL when it has a scheme, else a path.
11
+ */
12
+
13
+ const FILE_PROTOCOL = "file:";
14
+
15
+ /** A `file:` cwd is a local directory; an `http:` or `https:` one holds remote documents. */
16
+ const CWD_PROTOCOLS: ReadonlySet<string> = new Set([FILE_PROTOCOL, "http:", "https:"]);
17
+
18
+ const CWD_EXPECTED = "a directory path, or a file:, http: or https: URL";
19
+
20
+ /** A path, resolved against the working directory, as a directory URL; `#` and `%` are encoded. */
21
+ function pathDirectoryUrl(directory: string): URL {
22
+ return pathToFileURL(path.join(path.resolve(directory), path.sep));
23
+ }
24
+
25
+ /** The URL the value names, before its scheme is checked; undefined for another type. */
26
+ function cwdUrlOf(value: unknown): URL | undefined {
27
+ if (value === undefined) {
28
+ return pathDirectoryUrl(processCwd());
29
+ }
30
+ if (typeof value === "string") {
31
+ if (!hasUrlScheme(value)) {
32
+ return pathDirectoryUrl(value);
33
+ }
34
+ return URL.parse(value, pathDirectoryUrl(processCwd()).href) ?? undefined;
35
+ }
36
+ if (Buffer.isBuffer(value)) {
37
+ return pathDirectoryUrl(value.toString());
38
+ }
39
+ return value instanceof URL ? value : undefined;
40
+ }
41
+
42
+ /** A `file:` URL with a host, or with an encoded slash, names no path on this platform. */
43
+ function isLocalPath(url: URL): boolean {
44
+ try {
45
+ fileURLToPath(url);
46
+ return true;
47
+ } catch {
48
+ return false;
49
+ }
50
+ }
51
+
52
+ /** What the cwd is instead of a directory, for the error. */
53
+ function cwdKindOf(value: unknown, url: URL | undefined): string {
54
+ if (url?.protocol === FILE_PROTOCOL) {
55
+ return "a file: URL that names no local path";
56
+ }
57
+ return url === undefined && typeof value === "string"
58
+ ? "a string that is no valid URL"
59
+ : kindOf(url ?? value);
60
+ }
61
+
62
+ /**
63
+ * The cwd as a URL that ends in a slash. Fails with `OPTION_INVALID` for another type, a URL of
64
+ * another scheme or a `file:` URL that names no local path.
65
+ */
66
+ export function sourceCwdOf(options: GeneratorOptions): URL {
67
+ const url = cwdUrlOf(options.cwd);
68
+ const isUsable =
69
+ url !== undefined &&
70
+ CWD_PROTOCOLS.has(url.protocol) &&
71
+ (url.protocol !== FILE_PROTOCOL || isLocalPath(url));
72
+ if (!isUsable) {
73
+ throw optionInvalid("cwd", CWD_EXPECTED, cwdKindOf(options.cwd, url));
74
+ }
75
+ return url.pathname.endsWith("/") ? url : new URL(`${url.pathname}/`, url);
76
+ }
@@ -1,10 +1,11 @@
1
1
  import { performance } from "node:perf_hooks";
2
2
  import { createConfig, type Config as RedoclyConfig } from "@redocly/openapi-core";
3
- import supportsColor from "supports-color";
3
+ import supportsColor, { type ColorInfo } from "supports-color";
4
4
  import { createNodeDeprecationReporter } from "../adapters/node-deprecations/node-deprecations.js";
5
5
  import type { RemoteDocumentLimits } from "../adapters/redocly-loader/remote-fetch.js";
6
6
  import {
7
7
  createTerminalLogger,
8
+ type TerminalLoggerOptions,
8
9
  type TerminalSink,
9
10
  } from "../adapters/terminal-logger/terminal-logger.js";
10
11
  import type { DeprecationReporter } from "../application/ports/deprecation-reporter.js";
@@ -15,10 +16,24 @@ import type { Logger } from "../application/ports/logger.js";
15
16
  * composition root are where the process environment is read.
16
17
  */
17
18
 
18
- /** Colors when the terminal supports them, as the original decided. */
19
- export function terminalSupportsColor(): boolean {
20
- const stdout = supportsColor.stdout;
21
- return Boolean(stdout) && stdout !== false && stdout.hasBasic !== false;
19
+ /** The output streams of the process, which can each be a terminal or redirected. */
20
+ export type TerminalStream = "stdout" | "stderr";
21
+
22
+ /** The color support of each stream, as `supports-color` detects it. */
23
+ export type StreamColorSupport = Readonly<Record<TerminalStream, ColorInfo>>;
24
+
25
+ /**
26
+ * Whether `stream` shows colors. Each stream is asked for itself: with stdout on a terminal and
27
+ * stderr redirected to a log, the log gets no escape sequences.
28
+ */
29
+ export function streamSupportsColor(stream: TerminalStream, support: StreamColorSupport): boolean {
30
+ const info = support[stream];
31
+ return info !== false && info.hasBasic;
32
+ }
33
+
34
+ /** Colors when the terminal of `stream` supports them, as the original decided. */
35
+ export function terminalSupportsColor(stream: TerminalStream): boolean {
36
+ return streamSupportsColor(stream, supportsColor);
22
37
  }
23
38
 
24
39
  /**
@@ -34,14 +49,22 @@ const STDERR_SINK: TerminalSink = {
34
49
  error: (line) => console.error(line),
35
50
  };
36
51
 
37
- /** The terminal logger with `DEBUG` from the environment, writing to stderr. */
38
- export function defaultLogger(silent: boolean): Logger {
39
- return createTerminalLogger({
52
+ /** The options of the default logger: it writes to stderr, so the colors follow stderr. */
53
+ export function defaultLoggerOptions(
54
+ silent: boolean,
55
+ support: StreamColorSupport,
56
+ ): TerminalLoggerOptions {
57
+ return {
40
58
  silent,
41
59
  debugFilter: process.env.DEBUG,
42
- useColors: terminalSupportsColor(),
60
+ useColors: streamSupportsColor("stderr", support),
43
61
  sink: STDERR_SINK,
44
- });
62
+ };
63
+ }
64
+
65
+ /** The terminal logger with `DEBUG` from the environment, writing to stderr. */
66
+ export function defaultLogger(silent: boolean): Logger {
67
+ return createTerminalLogger(defaultLoggerOptions(silent, supportsColor));
45
68
  }
46
69
 
47
70
  /** Deprecations as Node.js `DeprecationWarning`s, each code once per process. */
@@ -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,8 @@
1
- import path from "node:path";
2
- import { fileURLToPath, pathToFileURL } from "node:url";
3
1
  import type { Config as RedoclyConfig } from "@redocly/openapi-core";
4
2
  import { sourceEmptyError } from "../adapters/redocly-loader/schema-load-errors.js";
5
3
  import type { DeprecationReporter } from "../application/ports/deprecation-reporter.js";
6
4
  import type { Logger } from "../application/ports/logger.js";
7
- import type { RefAccess, SchemaLoader } from "../application/ports/schema-loader.js";
5
+ import type { SchemaLoader } from "../application/ports/schema-loader.js";
8
6
  import { withStackOverflowReported } from "../application/stack-overflow.js";
9
7
  import {
10
8
  ineffectiveFixNotices,
@@ -14,11 +12,12 @@ import {
14
12
  } from "../domain/output-fixes.js";
15
13
  import { WARNING_CODES, warningText } from "../domain/warnings.js";
16
14
  import type { GeneratorOptions, OpenAPI3, SchemaSource } from "../types.js";
17
- import { processCwd } from "./default-ports.js";
15
+ import { sourceCwdOf } from "./cwd-option.js";
18
16
  import { warnDiscriminatorValueConflicts } from "./discriminator-conflict-warnings.js";
19
17
  import { findDiscriminators } from "./discriminator-scan.js";
20
18
  import { resolveDocumentRef } from "./document-refs.js";
21
19
  import { warnIdenticalPathTemplates } from "./path-template-warnings.js";
20
+ import { refAccessOf } from "./ref-limit-options.js";
22
21
  import type { RunContext } from "./run-context.js";
23
22
  import { warnUnsupportedFeatures } from "./unsupported-feature-warnings.js";
24
23
 
@@ -55,32 +54,6 @@ type ContextFlags = Omit<
55
54
  | "fix"
56
55
  >;
57
56
 
58
- /**
59
- * The directory that relative `$ref`s of an inline source resolve against, as a URL that ends in
60
- * a slash. A path is resolved and encoded (`#`, `%`, spaces); a URL without the slash gets one.
61
- */
62
- function sourceCwd(options: GeneratorOptions): URL {
63
- const { cwd } = options;
64
- if (cwd instanceof URL) {
65
- return cwd.pathname.endsWith("/") ? cwd : new URL(`${cwd.pathname}/`, cwd);
66
- }
67
- const directory = path.resolve(cwd === undefined ? processCwd() : cwd.toString());
68
- return pathToFileURL(path.join(directory, path.sep));
69
- }
70
-
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
57
  function callbackOptions(options: GeneratorOptions) {
85
58
  return {
86
59
  irCallbacks: options.irCallbacks === true,
@@ -126,7 +99,9 @@ function contextFlags(options: GeneratorOptions): ContextFlags {
126
99
  */
127
100
  function outputFixesOf(options: GeneratorOptions, logger: Logger): readonly OutputFix[] {
128
101
  const names = outputFixNamesOf(options.fix ?? []);
129
- for (const notice of ineffectiveFixNotices(names, (option) => options[option] === true)) {
102
+ // The options with their defaults: `defaultNonNullable` is on unless set to false.
103
+ const flags = contextFlags(options);
104
+ for (const notice of ineffectiveFixNotices(names, (option) => flags[option])) {
130
105
  logger.warn(warningText(notice, WARNING_CODES.FIX_NEEDS_OPTION));
131
106
  }
132
107
  return [...resolveOutputFixes(names)];
@@ -156,7 +131,7 @@ export async function createDocumentRun(
156
131
  const { logger } = ports;
157
132
  const fix = outputFixesOf(options, logger);
158
133
  const silent = options.silent ?? false;
159
- const cwd = sourceCwd(options);
134
+ const cwd = sourceCwdOf(options);
160
135
  const { document: schema, localFiles } = await ports.loader.load({
161
136
  source,
162
137
  cwd,
@@ -15,7 +15,7 @@ import { type ResolveRefOptions, resolveDocumentRef, walkDocument } from "./docu
15
15
 
16
16
  // The exported `c` is the shared ansi-colors instance, switched off at import when the terminal has
17
17
  // no colors, as the original did. The generator's own logger uses a separate instance.
18
- if (!terminalSupportsColor()) {
18
+ if (!terminalSupportsColor("stdout")) {
19
19
  colors.enabled = false;
20
20
  }
21
21
 
@@ -1,7 +1,8 @@
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
- import type { GeneratorOptions, SchemaSource } from "../types.js";
5
+ import type { GeneratorOptions, OpenAPITSOptions, SchemaSource } from "../types.js";
5
6
  import {
6
7
  DEFAULT_REMOTE_DOCUMENT_LIMITS,
7
8
  defaultDeprecationReporter,
@@ -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,
@@ -75,6 +78,19 @@ export async function defaultGeneratorPorts(options: GeneratorOptions): Promise<
75
78
  * - URL: URL to a YAML or JSON file (local or remote)
76
79
  * - Readable: Readable stream of YAML or JSON
77
80
  */
81
+ export default function openapiTS(
82
+ source: SchemaSource,
83
+ options?: OpenAPITSOptions,
84
+ ): Promise<IrDeclaration[]>;
85
+ // The options without `irCallbacks` get their own signature: TypeScript does not pick a member of
86
+ // `GeneratorOptions | undefined` (an optional parameter) by a key that the object leaves out, so
87
+ // inline callbacks had implicit `any` parameters (TS7006). `irCallbacks: true` picks the member
88
+ // here. This signature is last, so `Parameters<typeof openapiTS>` reads it.
89
+ /** Convert an OpenAPI schema to IR declarations, with either form of the options. */
90
+ export default function openapiTS(
91
+ source: SchemaSource,
92
+ options?: GeneratorOptions,
93
+ ): Promise<IrDeclaration[]>;
78
94
  export default async function openapiTS(
79
95
  source: SchemaSource,
80
96
  options: GeneratorOptions = {},
@@ -0,0 +1,22 @@
1
+ import { ERROR_CODES, OpenAPITypesafeError } from "../domain/errors.js";
2
+
3
+ /** The `OPTION_INVALID` errors of the Node API's options, checked before anything loads. */
4
+
5
+ /** What a value is, for the error; a URL is named by its protocol only, as it may hold secrets. */
6
+ export function kindOf(value: unknown): string {
7
+ if (value instanceof URL) {
8
+ return `a ${value.protocol} URL`;
9
+ }
10
+ if (Array.isArray(value)) {
11
+ return "a list";
12
+ }
13
+ return value === null ? "null" : `a ${typeof value}`;
14
+ }
15
+
16
+ export function optionInvalid(option: string, expected: string, got: string): OpenAPITypesafeError {
17
+ return new OpenAPITypesafeError({
18
+ code: ERROR_CODES.OPTION_INVALID,
19
+ message: `The ${option} option must be ${expected}; got ${got}.`,
20
+ details: { option },
21
+ });
22
+ }
@@ -0,0 +1,69 @@
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 { hasUrlScheme } from "../domain/location-text.js";
5
+ import type { GeneratorOptions } from "../types.js";
6
+ import { kindOf, optionInvalid } from "./option-errors.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
+ const LOCAL_ROOT_EXPECTED = "a directory path or a file: URL";
17
+ const RELATIVE_ROOT_EXPECTED = "an absolute path or a file: URL when cwd is no file: URL";
18
+
19
+ /**
20
+ * A string as the CLI reads `--local-ref-root`: a URL when it has a scheme (`file:///specs/`),
21
+ * else a path that resolves against the cwd. Undefined when it does not parse as a URL. A
22
+ * relative path needs a local cwd: with an `http:` cwd it names no local directory.
23
+ */
24
+ function localRootUrlOf(value: string, cwd: URL): URL | undefined {
25
+ if (hasUrlScheme(value)) {
26
+ return URL.parse(value, cwd.href) ?? undefined;
27
+ }
28
+ if (path.isAbsolute(value)) {
29
+ return pathToFileURL(value);
30
+ }
31
+ if (cwd.protocol !== FILE_PROTOCOL) {
32
+ throw optionInvalid(
33
+ "localRefRoot",
34
+ RELATIVE_ROOT_EXPECTED,
35
+ `a relative path, and cwd is ${kindOf(cwd)}`,
36
+ );
37
+ }
38
+ return pathToFileURL(path.resolve(fileURLToPath(cwd), value));
39
+ }
40
+
41
+ /** The local root as a `file:` URL; the option is set, so it is not undefined. */
42
+ function localRootOf(value: unknown, cwd: URL): URL {
43
+ const root = typeof value === "string" ? localRootUrlOf(value, cwd) : value;
44
+ if (root instanceof URL && root.protocol === FILE_PROTOCOL) {
45
+ return root;
46
+ }
47
+ const got = root === undefined ? "a string that is no valid URL" : kindOf(root);
48
+ throw optionInvalid("localRefRoot", LOCAL_ROOT_EXPECTED, got);
49
+ }
50
+
51
+ function remoteHostsOf(value: unknown): readonly string[] {
52
+ if (!Array.isArray(value)) {
53
+ throw optionInvalid("remoteRefHosts", "a list of hosts", kindOf(value));
54
+ }
55
+ const other = value.find((entry) => typeof entry !== "string");
56
+ if (other !== undefined) {
57
+ throw optionInvalid("remoteRefHosts", "a list of hosts", `a list with ${kindOf(other)}`);
58
+ }
59
+ return value;
60
+ }
61
+
62
+ /** The limits of the options; fails with `OPTION_INVALID` for a value of another type. */
63
+ export function refAccessOf(options: GeneratorOptions, cwd: URL): RefAccess {
64
+ const { localRefRoot, remoteRefHosts } = options;
65
+ return {
66
+ ...(localRefRoot === undefined ? {} : { localRoot: localRootOf(localRefRoot, cwd) }),
67
+ ...(remoteRefHosts === undefined ? {} : { remoteHosts: remoteHostsOf(remoteRefHosts) }),
68
+ };
69
+ }