openapi-typesafe 1.0.0-rc.3 → 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 (596) hide show
  1. package/CHANGELOG.md +1972 -0
  2. package/dist/adapters/node-file-system/file-claims.cjs +105 -0
  3. package/dist/adapters/node-file-system/file-claims.cjs.map +1 -0
  4. package/dist/adapters/node-file-system/file-claims.mjs +95 -0
  5. package/dist/adapters/node-file-system/file-claims.mjs.map +1 -0
  6. package/dist/adapters/node-file-system/file-identity.cjs +82 -0
  7. package/dist/adapters/node-file-system/file-identity.cjs.map +1 -0
  8. package/dist/adapters/node-file-system/file-identity.mjs +74 -0
  9. package/dist/adapters/node-file-system/file-identity.mjs.map +1 -0
  10. package/dist/adapters/node-file-system/node-file-system.cjs +97 -14
  11. package/dist/adapters/node-file-system/node-file-system.cjs.map +1 -1
  12. package/dist/adapters/node-file-system/node-file-system.mjs +97 -14
  13. package/dist/adapters/node-file-system/node-file-system.mjs.map +1 -1
  14. package/dist/adapters/pipe-safe-writer/pipe-safe-writer.cjs +19 -0
  15. package/dist/adapters/pipe-safe-writer/pipe-safe-writer.cjs.map +1 -0
  16. package/dist/adapters/pipe-safe-writer/pipe-safe-writer.mjs +17 -0
  17. package/dist/adapters/pipe-safe-writer/pipe-safe-writer.mjs.map +1 -0
  18. package/dist/adapters/redocly-loader/bundled-mapping-copies.cjs +48 -0
  19. package/dist/adapters/redocly-loader/bundled-mapping-copies.cjs.map +1 -0
  20. package/dist/adapters/redocly-loader/bundled-mapping-copies.mjs +45 -0
  21. package/dist/adapters/redocly-loader/bundled-mapping-copies.mjs.map +1 -0
  22. package/dist/adapters/redocly-loader/bundled-ref-repair.cjs +1 -1
  23. package/dist/adapters/redocly-loader/bundled-ref-repair.cjs.map +1 -1
  24. package/dist/adapters/redocly-loader/bundled-ref-repair.mjs +2 -2
  25. package/dist/adapters/redocly-loader/bundled-ref-repair.mjs.map +1 -1
  26. package/dist/adapters/redocly-loader/component-schemas.cjs +17 -0
  27. package/dist/adapters/redocly-loader/component-schemas.cjs.map +1 -0
  28. package/dist/adapters/redocly-loader/component-schemas.mjs +14 -0
  29. package/dist/adapters/redocly-loader/component-schemas.mjs.map +1 -0
  30. package/dist/adapters/redocly-loader/loaded-documents.cjs +28 -0
  31. package/dist/adapters/redocly-loader/loaded-documents.cjs.map +1 -0
  32. package/dist/adapters/redocly-loader/loaded-documents.mjs +21 -0
  33. package/dist/adapters/redocly-loader/loaded-documents.mjs.map +1 -0
  34. package/dist/adapters/redocly-loader/mapping-schema-names.cjs +22 -0
  35. package/dist/adapters/redocly-loader/mapping-schema-names.cjs.map +1 -0
  36. package/dist/adapters/redocly-loader/mapping-schema-names.mjs +20 -0
  37. package/dist/adapters/redocly-loader/mapping-schema-names.mjs.map +1 -0
  38. package/dist/adapters/redocly-loader/query-named-components.cjs +137 -0
  39. package/dist/adapters/redocly-loader/query-named-components.cjs.map +1 -0
  40. package/dist/adapters/redocly-loader/query-named-components.mjs +134 -0
  41. package/dist/adapters/redocly-loader/query-named-components.mjs.map +1 -0
  42. package/dist/adapters/redocly-loader/redocly-schema-loader.cjs +40 -29
  43. package/dist/adapters/redocly-loader/redocly-schema-loader.cjs.map +1 -1
  44. package/dist/adapters/redocly-loader/redocly-schema-loader.mjs +38 -27
  45. package/dist/adapters/redocly-loader/redocly-schema-loader.mjs.map +1 -1
  46. package/dist/adapters/redocly-loader/ref-access.cjs +45 -4
  47. package/dist/adapters/redocly-loader/ref-access.cjs.map +1 -1
  48. package/dist/adapters/redocly-loader/ref-access.mjs +45 -4
  49. package/dist/adapters/redocly-loader/ref-access.mjs.map +1 -1
  50. package/dist/adapters/redocly-loader/ref-holders.cjs +52 -10
  51. package/dist/adapters/redocly-loader/ref-holders.cjs.map +1 -1
  52. package/dist/adapters/redocly-loader/ref-holders.mjs +53 -12
  53. package/dist/adapters/redocly-loader/ref-holders.mjs.map +1 -1
  54. package/dist/adapters/redocly-loader/remote-body-limit.cjs +50 -0
  55. package/dist/adapters/redocly-loader/remote-body-limit.cjs.map +1 -0
  56. package/dist/adapters/redocly-loader/remote-body-limit.mjs +48 -0
  57. package/dist/adapters/redocly-loader/remote-body-limit.mjs.map +1 -0
  58. package/dist/adapters/redocly-loader/remote-fetch.cjs +40 -9
  59. package/dist/adapters/redocly-loader/remote-fetch.cjs.map +1 -1
  60. package/dist/adapters/redocly-loader/remote-fetch.mjs +40 -9
  61. package/dist/adapters/redocly-loader/remote-fetch.mjs.map +1 -1
  62. package/dist/adapters/redocly-loader/remote-node-limit.cjs +61 -0
  63. package/dist/adapters/redocly-loader/remote-node-limit.cjs.map +1 -0
  64. package/dist/adapters/redocly-loader/remote-node-limit.mjs +58 -0
  65. package/dist/adapters/redocly-loader/remote-node-limit.mjs.map +1 -0
  66. package/dist/adapters/redocly-loader/schema-load-errors.cjs +32 -8
  67. package/dist/adapters/redocly-loader/schema-load-errors.cjs.map +1 -1
  68. package/dist/adapters/redocly-loader/schema-load-errors.mjs +32 -9
  69. package/dist/adapters/redocly-loader/schema-load-errors.mjs.map +1 -1
  70. package/dist/adapters/redocly-loader/schema-source.cjs +18 -9
  71. package/dist/adapters/redocly-loader/schema-source.cjs.map +1 -1
  72. package/dist/adapters/redocly-loader/schema-source.mjs +20 -11
  73. package/dist/adapters/redocly-loader/schema-source.mjs.map +1 -1
  74. package/dist/adapters/redocly-loader/unsupported-keys.cjs +3 -5
  75. package/dist/adapters/redocly-loader/unsupported-keys.cjs.map +1 -1
  76. package/dist/adapters/redocly-loader/unsupported-keys.mjs +3 -5
  77. package/dist/adapters/redocly-loader/unsupported-keys.mjs.map +1 -1
  78. package/dist/adapters/terminal-logger/terminal-logger.cjs +10 -4
  79. package/dist/adapters/terminal-logger/terminal-logger.cjs.map +1 -1
  80. package/dist/adapters/terminal-logger/terminal-logger.mjs +10 -4
  81. package/dist/adapters/terminal-logger/terminal-logger.mjs.map +1 -1
  82. package/dist/api/default-ports.cjs +14 -2
  83. package/dist/api/default-ports.cjs.map +1 -1
  84. package/dist/api/default-ports.mjs +14 -2
  85. package/dist/api/default-ports.mjs.map +1 -1
  86. package/dist/api/discriminator-enum-parent.cjs +2 -2
  87. package/dist/api/discriminator-enum-parent.cjs.map +1 -1
  88. package/dist/api/discriminator-enum-parent.mjs +3 -3
  89. package/dist/api/discriminator-enum-parent.mjs.map +1 -1
  90. package/dist/api/discriminator-other-owners.cjs +5 -3
  91. package/dist/api/discriminator-other-owners.cjs.map +1 -1
  92. package/dist/api/discriminator-other-owners.mjs +5 -3
  93. package/dist/api/discriminator-other-owners.mjs.map +1 -1
  94. package/dist/api/discriminator-patch.cjs +5 -1
  95. package/dist/api/discriminator-patch.cjs.map +1 -1
  96. package/dist/api/discriminator-patch.mjs +5 -1
  97. package/dist/api/discriminator-patch.mjs.map +1 -1
  98. package/dist/api/discriminator-scan.cjs +15 -8
  99. package/dist/api/discriminator-scan.cjs.map +1 -1
  100. package/dist/api/discriminator-scan.mjs +15 -8
  101. package/dist/api/discriminator-scan.mjs.map +1 -1
  102. package/dist/api/document-refs.cjs +16 -1
  103. package/dist/api/document-refs.cjs.map +1 -1
  104. package/dist/api/document-refs.mjs +16 -1
  105. package/dist/api/document-refs.mjs.map +1 -1
  106. package/dist/api/document-run.cjs +6 -12
  107. package/dist/api/document-run.cjs.map +1 -1
  108. package/dist/api/document-run.mjs +6 -12
  109. package/dist/api/document-run.mjs.map +1 -1
  110. package/dist/api/legacy-utils.cjs.map +1 -1
  111. package/dist/api/legacy-utils.mjs.map +1 -1
  112. package/dist/api/openapi-ts.cjs +9 -6
  113. package/dist/api/openapi-ts.cjs.map +1 -1
  114. package/dist/api/openapi-ts.mjs +10 -8
  115. package/dist/api/openapi-ts.mjs.map +1 -1
  116. package/dist/api/ref-limit-options.cjs +63 -0
  117. package/dist/api/ref-limit-options.cjs.map +1 -0
  118. package/dist/api/ref-limit-options.mjs +57 -0
  119. package/dist/api/ref-limit-options.mjs.map +1 -0
  120. package/dist/api/schema-callbacks.cjs +36 -12
  121. package/dist/api/schema-callbacks.cjs.map +1 -1
  122. package/dist/api/schema-callbacks.mjs +36 -12
  123. package/dist/api/schema-callbacks.mjs.map +1 -1
  124. package/dist/api/transform-context.cjs +7 -3
  125. package/dist/api/transform-context.cjs.map +1 -1
  126. package/dist/api/transform-context.mjs +7 -3
  127. package/dist/api/transform-context.mjs.map +1 -1
  128. package/dist/api/transform-schema.cjs +47 -10
  129. package/dist/api/transform-schema.cjs.map +1 -1
  130. package/dist/api/transform-schema.mjs +48 -11
  131. package/dist/api/transform-schema.mjs.map +1 -1
  132. package/dist/application/print/text-writer.cjs +10 -3
  133. package/dist/application/print/text-writer.cjs.map +1 -1
  134. package/dist/application/print/text-writer.mjs +10 -3
  135. package/dist/application/print/text-writer.mjs.map +1 -1
  136. package/dist/application/stack-overflow.cjs +2 -1
  137. package/dist/application/stack-overflow.cjs.map +1 -1
  138. package/dist/application/stack-overflow.mjs +2 -1
  139. package/dist/application/stack-overflow.mjs.map +1 -1
  140. package/dist/application/transform/declared-value-types.cjs +36 -0
  141. package/dist/application/transform/declared-value-types.cjs.map +1 -0
  142. package/dist/application/transform/declared-value-types.mjs +34 -0
  143. package/dist/application/transform/declared-value-types.mjs.map +1 -0
  144. package/dist/application/transform/document/components.cjs +1 -1
  145. package/dist/application/transform/document/components.cjs.map +1 -1
  146. package/dist/application/transform/document/components.mjs +2 -2
  147. package/dist/application/transform/document/components.mjs.map +1 -1
  148. package/dist/application/transform/document/declared-type-names.cjs +23 -0
  149. package/dist/application/transform/document/declared-type-names.cjs.map +1 -0
  150. package/dist/application/transform/document/declared-type-names.mjs +20 -0
  151. package/dist/application/transform/document/declared-type-names.mjs.map +1 -0
  152. package/dist/application/transform/document/defs-root.cjs +1 -1
  153. package/dist/application/transform/document/defs-root.cjs.map +1 -1
  154. package/dist/application/transform/document/defs-root.mjs +2 -2
  155. package/dist/application/transform/document/defs-root.mjs.map +1 -1
  156. package/dist/application/transform/document/document.cjs +7 -2
  157. package/dist/application/transform/document/document.cjs.map +1 -1
  158. package/dist/application/transform/document/document.mjs +6 -3
  159. package/dist/application/transform/document/document.mjs.map +1 -1
  160. package/dist/application/transform/document/enum-names-given-back.cjs +62 -0
  161. package/dist/application/transform/document/enum-names-given-back.cjs.map +1 -0
  162. package/dist/application/transform/document/enum-names-given-back.mjs +58 -0
  163. package/dist/application/transform/document/enum-names-given-back.mjs.map +1 -0
  164. package/dist/application/transform/document/exploded-query-values.cjs +63 -0
  165. package/dist/application/transform/document/exploded-query-values.cjs.map +1 -0
  166. package/dist/application/transform/document/exploded-query-values.mjs +59 -0
  167. package/dist/application/transform/document/exploded-query-values.mjs.map +1 -0
  168. package/dist/application/transform/document/global-type-references.cjs +1 -0
  169. package/dist/application/transform/document/global-type-references.cjs.map +1 -1
  170. package/dist/application/transform/document/global-type-references.mjs +1 -1
  171. package/dist/application/transform/document/global-type-references.mjs.map +1 -1
  172. package/dist/application/transform/document/header.cjs +11 -3
  173. package/dist/application/transform/document/header.cjs.map +1 -1
  174. package/dist/application/transform/document/header.mjs +10 -2
  175. package/dist/application/transform/document/header.mjs.map +1 -1
  176. package/dist/application/transform/document/media-type.cjs +2 -2
  177. package/dist/application/transform/document/media-type.cjs.map +1 -1
  178. package/dist/application/transform/document/media-type.mjs +3 -3
  179. package/dist/application/transform/document/media-type.mjs.map +1 -1
  180. package/dist/application/transform/document/operation-keys-written.cjs +26 -0
  181. package/dist/application/transform/document/operation-keys-written.cjs.map +1 -0
  182. package/dist/application/transform/document/operation-keys-written.mjs +24 -0
  183. package/dist/application/transform/document/operation-keys-written.mjs.map +1 -0
  184. package/dist/application/transform/document/operation.cjs +1 -1
  185. package/dist/application/transform/document/operation.cjs.map +1 -1
  186. package/dist/application/transform/document/operation.mjs +2 -2
  187. package/dist/application/transform/document/operation.mjs.map +1 -1
  188. package/dist/application/transform/document/parameter-entries.cjs +34 -0
  189. package/dist/application/transform/document/parameter-entries.cjs.map +1 -0
  190. package/dist/application/transform/document/parameter-entries.mjs +30 -0
  191. package/dist/application/transform/document/parameter-entries.mjs.map +1 -0
  192. package/dist/application/transform/document/parameter.cjs +12 -4
  193. package/dist/application/transform/document/parameter.cjs.map +1 -1
  194. package/dist/application/transform/document/parameter.mjs +13 -5
  195. package/dist/application/transform/document/parameter.mjs.map +1 -1
  196. package/dist/application/transform/document/parameters.cjs +29 -34
  197. package/dist/application/transform/document/parameters.cjs.map +1 -1
  198. package/dist/application/transform/document/parameters.mjs +29 -34
  199. package/dist/application/transform/document/parameters.mjs.map +1 -1
  200. package/dist/application/transform/document/path-item.cjs +15 -17
  201. package/dist/application/transform/document/path-item.cjs.map +1 -1
  202. package/dist/application/transform/document/path-item.mjs +15 -19
  203. package/dist/application/transform/document/path-item.mjs.map +1 -1
  204. package/dist/application/transform/document/path-template-key.cjs +84 -0
  205. package/dist/application/transform/document/path-template-key.cjs.map +1 -0
  206. package/dist/application/transform/document/path-template-key.mjs +79 -0
  207. package/dist/application/transform/document/path-template-key.mjs.map +1 -0
  208. package/dist/application/transform/document/path-template-refs.cjs +70 -0
  209. package/dist/application/transform/document/path-template-refs.cjs.map +1 -0
  210. package/dist/application/transform/document/path-template-refs.mjs +67 -0
  211. package/dist/application/transform/document/path-template-refs.mjs.map +1 -0
  212. package/dist/application/transform/document/paths-enum.cjs +8 -6
  213. package/dist/application/transform/document/paths-enum.cjs.map +1 -1
  214. package/dist/application/transform/document/paths-enum.mjs +8 -7
  215. package/dist/application/transform/document/paths-enum.mjs.map +1 -1
  216. package/dist/application/transform/document/paths.cjs +43 -67
  217. package/dist/application/transform/document/paths.cjs.map +1 -1
  218. package/dist/application/transform/document/paths.mjs +42 -68
  219. package/dist/application/transform/document/paths.mjs.map +1 -1
  220. package/dist/application/transform/document/referenced-parameter.cjs +80 -0
  221. package/dist/application/transform/document/referenced-parameter.cjs.map +1 -0
  222. package/dist/application/transform/document/referenced-parameter.mjs +78 -0
  223. package/dist/application/transform/document/referenced-parameter.mjs.map +1 -0
  224. package/dist/application/transform/document/request-body.cjs +1 -1
  225. package/dist/application/transform/document/request-body.cjs.map +1 -1
  226. package/dist/application/transform/document/request-body.mjs +2 -2
  227. package/dist/application/transform/document/request-body.mjs.map +1 -1
  228. package/dist/application/transform/document/response.cjs +2 -2
  229. package/dist/application/transform/document/response.cjs.map +1 -1
  230. package/dist/application/transform/document/response.mjs +3 -3
  231. package/dist/application/transform/document/response.mjs.map +1 -1
  232. package/dist/application/transform/document/responses.cjs +1 -1
  233. package/dist/application/transform/document/responses.cjs.map +1 -1
  234. package/dist/application/transform/document/responses.mjs +2 -2
  235. package/dist/application/transform/document/responses.mjs.map +1 -1
  236. package/dist/application/transform/document/root-type-aliases.cjs +8 -23
  237. package/dist/application/transform/document/root-type-aliases.cjs.map +1 -1
  238. package/dist/application/transform/document/root-type-aliases.mjs +8 -24
  239. package/dist/application/transform/document/root-type-aliases.mjs.map +1 -1
  240. package/dist/application/transform/document/type-resolution.cjs +3 -1
  241. package/dist/application/transform/document/type-resolution.cjs.map +1 -1
  242. package/dist/application/transform/document/type-resolution.mjs +3 -1
  243. package/dist/application/transform/document/type-resolution.mjs.map +1 -1
  244. package/dist/application/transform/document/webhooks.cjs +10 -3
  245. package/dist/application/transform/document/webhooks.cjs.map +1 -1
  246. package/dist/application/transform/document/webhooks.mjs +10 -3
  247. package/dist/application/transform/document/webhooks.mjs.map +1 -1
  248. package/dist/application/transform/footer/document-footer.cjs +16 -5
  249. package/dist/application/transform/footer/document-footer.cjs.map +1 -1
  250. package/dist/application/transform/footer/document-footer.mjs +16 -5
  251. package/dist/application/transform/footer/document-footer.mjs.map +1 -1
  252. package/dist/application/transform/footer/enum-name-claims.cjs +48 -0
  253. package/dist/application/transform/footer/enum-name-claims.cjs.map +1 -0
  254. package/dist/application/transform/footer/enum-name-claims.mjs +46 -0
  255. package/dist/application/transform/footer/enum-name-claims.mjs.map +1 -0
  256. package/dist/application/transform/footer/operation-keys.cjs +23 -0
  257. package/dist/application/transform/footer/operation-keys.cjs.map +1 -0
  258. package/dist/application/transform/footer/operation-keys.mjs +21 -0
  259. package/dist/application/transform/footer/operation-keys.mjs.map +1 -0
  260. package/dist/application/transform/footer/type-evaluation.cjs +4 -4
  261. package/dist/application/transform/footer/type-evaluation.cjs.map +1 -1
  262. package/dist/application/transform/footer/type-evaluation.mjs +4 -4
  263. package/dist/application/transform/footer/type-evaluation.mjs.map +1 -1
  264. package/dist/application/transform/helper-types.cjs +3 -3
  265. package/dist/application/transform/helper-types.cjs.map +1 -1
  266. package/dist/application/transform/helper-types.mjs +3 -3
  267. package/dist/application/transform/helper-types.mjs.map +1 -1
  268. package/dist/application/transform/numbered-names.cjs +9 -0
  269. package/dist/application/transform/numbered-names.cjs.map +1 -1
  270. package/dist/application/transform/numbered-names.mjs +9 -1
  271. package/dist/application/transform/numbered-names.mjs.map +1 -1
  272. package/dist/application/transform/ref-pointer/generated-location.cjs +5 -5
  273. package/dist/application/transform/ref-pointer/generated-location.cjs.map +1 -1
  274. package/dist/application/transform/ref-pointer/generated-location.mjs +3 -3
  275. package/dist/application/transform/ref-pointer/generated-location.mjs.map +1 -1
  276. package/dist/application/transform/ref-pointer/generated-shape.cjs +47 -66
  277. package/dist/application/transform/ref-pointer/generated-shape.cjs.map +1 -1
  278. package/dist/application/transform/ref-pointer/generated-shape.mjs +36 -50
  279. package/dist/application/transform/ref-pointer/generated-shape.mjs.map +1 -1
  280. package/dist/application/transform/ref-pointer/inline-cycles.cjs +2 -2
  281. package/dist/application/transform/ref-pointer/inline-cycles.cjs.map +1 -1
  282. package/dist/application/transform/ref-pointer/inline-cycles.mjs +1 -1
  283. package/dist/application/transform/ref-pointer/inline-cycles.mjs.map +1 -1
  284. package/dist/application/transform/ref-pointer/located-node.cjs +23 -0
  285. package/dist/application/transform/ref-pointer/located-node.cjs.map +1 -0
  286. package/dist/application/transform/ref-pointer/located-node.mjs +18 -0
  287. package/dist/application/transform/ref-pointer/located-node.mjs.map +1 -0
  288. package/dist/application/transform/ref-pointer/original-index.cjs +68 -13
  289. package/dist/application/transform/ref-pointer/original-index.cjs.map +1 -1
  290. package/dist/application/transform/ref-pointer/original-index.mjs +70 -15
  291. package/dist/application/transform/ref-pointer/original-index.mjs.map +1 -1
  292. package/dist/application/transform/ref-pointer/pointer-type.cjs +2 -2
  293. package/dist/application/transform/ref-pointer/pointer-type.cjs.map +1 -1
  294. package/dist/application/transform/ref-pointer/pointer-type.mjs +1 -1
  295. package/dist/application/transform/ref-pointer/pointer-type.mjs.map +1 -1
  296. package/dist/application/transform/ref-pointer/pointer-walk.cjs +54 -13
  297. package/dist/application/transform/ref-pointer/pointer-walk.cjs.map +1 -1
  298. package/dist/application/transform/ref-pointer/pointer-walk.mjs +46 -7
  299. package/dist/application/transform/ref-pointer/pointer-walk.mjs.map +1 -1
  300. package/dist/application/transform/ref-pointer/property-shape.cjs +52 -0
  301. package/dist/application/transform/ref-pointer/property-shape.cjs.map +1 -0
  302. package/dist/application/transform/ref-pointer/property-shape.mjs +45 -0
  303. package/dist/application/transform/ref-pointer/property-shape.mjs.map +1 -0
  304. package/dist/application/transform/ref-pointer/typed-walk.cjs +79 -21
  305. package/dist/application/transform/ref-pointer/typed-walk.cjs.map +1 -1
  306. package/dist/application/transform/ref-pointer/typed-walk.mjs +76 -18
  307. package/dist/application/transform/ref-pointer/typed-walk.mjs.map +1 -1
  308. package/dist/application/transform/ref-type.cjs +2 -1
  309. package/dist/application/transform/ref-type.cjs.map +1 -1
  310. package/dist/application/transform/ref-type.mjs +2 -1
  311. package/dist/application/transform/ref-type.mjs.map +1 -1
  312. package/dist/application/transform/schema/access-flags.cjs.map +1 -1
  313. package/dist/application/transform/schema/access-flags.mjs.map +1 -1
  314. package/dist/application/transform/schema/composition-type.cjs +1 -1
  315. package/dist/application/transform/schema/composition-type.cjs.map +1 -1
  316. package/dist/application/transform/schema/composition-type.mjs +2 -2
  317. package/dist/application/transform/schema/composition-type.mjs.map +1 -1
  318. package/dist/application/transform/schema/enum-rule.cjs +1 -1
  319. package/dist/application/transform/schema/enum-rule.cjs.map +1 -1
  320. package/dist/application/transform/schema/enum-rule.mjs +2 -2
  321. package/dist/application/transform/schema/enum-rule.mjs.map +1 -1
  322. package/dist/application/transform/schema/index-signature-type.cjs +21 -0
  323. package/dist/application/transform/schema/index-signature-type.cjs.map +1 -0
  324. package/dist/application/transform/schema/index-signature-type.mjs +19 -0
  325. package/dist/application/transform/schema/index-signature-type.mjs.map +1 -0
  326. package/dist/application/transform/schema/input-location.cjs +23 -0
  327. package/dist/application/transform/schema/input-location.cjs.map +1 -0
  328. package/dist/application/transform/schema/input-location.mjs +21 -0
  329. package/dist/application/transform/schema/input-location.mjs.map +1 -0
  330. package/dist/application/transform/schema/object-type.cjs +18 -8
  331. package/dist/application/transform/schema/object-type.cjs.map +1 -1
  332. package/dist/application/transform/schema/object-type.mjs +19 -9
  333. package/dist/application/transform/schema/object-type.mjs.map +1 -1
  334. package/dist/application/transform/schema/ref-siblings.cjs +47 -0
  335. package/dist/application/transform/schema/ref-siblings.cjs.map +1 -0
  336. package/dist/application/transform/schema/ref-siblings.mjs +45 -0
  337. package/dist/application/transform/schema/ref-siblings.mjs.map +1 -0
  338. package/dist/application/transform/schema/schema-transform.cjs +6 -3
  339. package/dist/application/transform/schema/schema-transform.cjs.map +1 -1
  340. package/dist/application/transform/schema/schema-transform.mjs +6 -3
  341. package/dist/application/transform/schema/schema-transform.mjs.map +1 -1
  342. package/dist/application/transform/schema/unevaluated-properties.cjs +2 -30
  343. package/dist/application/transform/schema/unevaluated-properties.cjs.map +1 -1
  344. package/dist/application/transform/schema/unevaluated-properties.mjs +3 -31
  345. package/dist/application/transform/schema/unevaluated-properties.mjs.map +1 -1
  346. package/dist/application/transform/schema-comment.cjs +5 -1
  347. package/dist/application/transform/schema-comment.cjs.map +1 -1
  348. package/dist/application/transform/schema-comment.mjs +5 -1
  349. package/dist/application/transform/schema-comment.mjs.map +1 -1
  350. package/dist/cli/api-logger.cjs +13 -0
  351. package/dist/cli/api-logger.cjs.map +1 -0
  352. package/dist/cli/api-logger.mjs +11 -0
  353. package/dist/cli/api-logger.mjs.map +1 -0
  354. package/dist/cli/api-settings.cjs +57 -9
  355. package/dist/cli/api-settings.cjs.map +1 -1
  356. package/dist/cli/api-settings.mjs +56 -11
  357. package/dist/cli/api-settings.mjs.map +1 -1
  358. package/dist/cli/api-writes.cjs +72 -0
  359. package/dist/cli/api-writes.cjs.map +1 -0
  360. package/dist/cli/api-writes.mjs +69 -0
  361. package/dist/cli/api-writes.mjs.map +1 -0
  362. package/dist/cli/cli-arguments.cjs +51 -11
  363. package/dist/cli/cli-arguments.cjs.map +1 -1
  364. package/dist/cli/cli-arguments.mjs +51 -11
  365. package/dist/cli/cli-arguments.mjs.map +1 -1
  366. package/dist/cli/cli-errors.cjs +29 -1
  367. package/dist/cli/cli-errors.cjs.map +1 -1
  368. package/dist/cli/cli-errors.mjs +29 -2
  369. package/dist/cli/cli-errors.mjs.map +1 -1
  370. package/dist/cli/cli-output.cjs +111 -4
  371. package/dist/cli/cli-output.cjs.map +1 -1
  372. package/dist/cli/cli-output.mjs +104 -5
  373. package/dist/cli/cli-output.mjs.map +1 -1
  374. package/dist/cli/main.cjs +13 -3
  375. package/dist/cli/main.cjs.map +1 -1
  376. package/dist/cli/main.mjs +14 -4
  377. package/dist/cli/main.mjs.map +1 -1
  378. package/dist/cli/redocly-apis.cjs +94 -37
  379. package/dist/cli/redocly-apis.cjs.map +1 -1
  380. package/dist/cli/redocly-apis.mjs +99 -42
  381. package/dist/cli/redocly-apis.mjs.map +1 -1
  382. package/dist/cli/ref-limits.cjs +15 -1
  383. package/dist/cli/ref-limits.cjs.map +1 -1
  384. package/dist/cli/ref-limits.mjs +15 -2
  385. package/dist/cli/ref-limits.mjs.map +1 -1
  386. package/dist/cli/run-cli.cjs +37 -14
  387. package/dist/cli/run-cli.cjs.map +1 -1
  388. package/dist/cli/run-cli.mjs +39 -16
  389. package/dist/cli/run-cli.mjs.map +1 -1
  390. package/dist/domain/append-all.cjs +10 -0
  391. package/dist/domain/append-all.cjs.map +1 -0
  392. package/dist/domain/append-all.mjs +8 -0
  393. package/dist/domain/append-all.mjs.map +1 -0
  394. package/dist/domain/cause-secrets.cjs +52 -0
  395. package/dist/domain/cause-secrets.cjs.map +1 -0
  396. package/dist/domain/cause-secrets.mjs +50 -0
  397. package/dist/domain/cause-secrets.mjs.map +1 -0
  398. package/dist/domain/errors.cjs +11 -3
  399. package/dist/domain/errors.cjs.map +1 -1
  400. package/dist/domain/errors.mjs +11 -3
  401. package/dist/domain/errors.mjs.map +1 -1
  402. package/dist/domain/ir/identifier-text.cjs.map +1 -1
  403. package/dist/domain/ir/identifier-text.mjs +1 -1
  404. package/dist/domain/ir/identifier-text.mjs.map +1 -1
  405. package/dist/domain/ir/ir-shape.cjs +187 -0
  406. package/dist/domain/ir/ir-shape.cjs.map +1 -0
  407. package/dist/domain/ir/ir-shape.mjs +184 -0
  408. package/dist/domain/ir/ir-shape.mjs.map +1 -0
  409. package/dist/domain/ir/newer-identifier-ranges.cjs +182 -29
  410. package/dist/domain/ir/newer-identifier-ranges.cjs.map +1 -1
  411. package/dist/domain/ir/newer-identifier-ranges.mjs +182 -29
  412. package/dist/domain/ir/newer-identifier-ranges.mjs.map +1 -1
  413. package/dist/domain/location-text.cjs +9 -0
  414. package/dist/domain/location-text.cjs.map +1 -0
  415. package/dist/domain/location-text.mjs +7 -0
  416. package/dist/domain/location-text.mjs.map +1 -0
  417. package/dist/domain/openapi/alias-expansion.cjs +3 -1
  418. package/dist/domain/openapi/alias-expansion.cjs.map +1 -1
  419. package/dist/domain/openapi/alias-expansion.mjs +3 -1
  420. package/dist/domain/openapi/alias-expansion.mjs.map +1 -1
  421. package/dist/domain/openapi/discriminated-union.cjs +3 -3
  422. package/dist/domain/openapi/discriminated-union.cjs.map +1 -1
  423. package/dist/domain/openapi/discriminated-union.mjs +4 -4
  424. package/dist/domain/openapi/discriminated-union.mjs.map +1 -1
  425. package/dist/domain/openapi/discriminator-conflicts.cjs +10 -6
  426. package/dist/domain/openapi/discriminator-conflicts.cjs.map +1 -1
  427. package/dist/domain/openapi/discriminator-conflicts.mjs +11 -7
  428. package/dist/domain/openapi/discriminator-conflicts.mjs.map +1 -1
  429. package/dist/domain/openapi/document-places.cjs +55 -0
  430. package/dist/domain/openapi/document-places.cjs.map +1 -0
  431. package/dist/domain/openapi/document-places.mjs +53 -0
  432. package/dist/domain/openapi/document-places.mjs.map +1 -0
  433. package/dist/domain/openapi/json-pointer.cjs +43 -1
  434. package/dist/domain/openapi/json-pointer.cjs.map +1 -1
  435. package/dist/domain/openapi/json-pointer.mjs +40 -2
  436. package/dist/domain/openapi/json-pointer.mjs.map +1 -1
  437. package/dist/domain/openapi/schema-dialect.cjs +15 -0
  438. package/dist/domain/openapi/schema-dialect.cjs.map +1 -0
  439. package/dist/domain/openapi/schema-dialect.mjs +12 -0
  440. package/dist/domain/openapi/schema-dialect.mjs.map +1 -0
  441. package/dist/domain/openapi/unsupported-features.cjs +1 -1
  442. package/dist/domain/openapi/unsupported-features.cjs.map +1 -1
  443. package/dist/domain/openapi/unsupported-features.mjs +2 -2
  444. package/dist/domain/openapi/unsupported-features.mjs.map +1 -1
  445. package/dist/domain/output-fixes.cjs +20 -0
  446. package/dist/domain/output-fixes.cjs.map +1 -1
  447. package/dist/domain/output-fixes.mjs +20 -0
  448. package/dist/domain/output-fixes.mjs.map +1 -1
  449. package/dist/domain/terminal-text.cjs +31 -0
  450. package/dist/domain/terminal-text.cjs.map +1 -0
  451. package/dist/domain/terminal-text.mjs +29 -0
  452. package/dist/domain/terminal-text.mjs.map +1 -0
  453. package/dist/domain/url-secrets.cjs +48 -0
  454. package/dist/domain/url-secrets.cjs.map +1 -0
  455. package/dist/domain/url-secrets.mjs +45 -0
  456. package/dist/domain/url-secrets.mjs.map +1 -0
  457. package/dist/domain/warnings.cjs +1 -0
  458. package/dist/domain/warnings.cjs.map +1 -1
  459. package/dist/domain/warnings.mjs +1 -0
  460. package/dist/domain/warnings.mjs.map +1 -1
  461. package/dist/index.d.cts +46 -20
  462. package/dist/index.d.mts +46 -20
  463. package/dist/index.d.ts +46 -20
  464. package/package.json +4 -5
  465. package/src/adapters/node-file-system/file-claims.ts +138 -0
  466. package/src/adapters/node-file-system/file-identity.ts +137 -0
  467. package/src/adapters/node-file-system/node-file-system.ts +158 -18
  468. package/src/adapters/pipe-safe-writer/pipe-safe-writer.ts +29 -0
  469. package/src/adapters/redocly-loader/bundled-mapping-copies.ts +60 -0
  470. package/src/adapters/redocly-loader/bundled-ref-repair.ts +2 -2
  471. package/src/adapters/redocly-loader/component-schemas.ts +19 -0
  472. package/src/adapters/redocly-loader/loaded-documents.ts +46 -0
  473. package/src/adapters/redocly-loader/mapping-schema-names.ts +25 -0
  474. package/src/adapters/redocly-loader/query-named-components.ts +201 -0
  475. package/src/adapters/redocly-loader/redocly-schema-loader.ts +64 -38
  476. package/src/adapters/redocly-loader/ref-access.ts +51 -4
  477. package/src/adapters/redocly-loader/ref-holders.ts +76 -15
  478. package/src/adapters/redocly-loader/remote-body-limit.ts +66 -0
  479. package/src/adapters/redocly-loader/remote-fetch.ts +85 -13
  480. package/src/adapters/redocly-loader/remote-node-limit.ts +102 -0
  481. package/src/adapters/redocly-loader/schema-load-errors.ts +52 -13
  482. package/src/adapters/redocly-loader/schema-source.ts +35 -11
  483. package/src/adapters/redocly-loader/unsupported-keys.ts +3 -7
  484. package/src/adapters/terminal-logger/terminal-logger.ts +17 -6
  485. package/src/api/default-ports.ts +33 -4
  486. package/src/api/discriminator-enum-parent.ts +3 -3
  487. package/src/api/discriminator-other-owners.ts +5 -3
  488. package/src/api/discriminator-patch.ts +5 -1
  489. package/src/api/discriminator-scan.ts +16 -8
  490. package/src/api/document-refs.ts +29 -1
  491. package/src/api/document-run.ts +10 -19
  492. package/src/api/legacy-utils.ts +5 -1
  493. package/src/api/openapi-ts.ts +26 -8
  494. package/src/api/ref-limit-options.ts +76 -0
  495. package/src/api/run-context.ts +3 -0
  496. package/src/api/schema-callbacks.ts +60 -17
  497. package/src/api/transform-context.ts +7 -3
  498. package/src/api/transform-schema.ts +79 -12
  499. package/src/application/ports/file-system.ts +47 -0
  500. package/src/application/ports/schema-loader.ts +11 -1
  501. package/src/application/print/text-writer.ts +10 -3
  502. package/src/application/stack-overflow.ts +9 -5
  503. package/src/application/transform/declared-value-types.ts +42 -0
  504. package/src/application/transform/document/components.ts +2 -2
  505. package/src/application/transform/document/declared-type-names.ts +36 -0
  506. package/src/application/transform/document/defs-root.ts +2 -2
  507. package/src/application/transform/document/document.ts +8 -5
  508. package/src/application/transform/document/enum-names-given-back.ts +91 -0
  509. package/src/application/transform/document/exploded-query-values.ts +88 -0
  510. package/src/application/transform/document/global-type-references.ts +1 -1
  511. package/src/application/transform/document/header.ts +25 -5
  512. package/src/application/transform/document/media-type.ts +4 -3
  513. package/src/application/transform/document/operation-keys-written.ts +27 -0
  514. package/src/application/transform/document/operation.ts +2 -2
  515. package/src/application/transform/document/parameter-entries.ts +70 -0
  516. package/src/application/transform/document/parameter.ts +21 -9
  517. package/src/application/transform/document/parameters.ts +56 -66
  518. package/src/application/transform/document/path-item.ts +16 -34
  519. package/src/application/transform/document/path-template-key.ts +152 -0
  520. package/src/application/transform/document/path-template-refs.ts +106 -0
  521. package/src/application/transform/document/paths-enum.ts +24 -10
  522. package/src/application/transform/document/paths.ts +59 -125
  523. package/src/application/transform/document/referenced-parameter.ts +146 -0
  524. package/src/application/transform/document/request-body.ts +2 -2
  525. package/src/application/transform/document/response.ts +3 -3
  526. package/src/application/transform/document/responses.ts +2 -2
  527. package/src/application/transform/document/root-type-aliases.ts +17 -38
  528. package/src/application/transform/document/type-resolution.ts +2 -1
  529. package/src/application/transform/document/webhooks.ts +19 -6
  530. package/src/application/transform/footer/document-footer.ts +36 -9
  531. package/src/application/transform/footer/enum-name-claims.ts +62 -0
  532. package/src/application/transform/footer/operation-keys.ts +28 -0
  533. package/src/application/transform/footer/type-evaluation.ts +4 -4
  534. package/src/application/transform/helper-types.ts +8 -7
  535. package/src/application/transform/numbered-names.ts +15 -0
  536. package/src/application/transform/ref-pointer/generated-location.ts +3 -3
  537. package/src/application/transform/ref-pointer/generated-shape.ts +67 -78
  538. package/src/application/transform/ref-pointer/inline-cycles.ts +1 -1
  539. package/src/application/transform/ref-pointer/located-node.ts +38 -0
  540. package/src/application/transform/ref-pointer/original-index.ts +100 -17
  541. package/src/application/transform/ref-pointer/pointer-type.ts +1 -1
  542. package/src/application/transform/ref-pointer/pointer-walk.ts +82 -8
  543. package/src/application/transform/ref-pointer/property-shape.ts +97 -0
  544. package/src/application/transform/ref-pointer/typed-walk.ts +117 -25
  545. package/src/application/transform/ref-type.ts +2 -1
  546. package/src/application/transform/schema/access-flags.ts +5 -2
  547. package/src/application/transform/schema/composition-type.ts +2 -2
  548. package/src/application/transform/schema/enum-rule.ts +6 -2
  549. package/src/application/transform/schema/index-signature-type.ts +31 -0
  550. package/src/application/transform/schema/input-location.ts +35 -0
  551. package/src/application/transform/schema/object-type.ts +22 -12
  552. package/src/application/transform/schema/ref-siblings.ts +76 -0
  553. package/src/application/transform/schema/schema-context.ts +3 -0
  554. package/src/application/transform/schema/schema-transform.ts +6 -3
  555. package/src/application/transform/schema/unevaluated-properties.ts +4 -44
  556. package/src/application/transform/schema-comment.ts +5 -1
  557. package/src/application/transform/transform-context.ts +5 -0
  558. package/src/cli/api-logger.ts +17 -0
  559. package/src/cli/api-settings.ts +80 -13
  560. package/src/cli/api-writes.ts +117 -0
  561. package/src/cli/cli-arguments.ts +90 -14
  562. package/src/cli/cli-dependencies.ts +26 -4
  563. package/src/cli/cli-errors.ts +42 -1
  564. package/src/cli/cli-output.ts +195 -15
  565. package/src/cli/main.ts +14 -5
  566. package/src/cli/redocly-apis.ts +177 -52
  567. package/src/cli/ref-limits.ts +21 -2
  568. package/src/cli/run-cli.ts +76 -15
  569. package/src/domain/append-all.ts +10 -0
  570. package/src/domain/cause-secrets.ts +86 -0
  571. package/src/domain/errors.ts +16 -5
  572. package/src/domain/ir/identifier-text.ts +6 -5
  573. package/src/domain/ir/ir-shape.ts +299 -0
  574. package/src/domain/ir/newer-identifier-ranges.ts +183 -30
  575. package/src/domain/location-text.ts +12 -0
  576. package/src/domain/openapi/alias-expansion.ts +4 -1
  577. package/src/domain/openapi/discriminated-union.ts +6 -4
  578. package/src/domain/openapi/discriminator-conflicts.ts +21 -7
  579. package/src/domain/openapi/document-object.ts +2 -0
  580. package/src/domain/openapi/document-places.ts +78 -0
  581. package/src/domain/openapi/json-pointer.ts +96 -7
  582. package/src/domain/openapi/schema-dialect.ts +22 -0
  583. package/src/domain/openapi/unsupported-features.ts +2 -2
  584. package/src/domain/output-fixes.ts +28 -2
  585. package/src/domain/terminal-text.ts +44 -0
  586. package/src/domain/url-secrets.ts +72 -0
  587. package/src/domain/warnings.ts +1 -0
  588. package/src/types/generator-options.ts +1 -1
  589. package/dist/cli/main.d.cts +0 -2
  590. package/dist/cli/main.d.mts +0 -2
  591. package/dist/cli/main.d.ts +0 -2
  592. package/dist/domain/url-credentials.cjs +0 -10
  593. package/dist/domain/url-credentials.cjs.map +0 -1
  594. package/dist/domain/url-credentials.mjs +0 -8
  595. package/dist/domain/url-credentials.mjs.map +0 -1
  596. package/src/domain/url-credentials.ts +0 -18
@@ -0,0 +1,66 @@
1
+ import type { OpenAPITypesafeError } from "../../domain/errors.js";
2
+ import { refForbiddenError } from "./schema-load-errors.js";
3
+
4
+ /** The decoded size limits of the remote documents of one load (threat model T2). */
5
+ export interface RemoteBodyLimits {
6
+ /** Bytes of one remote document's body after `Content-Encoding` is decoded. */
7
+ readonly maxDocumentBytes: number;
8
+ /** Bytes of the bodies of every remote document of one load together, decoded. */
9
+ readonly maxTotalBytes: number;
10
+ }
11
+
12
+ /**
13
+ * Reads remote bodies as a stream and stops at the limits. Fetch decodes `gzip`, `deflate` and
14
+ * `br` before the body reaches the loader, so a body of a few megabytes on the wire can become
15
+ * gigabytes in memory (a "gzip bomb"); counting the decoded bytes while they arrive stops it before
16
+ * the memory is taken. One instance per load: the total covers every document of that load.
17
+ */
18
+ export class RemoteBodyLimit {
19
+ readonly #limits: RemoteBodyLimits;
20
+ #totalBytes = 0;
21
+
22
+ constructor(limits: RemoteBodyLimits) {
23
+ this.#limits = limits;
24
+ }
25
+
26
+ /** The whole body of `response` from `url`; fails with `SCHEMA_REF_FORBIDDEN` past a limit. */
27
+ async read(response: Response, url: string): Promise<ArrayBuffer> {
28
+ const reader = response.body?.getReader();
29
+ if (reader === undefined) {
30
+ return new ArrayBuffer(0);
31
+ }
32
+ const chunks: Uint8Array[] = [];
33
+ let documentBytes = 0;
34
+ for (let next = await reader.read(); !next.done; next = await reader.read()) {
35
+ documentBytes += next.value.byteLength;
36
+ this.#totalBytes += next.value.byteLength;
37
+ const refusal = this.#refusal(url, documentBytes);
38
+ if (refusal !== undefined) {
39
+ await reader.cancel();
40
+ throw refusal;
41
+ }
42
+ chunks.push(next.value);
43
+ }
44
+ return joined(chunks, documentBytes);
45
+ }
46
+
47
+ #refusal(url: string, documentBytes: number): OpenAPITypesafeError | undefined {
48
+ const { maxDocumentBytes, maxTotalBytes } = this.#limits;
49
+ if (documentBytes > maxDocumentBytes) {
50
+ return refForbiddenError(url, { reason: "remote-document-too-large", maxDocumentBytes });
51
+ }
52
+ return this.#totalBytes > maxTotalBytes
53
+ ? refForbiddenError(url, { reason: "remote-documents-too-large", maxTotalBytes })
54
+ : undefined;
55
+ }
56
+ }
57
+
58
+ function joined(chunks: readonly Uint8Array[], byteLength: number): ArrayBuffer {
59
+ const body = new Uint8Array(byteLength);
60
+ let offset = 0;
61
+ for (const chunk of chunks) {
62
+ body.set(chunk, offset);
63
+ offset += chunk.byteLength;
64
+ }
65
+ return body.buffer;
66
+ }
@@ -2,11 +2,12 @@ import type { Config as RedoclyConfig } from "@redocly/openapi-core";
2
2
  import type { RefAccess } from "../../application/ports/schema-loader.js";
3
3
  import { isHostAllowed } from "./allowed-hosts.js";
4
4
  import { type ConfiguredHeader, headersFor } from "./configured-headers.js";
5
+ import { RemoteBodyLimit, type RemoteBodyLimits } from "./remote-body-limit.js";
5
6
  import { refForbiddenError } from "./schema-load-errors.js";
6
7
 
7
8
  /**
8
- * How the loader fetches remote documents: a time limit, the host limit and the configured headers
9
- * checked on every redirect.
9
+ * How the loader fetches remote documents: a time limit, size limits, the host limit and the
10
+ * configured headers checked on every redirect.
10
11
  */
11
12
 
12
13
  type ResolveConfig = RedoclyConfig["resolve"];
@@ -25,9 +26,16 @@ const REDIRECT_STATUSES = new Set([301, 302, 303, 307, 308]);
25
26
  /** Statuses whose responses have no body; `new Response()` refuses a body for them. */
26
27
  const NULL_BODY_STATUSES = new Set([101, 103, 204, 205, 304]);
27
28
 
28
- /** A response whose body is already read, so that reading it again cannot wait or fail. */
29
- async function bufferedResponse(response: Response): Promise<Response> {
30
- const body = await response.arrayBuffer();
29
+ /**
30
+ * A response whose body is already read within the size limits, so that reading it again cannot
31
+ * wait, fail or take more memory.
32
+ */
33
+ async function bufferedResponse(
34
+ response: Response,
35
+ url: string,
36
+ bodyLimit: RemoteBodyLimit,
37
+ ): Promise<Response> {
38
+ const body = await bodyLimit.read(response, url);
31
39
  const { status, statusText, headers } = response;
32
40
  return new Response(NULL_BODY_STATUSES.has(status) ? null : body, {
33
41
  status,
@@ -36,6 +44,45 @@ async function bufferedResponse(response: Response): Promise<Response> {
36
44
  });
37
45
  }
38
46
 
47
+ /**
48
+ * Codes of a connection that closed before any response came. The server can close a kept-alive
49
+ * connection while the process is busy (parsing a large document, generating another API) and
50
+ * has not read the close yet; the next request on that connection then fails with one of these.
51
+ */
52
+ const CLOSED_CONNECTION_CODES: ReadonlySet<string> = new Set([
53
+ "ECONNRESET",
54
+ "EPIPE",
55
+ "UND_ERR_SOCKET",
56
+ ]);
57
+
58
+ /** Methods that may be sent again without a second effect (RFC 9110 §9.2.2). */
59
+ const IDEMPOTENT_METHODS: ReadonlySet<string> = new Set(["GET", "HEAD"]);
60
+ const DEFAULT_METHOD = "GET";
61
+
62
+ /** Whether fetch failed because its connection closed before any response came. */
63
+ function isClosedConnection(error: unknown): boolean {
64
+ const cause = error instanceof TypeError ? error.cause : undefined;
65
+ const code = cause instanceof Error && "code" in cause ? cause.code : undefined;
66
+ return typeof code === "string" && CLOSED_CONNECTION_CODES.has(code);
67
+ }
68
+
69
+ /**
70
+ * Fetch, and send the request once more when its connection closed before any response came, as
71
+ * RFC 9112 §9.3.1 allows for an idempotent request. Fetch drops a connection that failed, so the
72
+ * second attempt goes out on another one; a second failure is the server's answer and is thrown.
73
+ */
74
+ async function fetchOnOpenConnection(input: string, init: RequestInit): Promise<Response> {
75
+ try {
76
+ return await fetch(input, init);
77
+ } catch (error) {
78
+ const method = (init.method ?? DEFAULT_METHOD).toUpperCase();
79
+ if (!(IDEMPOTENT_METHODS.has(method) && isClosedConnection(error))) {
80
+ throw error;
81
+ }
82
+ return fetch(input, init);
83
+ }
84
+ }
85
+
39
86
  /**
40
87
  * Fetch with a time limit for the whole response, body included: Node's own fetch waits five
41
88
  * minutes for a server that never answers, and a server that sends its body one byte at a time
@@ -43,11 +90,13 @@ async function bufferedResponse(response: Response): Promise<Response> {
43
90
  * surfaced as a `DOMException` whose read-only `message` Redocly tried to change, which gave
44
91
  * "Cannot set property message of which has only a getter" instead of the time limit.
45
92
  */
46
- function fetchWithTimeout(timeoutMs: number): RemoteFetch {
93
+ function fetchWithTimeout(timeoutMs: number, bodyLimit: RemoteBodyLimit): RemoteFetch {
47
94
  return async (input, init = {}) => {
48
95
  try {
49
- const response = await fetch(input, { ...init, signal: AbortSignal.timeout(timeoutMs) });
50
- return await bufferedResponse(response);
96
+ // One signal for both attempts, so that a request sent again stays within the same limit.
97
+ const signal = AbortSignal.timeout(timeoutMs);
98
+ const response = await fetchOnOpenConnection(input, { ...init, signal });
99
+ return await bufferedResponse(response, input, bodyLimit);
51
100
  } catch (cause) {
52
101
  const isTimeout = cause instanceof Error && cause.name === "TimeoutError";
53
102
  throw isTimeout ? new Error(`Timed out after ${timeoutMs} ms loading ${input}`) : cause;
@@ -55,12 +104,22 @@ function fetchWithTimeout(timeoutMs: number): RemoteFetch {
55
104
  };
56
105
  }
57
106
 
107
+ /** A configured `customFetch` with its bodies read within the size limits. */
108
+ function fetchWithBodyLimit(fetchImpl: RemoteFetch, bodyLimit: RemoteBodyLimit): RemoteFetch {
109
+ return async (input, init) => bufferedResponse(await fetchImpl(input, init), input, bodyLimit);
110
+ }
111
+
58
112
  /** What every hop of a remote fetch is checked against. */
59
113
  export interface RedirectPolicy {
60
114
  /** `resolve.http.headers` of the Redocly config; chosen again for the URL of every hop. */
61
115
  readonly headers: readonly ConfiguredHeader[];
62
116
  /** `--remote-ref-hosts`; undefined allows every host. */
63
117
  readonly hosts: readonly string[] | undefined;
118
+ /**
119
+ * The URL of the root document, if it is remote. The developer chose it, so neither it nor its
120
+ * redirects are limited to the hosts (ADR-017): a spec URL that redirects to a CDN loads.
121
+ */
122
+ readonly rootUrl: string | undefined;
64
123
  }
65
124
 
66
125
  /**
@@ -76,12 +135,13 @@ export function fetchWithCheckedRedirects(
76
135
  policy: RedirectPolicy,
77
136
  ): RemoteFetch {
78
137
  return async (input, init = {}) => {
138
+ const hosts = input === policy.rootUrl ? undefined : policy.hosts;
79
139
  let url = new URL(input);
80
140
  for (let hop = 0; hop <= MAX_REDIRECTS; hop += 1) {
81
- if (hop > 0 && policy.hosts !== undefined && !isHostAllowed(url, policy.hosts)) {
141
+ if (hop > 0 && hosts !== undefined && !isHostAllowed(url, hosts)) {
82
142
  throw refForbiddenError(url.href, {
83
143
  reason: "host-not-allowed",
84
- allowedHosts: [...policy.hosts],
144
+ allowedHosts: [...hosts],
85
145
  });
86
146
  }
87
147
  const headers = await headersFor(url, policy.headers);
@@ -97,11 +157,13 @@ export function fetchWithCheckedRedirects(
97
157
  }
98
158
 
99
159
  /** Bounds on the remote documents of one load (threat model T2, ADR-033). */
100
- export interface RemoteDocumentLimits {
160
+ export interface RemoteDocumentLimits extends RemoteBodyLimits {
101
161
  /** Remote documents one load may fetch, the root included; one more is refused. */
102
162
  readonly maxDocuments: number;
103
163
  /** Remote documents fetched at the same time; the others wait for a free slot. */
104
164
  readonly maxParallelFetches: number;
165
+ /** Nodes (keys and values) the remote documents of one load may parse into together. */
166
+ readonly maxNodes: number;
105
167
  }
106
168
 
107
169
  /**
@@ -134,12 +196,16 @@ export interface RemoteFetchOptions {
134
196
  /** Time limit of one fetch and its body, unless the Redocly config has its own `customFetch`. */
135
197
  readonly timeoutMs: number;
136
198
  readonly access: RefAccess;
199
+ /** The root document's URL, whose redirects are not limited to the hosts. */
200
+ readonly rootUrl: string | undefined;
137
201
  readonly maxParallelFetches: number;
202
+ readonly bodyLimits: RemoteBodyLimits;
138
203
  }
139
204
 
140
205
  /**
141
206
  * The resolve config with the fetch the loader uses. A `customFetch` of the Redocly config is
142
- * kept and wrapped, so that each redirect is checked and only a few documents load at once. The
207
+ * kept and wrapped, so that each redirect is checked, each body is read within the size limits
208
+ * and only a few documents load at once. The
143
209
  * configured headers move into that wrapper: Redocly would match them against the raw URL and
144
210
  * send them on through redirects.
145
211
  */
@@ -149,10 +215,16 @@ export function resolveConfigWithAccess(
149
215
  ): ResolveConfig {
150
216
  // Redocly types `customFetch` as `Function`; it is called as fetch is.
151
217
  const configured = resolve.http.customFetch as RemoteFetch | undefined;
152
- const base = configured ?? fetchWithTimeout(options.timeoutMs);
218
+ // One limit per load, so that the total covers every document this resolver fetches.
219
+ const bodyLimit = new RemoteBodyLimit(options.bodyLimits);
220
+ const base =
221
+ configured === undefined
222
+ ? fetchWithTimeout(options.timeoutMs, bodyLimit)
223
+ : fetchWithBodyLimit(configured, bodyLimit);
153
224
  const checked = fetchWithCheckedRedirects(base, {
154
225
  headers: resolve.http.headers,
155
226
  hosts: options.access.remoteHosts,
227
+ rootUrl: options.rootUrl,
156
228
  });
157
229
  // The limit is outside the redirects, so all hops of one document share one slot.
158
230
  const customFetch = fetchWithConcurrencyLimit(checked, options.maxParallelFetches);
@@ -0,0 +1,102 @@
1
+ import { type Document, parseYaml, type Source } from "@redocly/openapi-core";
2
+ import type { OpenAPITypesafeError } from "../../domain/errors.js";
3
+ import { refForbiddenError } from "./schema-load-errors.js";
4
+
5
+ /**
6
+ * Bounds the parsed size of the remote documents of one load (threat model T2). The byte limits
7
+ * do not bound memory: parsing multiplies it by the density of the text, and 250 MiB of `{},`
8
+ * (87 million empty objects, under a quarter of a megabyte as gzip) took the process past its
9
+ * 4 GB heap. The parser reports every node it builds, keys included, and the parse stops when
10
+ * the nodes of all remote documents of the load pass the limit, before their memory is taken.
11
+ */
12
+
13
+ /** The extensions that Redocly 1.34's `BaseResolver.parseDocument` parses as YAML or JSON. */
14
+ const PARSED_EXTENSIONS: ReadonlySet<string> = new Set([".json", ".yml", ".yaml"]);
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
+ }
43
+
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
+ */
52
+ export function isParsedSource(source: Source, isRoot: boolean): boolean {
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
+ );
62
+ }
63
+
64
+ /** The parser's event for a node it starts to build. */
65
+ const NODE_OPENED = "open";
66
+
67
+ /** One instance per load: the count covers every remote document of that load. */
68
+ export class RemoteNodeLimit {
69
+ readonly #maxNodes: number;
70
+ #nodes = 0;
71
+
72
+ constructor(maxNodes: number) {
73
+ this.#maxNodes = maxNodes;
74
+ }
75
+
76
+ /**
77
+ * The parsed document of `source`, as Redocly parses it. Fails with `SCHEMA_REF_FORBIDDEN`
78
+ * (`remote-documents-too-many-nodes`) once the nodes of the load pass the limit.
79
+ */
80
+ parse(source: Source): Document {
81
+ const listener = (event: string): void => {
82
+ if (event !== NODE_OPENED) {
83
+ return;
84
+ }
85
+ this.#nodes += 1;
86
+ const refusal = this.#refusal(source.absoluteRef);
87
+ if (refusal !== undefined) {
88
+ throw refusal;
89
+ }
90
+ };
91
+ return { source, parsed: parseYaml(source.body, { filename: source.absoluteRef, listener }) };
92
+ }
93
+
94
+ #refusal(url: string): OpenAPITypesafeError | undefined {
95
+ return this.#nodes > this.#maxNodes
96
+ ? refForbiddenError(url, {
97
+ reason: "remote-documents-too-many-nodes",
98
+ maxNodes: this.#maxNodes,
99
+ })
100
+ : undefined;
101
+ }
102
+ }
@@ -1,6 +1,6 @@
1
1
  import { assertNever } from "../../domain/assert-never.js";
2
2
  import { ERROR_CODES, OpenAPITypesafeError } from "../../domain/errors.js";
3
- import { redactUrlCredentials } from "../../domain/url-credentials.js";
3
+ import { redactUnparsedUrl, redactUrlSecrets } from "../../domain/url-secrets.js";
4
4
 
5
5
  /**
6
6
  * The typed errors of loading, validating and bundling a document (ADR-011). Messages are the
@@ -23,6 +23,15 @@ export function sourceInvalidError(source: unknown): OpenAPITypesafeError {
23
23
  });
24
24
  }
25
25
 
26
+ /** A source string starts with a URL scheme but does not parse as a URL. */
27
+ export function urlInvalidError(text: string): OpenAPITypesafeError {
28
+ return new OpenAPITypesafeError({
29
+ code: ERROR_CODES.SCHEMA_SOURCE_INVALID,
30
+ message: `The string ${redactUnparsedUrl(text)} starts like a URL but is not a valid URL`,
31
+ details: { reason: "url", received: "string" },
32
+ });
33
+ }
34
+
26
35
  function causeMessage(cause: unknown): string {
27
36
  return cause instanceof Error ? cause.message : String(cause);
28
37
  }
@@ -30,19 +39,29 @@ function causeMessage(cause: unknown): string {
30
39
  /** Enough of a source string to recognise it in the message. */
31
40
  const SOURCE_TEXT_IN_MESSAGE = 200;
32
41
 
42
+ /** Where document text that is only a YAML string came from. */
43
+ export type TextOrigin = "string" | "stream";
44
+
45
+ const LOAD_FILE_ADVICE =
46
+ "To load a file, pass a URL: openapiTS(pathToFileURL(path)) from node:url.";
47
+
33
48
  /**
34
49
  * A string source is document text, so a file path parses as a YAML string scalar, not as a
35
- * document (docs/node.md). The message says how to load the file instead.
50
+ * document (docs/node.md). The message says how to load the file instead. Text read from a
51
+ * stream (stdin in the CLI) is named as such; `details.text` lets the CLI give its own advice.
36
52
  */
37
- export function pathStringError(text: string): OpenAPITypesafeError {
53
+ export function pathStringError(text: string, origin: TextOrigin): OpenAPITypesafeError {
38
54
  const shown =
39
55
  text.length > SOURCE_TEXT_IN_MESSAGE ? `${text.slice(0, SOURCE_TEXT_IN_MESSAGE)}…` : text;
56
+ const message =
57
+ origin === "string"
58
+ ? `The string "${shown}" was parsed as YAML text, not loaded as a file. ${LOAD_FILE_ADVICE}`
59
+ : `The stream held only the YAML string "${shown}", not an OpenAPI document. ` +
60
+ LOAD_FILE_ADVICE;
40
61
  return new OpenAPITypesafeError({
41
62
  code: ERROR_CODES.SCHEMA_SOURCE_INVALID,
42
- message:
43
- `The string "${shown}" was parsed as YAML text, not loaded as a file. ` +
44
- "To load a file, pass a URL: openapiTS(pathToFileURL(path)) from node:url.",
45
- details: { reason: "path", received: "string" },
63
+ message,
64
+ details: { reason: "path", received: origin, text: shown },
46
65
  });
47
66
  }
48
67
 
@@ -51,7 +70,7 @@ export function loadFailedError(absoluteRef: string, cause: unknown): OpenAPITyp
51
70
  return new OpenAPITypesafeError({
52
71
  code: ERROR_CODES.SCHEMA_LOAD_FAILED,
53
72
  message: causeMessage(cause),
54
- details: { source: redactUrlCredentials(absoluteRef) },
73
+ details: { source: redactUrlSecrets(absoluteRef) },
55
74
  cause,
56
75
  });
57
76
  }
@@ -65,7 +84,7 @@ export function parseFailedError(
65
84
  return new OpenAPITypesafeError({
66
85
  code: ERROR_CODES.SCHEMA_PARSE_FAILED,
67
86
  message: causeMessage(cause),
68
- details: { format, source: redactUrlCredentials(absoluteRef) },
87
+ details: { format, source: redactUrlSecrets(absoluteRef) },
69
88
  cause,
70
89
  });
71
90
  }
@@ -90,7 +109,7 @@ export function schemaInvalidError(problems: readonly string[]): OpenAPITypesafe
90
109
  return new OpenAPITypesafeError({
91
110
  code: ERROR_CODES.SCHEMA_INVALID,
92
111
  message: problems.at(-1) ?? "",
93
- details: { problems: problems.map(redactUrlCredentials) },
112
+ details: { problems: problems.map(redactUrlSecrets) },
94
113
  });
95
114
  }
96
115
 
@@ -113,7 +132,17 @@ export type RefRefusalDetails =
113
132
  | { readonly reason: "outside-local-root"; readonly localRoot: string }
114
133
  | { readonly reason: "host-not-allowed"; readonly allowedHosts: readonly string[] }
115
134
  | { readonly reason: "local-ref-from-remote"; readonly base: string }
116
- | { readonly reason: "too-many-remote-documents"; readonly maxDocuments: number };
135
+ | { readonly reason: "too-many-remote-documents"; readonly maxDocuments: number }
136
+ | { readonly reason: "remote-document-too-large"; readonly maxDocumentBytes: number }
137
+ | { readonly reason: "remote-documents-too-large"; readonly maxTotalBytes: number }
138
+ | { readonly reason: "remote-documents-too-many-nodes"; readonly maxNodes: number };
139
+
140
+ const BYTES_PER_MEBIBYTE = 1024 * 1024;
141
+
142
+ /** A byte count in MiB, as the limits are chosen: `256 MiB`; a smaller limit in bytes. */
143
+ function sizeText(bytes: number): string {
144
+ return bytes % BYTES_PER_MEBIBYTE === 0 ? `${bytes / BYTES_PER_MEBIBYTE} MiB` : `${bytes} bytes`;
145
+ }
117
146
 
118
147
  function limitText(details: RefRefusalDetails): string {
119
148
  switch (details.reason) {
@@ -127,6 +156,16 @@ function limitText(details: RefRefusalDetails): string {
127
156
  return `it is not an http(s) URL, and the $ref is in the remote document ${details.base}`;
128
157
  case "too-many-remote-documents":
129
158
  return `the document refers to more than ${details.maxDocuments} remote documents`;
159
+ case "remote-document-too-large":
160
+ return `its body is larger than ${sizeText(details.maxDocumentBytes)} once decoded`;
161
+ case "remote-documents-too-large": {
162
+ const limit = sizeText(details.maxTotalBytes);
163
+ return `the remote documents of this run are larger than ${limit} together once decoded`;
164
+ }
165
+ case "remote-documents-too-many-nodes": {
166
+ const limit = details.maxNodes;
167
+ return `the remote documents of this run parse into more than ${limit} nodes together`;
168
+ }
130
169
  default:
131
170
  return assertNever(details);
132
171
  }
@@ -134,13 +173,13 @@ function limitText(details: RefRefusalDetails): string {
134
173
 
135
174
  /**
136
175
  * A `$ref` is outside the local root, its host is not allowed (threat model R1, R2), a remote
137
- * document refers to a local file (T3), or there are too many remote documents (T2).
176
+ * document refers to a local file (T3), or there are too many or too large remote documents (T2).
138
177
  */
139
178
  export function refForbiddenError(ref: string, details: RefRefusalDetails): OpenAPITypesafeError {
140
179
  const limit = limitText(details);
141
180
  return new OpenAPITypesafeError({
142
181
  code: ERROR_CODES.SCHEMA_REF_FORBIDDEN,
143
182
  message: `Refused to load ${ref}: ${limit}`,
144
- details: { ref: redactUrlCredentials(ref), ...details },
183
+ details: { ref: redactUrlSecrets(ref), ...details },
145
184
  });
146
185
  }
@@ -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,
@@ -17,6 +17,8 @@ import {
17
17
  pathStringError,
18
18
  sourceEmptyError,
19
19
  sourceInvalidError,
20
+ type TextOrigin,
21
+ urlInvalidError,
20
22
  } from "./schema-load-errors.js";
21
23
 
22
24
  /** Parse a schema source of any supported type into a Redocly document (ENGINEERING_PLAN 4.5). */
@@ -24,6 +26,8 @@ import {
24
26
  export interface ParseSchemaOptions {
25
27
  absoluteRef: string;
26
28
  resolver: BaseResolver;
29
+ /** Where document text came from, for the message of text that is only a string. */
30
+ textOrigin?: TextOrigin;
27
31
  }
28
32
 
29
33
  /**
@@ -38,21 +42,28 @@ async function readStream(stream: Readable, absoluteRef: string): Promise<string
38
42
  }
39
43
  }
40
44
 
41
- const URL_PREFIXES = ["http://", "https://", "file://"] as const;
45
+ /** A URL source string; a scheme is the same in any case (RFC 3986, section 3.1). */
46
+ const URL_PREFIX_RE = /^(?:https?|file):\/\//i;
42
47
 
43
- /** A string is a URL, JSON (starts with `{`) or YAML. */
48
+ /**
49
+ * A string is a URL, JSON (starts with `{`) or YAML. A string that starts like a URL but does not
50
+ * parse as one fails with `SCHEMA_SOURCE_INVALID`, not the parser's `TypeError`.
51
+ */
44
52
  function parseSchemaText(
45
53
  schema: string,
46
54
  options: ParseSchemaOptions,
47
55
  ): Promise<Document> | Document {
48
- if (URL_PREFIXES.some((prefix) => schema.startsWith(prefix))) {
49
- const url = new URL(schema);
56
+ if (URL_PREFIX_RE.test(schema)) {
57
+ const url = URL.parse(schema);
58
+ if (url === null) {
59
+ throw urlInvalidError(schema);
60
+ }
50
61
  const absoluteRef = url.protocol === "file:" ? fileURLToPath(url) : url.href;
51
62
  return parseSchema(url, { absoluteRef, resolver: options.resolver });
52
63
  }
53
64
  return schema[0] === "{"
54
65
  ? parseJsonText(schema, options.absoluteRef)
55
- : parseYamlText(schema, options.absoluteRef);
66
+ : parseYamlText(schema, options.absoluteRef, options.textOrigin ?? "string");
56
67
  }
57
68
 
58
69
  function parseJsonText(text: string, absoluteRef: string): Document {
@@ -63,16 +74,28 @@ function parseJsonText(text: string, absoluteRef: string): Document {
63
74
  }
64
75
  }
65
76
 
66
- function parseYamlText(text: string, absoluteRef: string): Document {
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
+
87
+ function parseYamlText(text: string, absoluteRef: string, origin: TextOrigin): Document {
88
+ const source = new Source(absoluteRef, text);
67
89
  let document: Document;
68
90
  try {
69
- document = makeDocumentFromString(text, absoluteRef);
91
+ document = { source, parsed: parseYaml(text, { filename: TEXT_ORIGIN_NAMES[origin] }) };
70
92
  } catch (cause) {
71
- 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));
72
95
  }
73
96
  // Text that is only a YAML string scalar, such as "./openapi.yaml", was meant as a file path.
74
97
  if (typeof document.parsed === "string") {
75
- throw pathStringError(text);
98
+ throw pathStringError(text, origin);
76
99
  }
77
100
  const failure = aliasExpansionFailure(document.parsed);
78
101
  if (failure !== undefined) {
@@ -139,7 +162,8 @@ export async function parseSchema(schema: unknown, options: ParseSchemaOptions):
139
162
  return resolveSchemaURL(options);
140
163
  }
141
164
  if (schema instanceof Readable) {
142
- return parseSchema(await readStream(schema, options.absoluteRef), options);
165
+ const text = await readStream(schema, options.absoluteRef);
166
+ return parseSchema(text, { ...options, textOrigin: "stream" });
143
167
  }
144
168
  if (schema instanceof Buffer) {
145
169
  return parseSchema(schema.toString("utf8"), options);
@@ -1,4 +1,5 @@
1
1
  import { ERROR_CODES, OpenAPITypesafeError } from "../../domain/errors.js";
2
+ import { childPointer, ROOT_POINTER } from "../../domain/openapi/json-pointer.js";
2
3
 
3
4
  /**
4
5
  * Keys Redocly 1.34 cannot walk. It looks up the type of every key with `type.properties[key]`,
@@ -57,11 +58,6 @@ interface PendingNode {
57
58
  readonly pointer: string;
58
59
  }
59
60
 
60
- /** RFC 6901: `~` becomes `~0` and `/` becomes `~1`. */
61
- function escapePointerSegment(segment: string): string {
62
- return segment.replaceAll("~", "~0").replaceAll("/", "~1");
63
- }
64
-
65
61
  /**
66
62
  * The unsupported keys of one parsed document. The walk uses its own stack, so a deeply nested
67
63
  * document is checked without running out of call stack; nodes shared by YAML aliases are checked
@@ -77,10 +73,10 @@ function unsupportedKeysOf(document: LoadedDocument): UnsupportedKey[] {
77
73
  pending.push({ value, pointer });
78
74
  }
79
75
  };
80
- enqueue(document.parsed, "#");
76
+ enqueue(document.parsed, ROOT_POINTER);
81
77
  for (let node = pending.pop(); node !== undefined; node = pending.pop()) {
82
78
  for (const [key, child] of Object.entries(node.value)) {
83
- const pointer = `${node.pointer}/${escapePointerSegment(key)}`;
79
+ const pointer = childPointer(node.pointer, key);
84
80
  if (isUnsupportedEntry(key, child)) {
85
81
  found.push({ key, pointer, source: document.source });
86
82
  }