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

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 (448) hide show
  1. package/CHANGELOG.md +1831 -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 +90 -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 +90 -14
  13. package/dist/adapters/node-file-system/node-file-system.mjs.map +1 -1
  14. package/dist/adapters/redocly-loader/bundled-mapping-copies.cjs +75 -0
  15. package/dist/adapters/redocly-loader/bundled-mapping-copies.cjs.map +1 -0
  16. package/dist/adapters/redocly-loader/bundled-mapping-copies.mjs +72 -0
  17. package/dist/adapters/redocly-loader/bundled-mapping-copies.mjs.map +1 -0
  18. package/dist/adapters/redocly-loader/bundled-ref-repair.cjs +1 -1
  19. package/dist/adapters/redocly-loader/bundled-ref-repair.cjs.map +1 -1
  20. package/dist/adapters/redocly-loader/bundled-ref-repair.mjs +2 -2
  21. package/dist/adapters/redocly-loader/bundled-ref-repair.mjs.map +1 -1
  22. package/dist/adapters/redocly-loader/loaded-documents.cjs +28 -0
  23. package/dist/adapters/redocly-loader/loaded-documents.cjs.map +1 -0
  24. package/dist/adapters/redocly-loader/loaded-documents.mjs +21 -0
  25. package/dist/adapters/redocly-loader/loaded-documents.mjs.map +1 -0
  26. package/dist/adapters/redocly-loader/redocly-schema-loader.cjs +14 -15
  27. package/dist/adapters/redocly-loader/redocly-schema-loader.cjs.map +1 -1
  28. package/dist/adapters/redocly-loader/redocly-schema-loader.mjs +12 -13
  29. package/dist/adapters/redocly-loader/redocly-schema-loader.mjs.map +1 -1
  30. package/dist/adapters/redocly-loader/ref-access.cjs +31 -3
  31. package/dist/adapters/redocly-loader/ref-access.cjs.map +1 -1
  32. package/dist/adapters/redocly-loader/ref-access.mjs +31 -3
  33. package/dist/adapters/redocly-loader/ref-access.mjs.map +1 -1
  34. package/dist/adapters/redocly-loader/ref-holders.cjs +2 -2
  35. package/dist/adapters/redocly-loader/ref-holders.cjs.map +1 -1
  36. package/dist/adapters/redocly-loader/ref-holders.mjs +3 -3
  37. package/dist/adapters/redocly-loader/ref-holders.mjs.map +1 -1
  38. package/dist/adapters/redocly-loader/remote-body-limit.cjs +50 -0
  39. package/dist/adapters/redocly-loader/remote-body-limit.cjs.map +1 -0
  40. package/dist/adapters/redocly-loader/remote-body-limit.mjs +48 -0
  41. package/dist/adapters/redocly-loader/remote-body-limit.mjs.map +1 -0
  42. package/dist/adapters/redocly-loader/remote-fetch.cjs +35 -6
  43. package/dist/adapters/redocly-loader/remote-fetch.cjs.map +1 -1
  44. package/dist/adapters/redocly-loader/remote-fetch.mjs +35 -6
  45. package/dist/adapters/redocly-loader/remote-fetch.mjs.map +1 -1
  46. package/dist/adapters/redocly-loader/remote-node-limit.cjs +47 -0
  47. package/dist/adapters/redocly-loader/remote-node-limit.cjs.map +1 -0
  48. package/dist/adapters/redocly-loader/remote-node-limit.mjs +44 -0
  49. package/dist/adapters/redocly-loader/remote-node-limit.mjs.map +1 -0
  50. package/dist/adapters/redocly-loader/schema-load-errors.cjs +32 -8
  51. package/dist/adapters/redocly-loader/schema-load-errors.cjs.map +1 -1
  52. package/dist/adapters/redocly-loader/schema-load-errors.mjs +32 -9
  53. package/dist/adapters/redocly-loader/schema-load-errors.mjs.map +1 -1
  54. package/dist/adapters/redocly-loader/schema-source.cjs +11 -7
  55. package/dist/adapters/redocly-loader/schema-source.cjs.map +1 -1
  56. package/dist/adapters/redocly-loader/schema-source.mjs +12 -8
  57. package/dist/adapters/redocly-loader/schema-source.mjs.map +1 -1
  58. package/dist/adapters/redocly-loader/unsupported-keys.cjs +3 -5
  59. package/dist/adapters/redocly-loader/unsupported-keys.cjs.map +1 -1
  60. package/dist/adapters/redocly-loader/unsupported-keys.mjs +3 -5
  61. package/dist/adapters/redocly-loader/unsupported-keys.mjs.map +1 -1
  62. package/dist/adapters/terminal-logger/terminal-logger.cjs +6 -4
  63. package/dist/adapters/terminal-logger/terminal-logger.cjs.map +1 -1
  64. package/dist/adapters/terminal-logger/terminal-logger.mjs +6 -4
  65. package/dist/adapters/terminal-logger/terminal-logger.mjs.map +1 -1
  66. package/dist/api/default-ports.cjs +14 -2
  67. package/dist/api/default-ports.cjs.map +1 -1
  68. package/dist/api/default-ports.mjs +14 -2
  69. package/dist/api/default-ports.mjs.map +1 -1
  70. package/dist/api/discriminator-enum-parent.cjs +2 -2
  71. package/dist/api/discriminator-enum-parent.cjs.map +1 -1
  72. package/dist/api/discriminator-enum-parent.mjs +3 -3
  73. package/dist/api/discriminator-enum-parent.mjs.map +1 -1
  74. package/dist/api/discriminator-other-owners.cjs +5 -3
  75. package/dist/api/discriminator-other-owners.cjs.map +1 -1
  76. package/dist/api/discriminator-other-owners.mjs +5 -3
  77. package/dist/api/discriminator-other-owners.mjs.map +1 -1
  78. package/dist/api/discriminator-patch.cjs +5 -1
  79. package/dist/api/discriminator-patch.cjs.map +1 -1
  80. package/dist/api/discriminator-patch.mjs +5 -1
  81. package/dist/api/discriminator-patch.mjs.map +1 -1
  82. package/dist/api/discriminator-scan.cjs +15 -8
  83. package/dist/api/discriminator-scan.cjs.map +1 -1
  84. package/dist/api/discriminator-scan.mjs +15 -8
  85. package/dist/api/discriminator-scan.mjs.map +1 -1
  86. package/dist/api/document-run.cjs +2 -2
  87. package/dist/api/document-run.cjs.map +1 -1
  88. package/dist/api/document-run.mjs +2 -2
  89. package/dist/api/document-run.mjs.map +1 -1
  90. package/dist/api/legacy-utils.cjs.map +1 -1
  91. package/dist/api/legacy-utils.mjs.map +1 -1
  92. package/dist/api/openapi-ts.cjs +8 -4
  93. package/dist/api/openapi-ts.cjs.map +1 -1
  94. package/dist/api/openapi-ts.mjs +8 -5
  95. package/dist/api/openapi-ts.mjs.map +1 -1
  96. package/dist/api/schema-callbacks.cjs +36 -12
  97. package/dist/api/schema-callbacks.cjs.map +1 -1
  98. package/dist/api/schema-callbacks.mjs +36 -12
  99. package/dist/api/schema-callbacks.mjs.map +1 -1
  100. package/dist/api/transform-context.cjs +7 -3
  101. package/dist/api/transform-context.cjs.map +1 -1
  102. package/dist/api/transform-context.mjs +7 -3
  103. package/dist/api/transform-context.mjs.map +1 -1
  104. package/dist/api/transform-schema.cjs +40 -10
  105. package/dist/api/transform-schema.cjs.map +1 -1
  106. package/dist/api/transform-schema.mjs +41 -11
  107. package/dist/api/transform-schema.mjs.map +1 -1
  108. package/dist/application/print/text-writer.cjs +10 -3
  109. package/dist/application/print/text-writer.cjs.map +1 -1
  110. package/dist/application/print/text-writer.mjs +10 -3
  111. package/dist/application/print/text-writer.mjs.map +1 -1
  112. package/dist/application/stack-overflow.cjs +2 -1
  113. package/dist/application/stack-overflow.cjs.map +1 -1
  114. package/dist/application/stack-overflow.mjs +2 -1
  115. package/dist/application/stack-overflow.mjs.map +1 -1
  116. package/dist/application/transform/document/components.cjs +1 -1
  117. package/dist/application/transform/document/components.cjs.map +1 -1
  118. package/dist/application/transform/document/components.mjs +2 -2
  119. package/dist/application/transform/document/components.mjs.map +1 -1
  120. package/dist/application/transform/document/declared-type-names.cjs +23 -0
  121. package/dist/application/transform/document/declared-type-names.cjs.map +1 -0
  122. package/dist/application/transform/document/declared-type-names.mjs +20 -0
  123. package/dist/application/transform/document/declared-type-names.mjs.map +1 -0
  124. package/dist/application/transform/document/defs-root.cjs +1 -1
  125. package/dist/application/transform/document/defs-root.cjs.map +1 -1
  126. package/dist/application/transform/document/defs-root.mjs +2 -2
  127. package/dist/application/transform/document/defs-root.mjs.map +1 -1
  128. package/dist/application/transform/document/document.cjs +4 -1
  129. package/dist/application/transform/document/document.cjs.map +1 -1
  130. package/dist/application/transform/document/document.mjs +3 -2
  131. package/dist/application/transform/document/document.mjs.map +1 -1
  132. package/dist/application/transform/document/enum-names-given-back.cjs +62 -0
  133. package/dist/application/transform/document/enum-names-given-back.cjs.map +1 -0
  134. package/dist/application/transform/document/enum-names-given-back.mjs +58 -0
  135. package/dist/application/transform/document/enum-names-given-back.mjs.map +1 -0
  136. package/dist/application/transform/document/global-type-references.cjs +1 -0
  137. package/dist/application/transform/document/global-type-references.cjs.map +1 -1
  138. package/dist/application/transform/document/global-type-references.mjs +1 -1
  139. package/dist/application/transform/document/global-type-references.mjs.map +1 -1
  140. package/dist/application/transform/document/media-type.cjs +2 -2
  141. package/dist/application/transform/document/media-type.cjs.map +1 -1
  142. package/dist/application/transform/document/media-type.mjs +3 -3
  143. package/dist/application/transform/document/media-type.mjs.map +1 -1
  144. package/dist/application/transform/document/operation-keys-written.cjs +26 -0
  145. package/dist/application/transform/document/operation-keys-written.cjs.map +1 -0
  146. package/dist/application/transform/document/operation-keys-written.mjs +24 -0
  147. package/dist/application/transform/document/operation-keys-written.mjs.map +1 -0
  148. package/dist/application/transform/document/operation.cjs +1 -1
  149. package/dist/application/transform/document/operation.cjs.map +1 -1
  150. package/dist/application/transform/document/operation.mjs +2 -2
  151. package/dist/application/transform/document/operation.mjs.map +1 -1
  152. package/dist/application/transform/document/parameter-entries.cjs +34 -0
  153. package/dist/application/transform/document/parameter-entries.cjs.map +1 -0
  154. package/dist/application/transform/document/parameter-entries.mjs +30 -0
  155. package/dist/application/transform/document/parameter-entries.mjs.map +1 -0
  156. package/dist/application/transform/document/parameter.cjs +12 -4
  157. package/dist/application/transform/document/parameter.cjs.map +1 -1
  158. package/dist/application/transform/document/parameter.mjs +13 -5
  159. package/dist/application/transform/document/parameter.mjs.map +1 -1
  160. package/dist/application/transform/document/parameters.cjs +6 -29
  161. package/dist/application/transform/document/parameters.cjs.map +1 -1
  162. package/dist/application/transform/document/parameters.mjs +6 -29
  163. package/dist/application/transform/document/parameters.mjs.map +1 -1
  164. package/dist/application/transform/document/path-item.cjs +11 -14
  165. package/dist/application/transform/document/path-item.cjs.map +1 -1
  166. package/dist/application/transform/document/path-item.mjs +12 -16
  167. package/dist/application/transform/document/path-item.mjs.map +1 -1
  168. package/dist/application/transform/document/paths-enum.cjs +1 -0
  169. package/dist/application/transform/document/paths-enum.cjs.map +1 -1
  170. package/dist/application/transform/document/paths-enum.mjs +1 -1
  171. package/dist/application/transform/document/paths-enum.mjs.map +1 -1
  172. package/dist/application/transform/document/paths.cjs +5 -1
  173. package/dist/application/transform/document/paths.cjs.map +1 -1
  174. package/dist/application/transform/document/paths.mjs +6 -2
  175. package/dist/application/transform/document/paths.mjs.map +1 -1
  176. package/dist/application/transform/document/referenced-parameter.cjs +80 -0
  177. package/dist/application/transform/document/referenced-parameter.cjs.map +1 -0
  178. package/dist/application/transform/document/referenced-parameter.mjs +78 -0
  179. package/dist/application/transform/document/referenced-parameter.mjs.map +1 -0
  180. package/dist/application/transform/document/request-body.cjs +1 -1
  181. package/dist/application/transform/document/request-body.cjs.map +1 -1
  182. package/dist/application/transform/document/request-body.mjs +2 -2
  183. package/dist/application/transform/document/request-body.mjs.map +1 -1
  184. package/dist/application/transform/document/response.cjs +2 -2
  185. package/dist/application/transform/document/response.cjs.map +1 -1
  186. package/dist/application/transform/document/response.mjs +3 -3
  187. package/dist/application/transform/document/response.mjs.map +1 -1
  188. package/dist/application/transform/document/responses.cjs +1 -1
  189. package/dist/application/transform/document/responses.cjs.map +1 -1
  190. package/dist/application/transform/document/responses.mjs +2 -2
  191. package/dist/application/transform/document/responses.mjs.map +1 -1
  192. package/dist/application/transform/document/root-type-aliases.cjs +8 -23
  193. package/dist/application/transform/document/root-type-aliases.cjs.map +1 -1
  194. package/dist/application/transform/document/root-type-aliases.mjs +8 -24
  195. package/dist/application/transform/document/root-type-aliases.mjs.map +1 -1
  196. package/dist/application/transform/document/type-resolution.cjs +3 -1
  197. package/dist/application/transform/document/type-resolution.cjs.map +1 -1
  198. package/dist/application/transform/document/type-resolution.mjs +3 -1
  199. package/dist/application/transform/document/type-resolution.mjs.map +1 -1
  200. package/dist/application/transform/document/webhooks.cjs +1 -1
  201. package/dist/application/transform/document/webhooks.cjs.map +1 -1
  202. package/dist/application/transform/document/webhooks.mjs +2 -2
  203. package/dist/application/transform/document/webhooks.mjs.map +1 -1
  204. package/dist/application/transform/footer/document-footer.cjs +10 -2
  205. package/dist/application/transform/footer/document-footer.cjs.map +1 -1
  206. package/dist/application/transform/footer/document-footer.mjs +10 -2
  207. package/dist/application/transform/footer/document-footer.mjs.map +1 -1
  208. package/dist/application/transform/footer/enum-name-claims.cjs +48 -0
  209. package/dist/application/transform/footer/enum-name-claims.cjs.map +1 -0
  210. package/dist/application/transform/footer/enum-name-claims.mjs +46 -0
  211. package/dist/application/transform/footer/enum-name-claims.mjs.map +1 -0
  212. package/dist/application/transform/footer/operation-keys.cjs +23 -0
  213. package/dist/application/transform/footer/operation-keys.cjs.map +1 -0
  214. package/dist/application/transform/footer/operation-keys.mjs +21 -0
  215. package/dist/application/transform/footer/operation-keys.mjs.map +1 -0
  216. package/dist/application/transform/footer/type-evaluation.cjs +4 -4
  217. package/dist/application/transform/footer/type-evaluation.cjs.map +1 -1
  218. package/dist/application/transform/footer/type-evaluation.mjs +4 -4
  219. package/dist/application/transform/footer/type-evaluation.mjs.map +1 -1
  220. package/dist/application/transform/helper-types.cjs +1 -1
  221. package/dist/application/transform/helper-types.cjs.map +1 -1
  222. package/dist/application/transform/helper-types.mjs +1 -1
  223. package/dist/application/transform/helper-types.mjs.map +1 -1
  224. package/dist/application/transform/numbered-names.cjs +9 -0
  225. package/dist/application/transform/numbered-names.cjs.map +1 -1
  226. package/dist/application/transform/numbered-names.mjs +9 -1
  227. package/dist/application/transform/numbered-names.mjs.map +1 -1
  228. package/dist/application/transform/ref-pointer/generated-location.cjs +1 -1
  229. package/dist/application/transform/ref-pointer/generated-location.cjs.map +1 -1
  230. package/dist/application/transform/ref-pointer/generated-location.mjs +2 -2
  231. package/dist/application/transform/ref-pointer/generated-location.mjs.map +1 -1
  232. package/dist/application/transform/ref-type.cjs +2 -1
  233. package/dist/application/transform/ref-type.cjs.map +1 -1
  234. package/dist/application/transform/ref-type.mjs +2 -1
  235. package/dist/application/transform/ref-type.mjs.map +1 -1
  236. package/dist/application/transform/schema/composition-type.cjs +1 -1
  237. package/dist/application/transform/schema/composition-type.cjs.map +1 -1
  238. package/dist/application/transform/schema/composition-type.mjs +2 -2
  239. package/dist/application/transform/schema/composition-type.mjs.map +1 -1
  240. package/dist/application/transform/schema/enum-rule.cjs +1 -1
  241. package/dist/application/transform/schema/enum-rule.cjs.map +1 -1
  242. package/dist/application/transform/schema/enum-rule.mjs +2 -2
  243. package/dist/application/transform/schema/enum-rule.mjs.map +1 -1
  244. package/dist/application/transform/schema/object-type.cjs +3 -3
  245. package/dist/application/transform/schema/object-type.cjs.map +1 -1
  246. package/dist/application/transform/schema/object-type.mjs +4 -4
  247. package/dist/application/transform/schema/object-type.mjs.map +1 -1
  248. package/dist/application/transform/schema/ref-siblings.cjs +47 -0
  249. package/dist/application/transform/schema/ref-siblings.cjs.map +1 -0
  250. package/dist/application/transform/schema/ref-siblings.mjs +45 -0
  251. package/dist/application/transform/schema/ref-siblings.mjs.map +1 -0
  252. package/dist/application/transform/schema/schema-transform.cjs +4 -2
  253. package/dist/application/transform/schema/schema-transform.cjs.map +1 -1
  254. package/dist/application/transform/schema/schema-transform.mjs +4 -2
  255. package/dist/application/transform/schema/schema-transform.mjs.map +1 -1
  256. package/dist/application/transform/schema-comment.cjs +5 -1
  257. package/dist/application/transform/schema-comment.cjs.map +1 -1
  258. package/dist/application/transform/schema-comment.mjs +5 -1
  259. package/dist/application/transform/schema-comment.mjs.map +1 -1
  260. package/dist/cli/api-settings.cjs +62 -6
  261. package/dist/cli/api-settings.cjs.map +1 -1
  262. package/dist/cli/api-settings.mjs +60 -7
  263. package/dist/cli/api-settings.mjs.map +1 -1
  264. package/dist/cli/api-writes.cjs +72 -0
  265. package/dist/cli/api-writes.cjs.map +1 -0
  266. package/dist/cli/api-writes.mjs +69 -0
  267. package/dist/cli/api-writes.mjs.map +1 -0
  268. package/dist/cli/cli-errors.cjs +12 -1
  269. package/dist/cli/cli-errors.cjs.map +1 -1
  270. package/dist/cli/cli-errors.mjs +12 -1
  271. package/dist/cli/cli-errors.mjs.map +1 -1
  272. package/dist/cli/cli-output.cjs +106 -1
  273. package/dist/cli/cli-output.cjs.map +1 -1
  274. package/dist/cli/cli-output.mjs +99 -2
  275. package/dist/cli/cli-output.mjs.map +1 -1
  276. package/dist/cli/main.cjs +5 -1
  277. package/dist/cli/main.cjs.map +1 -1
  278. package/dist/cli/main.mjs +6 -2
  279. package/dist/cli/main.mjs.map +1 -1
  280. package/dist/cli/redocly-apis.cjs +82 -36
  281. package/dist/cli/redocly-apis.cjs.map +1 -1
  282. package/dist/cli/redocly-apis.mjs +85 -39
  283. package/dist/cli/redocly-apis.mjs.map +1 -1
  284. package/dist/cli/run-cli.cjs +13 -7
  285. package/dist/cli/run-cli.cjs.map +1 -1
  286. package/dist/cli/run-cli.mjs +14 -8
  287. package/dist/cli/run-cli.mjs.map +1 -1
  288. package/dist/domain/append-all.cjs +10 -0
  289. package/dist/domain/append-all.cjs.map +1 -0
  290. package/dist/domain/append-all.mjs +8 -0
  291. package/dist/domain/append-all.mjs.map +1 -0
  292. package/dist/domain/cause-secrets.cjs +37 -0
  293. package/dist/domain/cause-secrets.cjs.map +1 -0
  294. package/dist/domain/cause-secrets.mjs +35 -0
  295. package/dist/domain/cause-secrets.mjs.map +1 -0
  296. package/dist/domain/errors.cjs +9 -3
  297. package/dist/domain/errors.cjs.map +1 -1
  298. package/dist/domain/errors.mjs +9 -3
  299. package/dist/domain/errors.mjs.map +1 -1
  300. package/dist/domain/ir/identifier-text.cjs.map +1 -1
  301. package/dist/domain/ir/identifier-text.mjs +1 -1
  302. package/dist/domain/ir/identifier-text.mjs.map +1 -1
  303. package/dist/domain/ir/ir-shape.cjs +187 -0
  304. package/dist/domain/ir/ir-shape.cjs.map +1 -0
  305. package/dist/domain/ir/ir-shape.mjs +184 -0
  306. package/dist/domain/ir/ir-shape.mjs.map +1 -0
  307. package/dist/domain/ir/newer-identifier-ranges.cjs +182 -29
  308. package/dist/domain/ir/newer-identifier-ranges.cjs.map +1 -1
  309. package/dist/domain/ir/newer-identifier-ranges.mjs +182 -29
  310. package/dist/domain/ir/newer-identifier-ranges.mjs.map +1 -1
  311. package/dist/domain/openapi/alias-expansion.cjs +3 -1
  312. package/dist/domain/openapi/alias-expansion.cjs.map +1 -1
  313. package/dist/domain/openapi/alias-expansion.mjs +3 -1
  314. package/dist/domain/openapi/alias-expansion.mjs.map +1 -1
  315. package/dist/domain/openapi/discriminated-union.cjs +3 -3
  316. package/dist/domain/openapi/discriminated-union.cjs.map +1 -1
  317. package/dist/domain/openapi/discriminated-union.mjs +4 -4
  318. package/dist/domain/openapi/discriminated-union.mjs.map +1 -1
  319. package/dist/domain/openapi/discriminator-conflicts.cjs +10 -6
  320. package/dist/domain/openapi/discriminator-conflicts.cjs.map +1 -1
  321. package/dist/domain/openapi/discriminator-conflicts.mjs +11 -7
  322. package/dist/domain/openapi/discriminator-conflicts.mjs.map +1 -1
  323. package/dist/domain/openapi/json-pointer.cjs +43 -1
  324. package/dist/domain/openapi/json-pointer.cjs.map +1 -1
  325. package/dist/domain/openapi/json-pointer.mjs +40 -2
  326. package/dist/domain/openapi/json-pointer.mjs.map +1 -1
  327. package/dist/domain/openapi/schema-dialect.cjs +15 -0
  328. package/dist/domain/openapi/schema-dialect.cjs.map +1 -0
  329. package/dist/domain/openapi/schema-dialect.mjs +12 -0
  330. package/dist/domain/openapi/schema-dialect.mjs.map +1 -0
  331. package/dist/domain/openapi/unsupported-features.cjs +1 -1
  332. package/dist/domain/openapi/unsupported-features.cjs.map +1 -1
  333. package/dist/domain/openapi/unsupported-features.mjs +2 -2
  334. package/dist/domain/openapi/unsupported-features.mjs.map +1 -1
  335. package/dist/domain/output-fixes.cjs +14 -0
  336. package/dist/domain/output-fixes.cjs.map +1 -1
  337. package/dist/domain/output-fixes.mjs +14 -0
  338. package/dist/domain/output-fixes.mjs.map +1 -1
  339. package/dist/domain/url-secrets.cjs +48 -0
  340. package/dist/domain/url-secrets.cjs.map +1 -0
  341. package/dist/domain/url-secrets.mjs +45 -0
  342. package/dist/domain/url-secrets.mjs.map +1 -0
  343. package/dist/index.d.cts +22 -4
  344. package/dist/index.d.mts +22 -4
  345. package/dist/index.d.ts +22 -4
  346. package/package.json +4 -5
  347. package/src/adapters/node-file-system/file-claims.ts +138 -0
  348. package/src/adapters/node-file-system/file-identity.ts +137 -0
  349. package/src/adapters/node-file-system/node-file-system.ts +145 -18
  350. package/src/adapters/redocly-loader/bundled-mapping-copies.ts +95 -0
  351. package/src/adapters/redocly-loader/bundled-ref-repair.ts +2 -2
  352. package/src/adapters/redocly-loader/loaded-documents.ts +46 -0
  353. package/src/adapters/redocly-loader/redocly-schema-loader.ts +18 -24
  354. package/src/adapters/redocly-loader/ref-access.ts +37 -3
  355. package/src/adapters/redocly-loader/ref-holders.ts +3 -3
  356. package/src/adapters/redocly-loader/remote-body-limit.ts +66 -0
  357. package/src/adapters/redocly-loader/remote-fetch.ts +74 -11
  358. package/src/adapters/redocly-loader/remote-node-limit.ts +62 -0
  359. package/src/adapters/redocly-loader/schema-load-errors.ts +52 -13
  360. package/src/adapters/redocly-loader/schema-source.ts +20 -8
  361. package/src/adapters/redocly-loader/unsupported-keys.ts +3 -7
  362. package/src/adapters/terminal-logger/terminal-logger.ts +10 -5
  363. package/src/api/default-ports.ts +33 -4
  364. package/src/api/discriminator-enum-parent.ts +3 -3
  365. package/src/api/discriminator-other-owners.ts +5 -3
  366. package/src/api/discriminator-patch.ts +5 -1
  367. package/src/api/discriminator-scan.ts +16 -8
  368. package/src/api/document-run.ts +4 -3
  369. package/src/api/legacy-utils.ts +5 -1
  370. package/src/api/openapi-ts.ts +20 -5
  371. package/src/api/run-context.ts +3 -0
  372. package/src/api/schema-callbacks.ts +60 -17
  373. package/src/api/transform-context.ts +7 -3
  374. package/src/api/transform-schema.ts +64 -12
  375. package/src/application/ports/file-system.ts +39 -0
  376. package/src/application/ports/schema-loader.ts +11 -1
  377. package/src/application/print/text-writer.ts +10 -3
  378. package/src/application/stack-overflow.ts +9 -5
  379. package/src/application/transform/document/components.ts +2 -2
  380. package/src/application/transform/document/declared-type-names.ts +36 -0
  381. package/src/application/transform/document/defs-root.ts +2 -2
  382. package/src/application/transform/document/document.ts +4 -3
  383. package/src/application/transform/document/enum-names-given-back.ts +91 -0
  384. package/src/application/transform/document/global-type-references.ts +1 -1
  385. package/src/application/transform/document/media-type.ts +4 -3
  386. package/src/application/transform/document/operation-keys-written.ts +27 -0
  387. package/src/application/transform/document/operation.ts +2 -2
  388. package/src/application/transform/document/parameter-entries.ts +70 -0
  389. package/src/application/transform/document/parameter.ts +21 -9
  390. package/src/application/transform/document/parameters.ts +11 -56
  391. package/src/application/transform/document/path-item.ts +13 -31
  392. package/src/application/transform/document/paths-enum.ts +1 -1
  393. package/src/application/transform/document/paths.ts +6 -2
  394. package/src/application/transform/document/referenced-parameter.ts +146 -0
  395. package/src/application/transform/document/request-body.ts +2 -2
  396. package/src/application/transform/document/response.ts +3 -3
  397. package/src/application/transform/document/responses.ts +2 -2
  398. package/src/application/transform/document/root-type-aliases.ts +17 -38
  399. package/src/application/transform/document/type-resolution.ts +2 -1
  400. package/src/application/transform/document/webhooks.ts +2 -2
  401. package/src/application/transform/footer/document-footer.ts +18 -1
  402. package/src/application/transform/footer/enum-name-claims.ts +62 -0
  403. package/src/application/transform/footer/operation-keys.ts +28 -0
  404. package/src/application/transform/footer/type-evaluation.ts +4 -4
  405. package/src/application/transform/helper-types.ts +6 -5
  406. package/src/application/transform/numbered-names.ts +15 -0
  407. package/src/application/transform/ref-pointer/generated-location.ts +2 -2
  408. package/src/application/transform/ref-type.ts +2 -1
  409. package/src/application/transform/schema/composition-type.ts +2 -2
  410. package/src/application/transform/schema/enum-rule.ts +6 -2
  411. package/src/application/transform/schema/object-type.ts +4 -4
  412. package/src/application/transform/schema/ref-siblings.ts +76 -0
  413. package/src/application/transform/schema/schema-context.ts +3 -0
  414. package/src/application/transform/schema/schema-transform.ts +4 -2
  415. package/src/application/transform/schema-comment.ts +5 -1
  416. package/src/application/transform/transform-context.ts +5 -0
  417. package/src/cli/api-settings.ts +82 -7
  418. package/src/cli/api-writes.ts +117 -0
  419. package/src/cli/cli-dependencies.ts +21 -4
  420. package/src/cli/cli-errors.ts +20 -1
  421. package/src/cli/cli-output.ts +186 -7
  422. package/src/cli/main.ts +6 -3
  423. package/src/cli/redocly-apis.ts +153 -48
  424. package/src/cli/run-cli.ts +37 -8
  425. package/src/domain/append-all.ts +10 -0
  426. package/src/domain/cause-secrets.ts +52 -0
  427. package/src/domain/errors.ts +14 -5
  428. package/src/domain/ir/identifier-text.ts +6 -5
  429. package/src/domain/ir/ir-shape.ts +299 -0
  430. package/src/domain/ir/newer-identifier-ranges.ts +183 -30
  431. package/src/domain/openapi/alias-expansion.ts +4 -1
  432. package/src/domain/openapi/discriminated-union.ts +6 -4
  433. package/src/domain/openapi/discriminator-conflicts.ts +21 -7
  434. package/src/domain/openapi/document-object.ts +2 -0
  435. package/src/domain/openapi/json-pointer.ts +96 -7
  436. package/src/domain/openapi/schema-dialect.ts +22 -0
  437. package/src/domain/openapi/unsupported-features.ts +2 -2
  438. package/src/domain/output-fixes.ts +21 -2
  439. package/src/domain/url-secrets.ts +72 -0
  440. package/src/types/generator-options.ts +1 -1
  441. package/dist/cli/main.d.cts +0 -2
  442. package/dist/cli/main.d.mts +0 -2
  443. package/dist/cli/main.d.ts +0 -2
  444. package/dist/domain/url-credentials.cjs +0 -10
  445. package/dist/domain/url-credentials.cjs.map +0 -1
  446. package/dist/domain/url-credentials.mjs +0 -8
  447. package/dist/domain/url-credentials.mjs.map +0 -1
  448. package/src/domain/url-credentials.ts +0 -18
@@ -0,0 +1,105 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs');
4
+ const path = require('node:path');
5
+ const fileIdentity = require('./file-identity.cjs');
6
+
7
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
8
+
9
+ const fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
10
+ const path__default = /*#__PURE__*/_interopDefaultCompat(path);
11
+
12
+ function missingDirectories(directory, exists) {
13
+ const missing = [];
14
+ for (let current = directory; !exists(current); current = path__default.dirname(current)) {
15
+ missing.push(current);
16
+ if (path__default.dirname(current) === current) {
17
+ break;
18
+ }
19
+ }
20
+ return missing;
21
+ }
22
+ function claimIdentity(number, storedPath) {
23
+ return number.file === 0n ? `path:${storedPath()}` : `${number.device}:${number.file}`;
24
+ }
25
+ function identityOf(descriptor, filePath) {
26
+ const status = fs__default.fstatSync(descriptor, { bigint: true });
27
+ return claimIdentity(
28
+ { device: status.dev, file: status.ino },
29
+ () => fs__default.realpathSync.native(filePath)
30
+ );
31
+ }
32
+ function errorCodeOf(error) {
33
+ return error instanceof Error && "code" in error ? error.code : void 0;
34
+ }
35
+ function removeDirectories(directories) {
36
+ for (const directory of directories) {
37
+ try {
38
+ fs__default.rmdirSync(directory);
39
+ } catch (error) {
40
+ const code = errorCodeOf(error);
41
+ if (code === "ENOTEMPTY" || code === "EEXIST") {
42
+ return;
43
+ }
44
+ if (code !== "ENOENT" || fs__default.lstatSync(directory, { throwIfNoEntry: false }) !== void 0) {
45
+ throw error;
46
+ }
47
+ }
48
+ }
49
+ }
50
+ function createDirectory(directory, queries) {
51
+ const target = fileIdentity.creationPath(directory, queries);
52
+ const missing = missingDirectories(target, fs__default.existsSync);
53
+ try {
54
+ fs__default.mkdirSync(target, { recursive: true });
55
+ } catch (error) {
56
+ removeDirectories(missing.filter((created) => fs__default.existsSync(created)));
57
+ throw error;
58
+ }
59
+ return missing;
60
+ }
61
+ function openClaim(filePath) {
62
+ const isCreated = !fs__default.existsSync(filePath);
63
+ const descriptor = fs__default.openSync(filePath, "a");
64
+ try {
65
+ const identity = identityOf(descriptor, filePath);
66
+ return { identity, createdFile: isCreated ? fs__default.realpathSync.native(filePath) : void 0 };
67
+ } finally {
68
+ fs__default.closeSync(descriptor);
69
+ }
70
+ }
71
+ function claimFileAt(filePath, queries) {
72
+ const directories = createDirectory(path__default.dirname(filePath), queries);
73
+ let opened;
74
+ try {
75
+ opened = openClaim(filePath);
76
+ } catch (error) {
77
+ removeDirectories(directories);
78
+ throw error;
79
+ }
80
+ const { identity, createdFile } = opened;
81
+ return {
82
+ identity,
83
+ release: () => {
84
+ if (createdFile !== void 0) {
85
+ fs__default.rmSync(createdFile, { force: true });
86
+ }
87
+ removeDirectories(directories);
88
+ }
89
+ };
90
+ }
91
+ function writeFileAt(filePath, contents, queries) {
92
+ const directories = createDirectory(path__default.dirname(filePath), queries);
93
+ try {
94
+ fs__default.writeFileSync(filePath, contents, "utf8");
95
+ } catch (error) {
96
+ removeDirectories(directories);
97
+ throw error;
98
+ }
99
+ }
100
+
101
+ exports.claimFileAt = claimFileAt;
102
+ exports.claimIdentity = claimIdentity;
103
+ exports.missingDirectories = missingDirectories;
104
+ exports.writeFileAt = writeFileAt;
105
+ //# sourceMappingURL=file-claims.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-claims.cjs","sources":["../../../src/adapters/node-file-system/file-claims.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { ClaimedFile } from \"../../application/ports/file-system.js\";\nimport { creationPath, type FileNumber, type PathQueries } from \"./file-identity.js\";\n\n/**\n * Claiming an output on Node's `fs` (ADR-043, decision 1): the file is opened for appending,\n * which creates it when it is missing (also through a symbolic link) and never truncates it.\n * The device and file number of the open file are its identity. A claim or a write that fails\n * removes the directories it created.\n */\n\n/**\n * The directories that do not exist yet on the way to `directory`, deepest first: the ones a\n * recursive `fs.mkdirSync` creates. They are listed before it runs, in the form of `directory`\n * itself: on Windows the path that `fs.mkdirSync` returns did not match that form, so comparing\n * with it found no new directory and a failed run left them behind.\n */\nexport function missingDirectories(\n directory: string,\n exists: (candidate: string) => boolean,\n): string[] {\n const missing: string[] = [];\n for (let current = directory; !exists(current); current = path.dirname(current)) {\n missing.push(current);\n if (path.dirname(current) === current) {\n break;\n }\n }\n return missing;\n}\n\n/**\n * The identity of an open file: its device and file number, or its stored path on a file system\n * that reports the file number 0 (FAT, some network shares), which proves nothing.\n */\nexport function claimIdentity(number: FileNumber, storedPath: () => string): string {\n return number.file === 0n ? `path:${storedPath()}` : `${number.device}:${number.file}`;\n}\n\nfunction identityOf(descriptor: number, filePath: string): string {\n const status = fs.fstatSync(descriptor, { bigint: true });\n return claimIdentity({ device: status.dev, file: status.ino }, () =>\n fs.realpathSync.native(filePath),\n );\n}\n\nfunction errorCodeOf(error: unknown): unknown {\n return error instanceof Error && \"code\" in error ? error.code : undefined;\n}\n\n/** Remove the new directories, deepest first, until one holds another file. */\nfunction removeDirectories(directories: readonly string[]): void {\n for (const directory of directories) {\n try {\n fs.rmdirSync(directory);\n } catch (error) {\n const code = errorCodeOf(error);\n // Another output or the user put a file there since: the directory and its parents stay.\n if (code === \"ENOTEMPTY\" || code === \"EEXIST\") {\n return;\n }\n // Removed since: nothing to remove, but its parents may still be empty. Windows also gives\n // ENOENT for a file put in its place, which must fail like any other path it cannot remove.\n if (code !== \"ENOENT\" || fs.lstatSync(directory, { throwIfNoEntry: false }) !== undefined) {\n throw error;\n }\n }\n }\n}\n\n/**\n * Create `directory` and give the directories that this created. Through a link to a missing\n * directory it creates the link's target, as a write through the link would, so the path to\n * create and the new directories are real paths, where links are already followed. A recursive\n * `mkdir` that fails part of the way (a directory name too long for the file system) keeps the\n * directories above the failing one, so the ones that exist now are removed here.\n */\nfunction createDirectory(directory: string, queries: PathQueries): string[] {\n const target = creationPath(directory, queries);\n const missing = missingDirectories(target, fs.existsSync);\n try {\n fs.mkdirSync(target, { recursive: true });\n } catch (error) {\n removeDirectories(missing.filter((created) => fs.existsSync(created)));\n throw error;\n }\n return missing;\n}\n\n/** Open `filePath` for appending, which creates it, and give its identity and whether it is new. */\nfunction openClaim(filePath: string): { identity: string; createdFile: string | undefined } {\n const isCreated = !fs.existsSync(filePath);\n const descriptor = fs.openSync(filePath, \"a\");\n try {\n const identity = identityOf(descriptor, filePath);\n // A link to a missing file created its target: that file is what the claim removes.\n return { identity, createdFile: isCreated ? fs.realpathSync.native(filePath) : undefined };\n } finally {\n fs.closeSync(descriptor);\n }\n}\n\n/**\n * Claim the file at `filePath`; the caller turns a failure into a typed error. A claim that fails\n * after creating directories (a file name too long for the file system) removes them again.\n */\nexport function claimFileAt(filePath: string, queries: PathQueries): ClaimedFile {\n const directories = createDirectory(path.dirname(filePath), queries);\n let opened: ReturnType<typeof openClaim>;\n try {\n opened = openClaim(filePath);\n } catch (error) {\n removeDirectories(directories);\n throw error;\n }\n const { identity, createdFile } = opened;\n return {\n identity,\n release: () => {\n if (createdFile !== undefined) {\n fs.rmSync(createdFile, { force: true });\n }\n removeDirectories(directories);\n },\n };\n}\n\n/** Write `contents` to `filePath`, creating its directory; a failed write removes what it made. */\nexport function writeFileAt(filePath: string, contents: string, queries: PathQueries): void {\n const directories = createDirectory(path.dirname(filePath), queries);\n try {\n fs.writeFileSync(filePath, contents, \"utf8\");\n } catch (error) {\n removeDirectories(directories);\n throw error;\n }\n}\n"],"names":["path","fs","creationPath"],"mappings":"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"}
@@ -0,0 +1,95 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { creationPath } from './file-identity.mjs';
4
+
5
+ function missingDirectories(directory, exists) {
6
+ const missing = [];
7
+ for (let current = directory; !exists(current); current = path.dirname(current)) {
8
+ missing.push(current);
9
+ if (path.dirname(current) === current) {
10
+ break;
11
+ }
12
+ }
13
+ return missing;
14
+ }
15
+ function claimIdentity(number, storedPath) {
16
+ return number.file === 0n ? `path:${storedPath()}` : `${number.device}:${number.file}`;
17
+ }
18
+ function identityOf(descriptor, filePath) {
19
+ const status = fs.fstatSync(descriptor, { bigint: true });
20
+ return claimIdentity(
21
+ { device: status.dev, file: status.ino },
22
+ () => fs.realpathSync.native(filePath)
23
+ );
24
+ }
25
+ function errorCodeOf(error) {
26
+ return error instanceof Error && "code" in error ? error.code : void 0;
27
+ }
28
+ function removeDirectories(directories) {
29
+ for (const directory of directories) {
30
+ try {
31
+ fs.rmdirSync(directory);
32
+ } catch (error) {
33
+ const code = errorCodeOf(error);
34
+ if (code === "ENOTEMPTY" || code === "EEXIST") {
35
+ return;
36
+ }
37
+ if (code !== "ENOENT" || fs.lstatSync(directory, { throwIfNoEntry: false }) !== void 0) {
38
+ throw error;
39
+ }
40
+ }
41
+ }
42
+ }
43
+ function createDirectory(directory, queries) {
44
+ const target = creationPath(directory, queries);
45
+ const missing = missingDirectories(target, fs.existsSync);
46
+ try {
47
+ fs.mkdirSync(target, { recursive: true });
48
+ } catch (error) {
49
+ removeDirectories(missing.filter((created) => fs.existsSync(created)));
50
+ throw error;
51
+ }
52
+ return missing;
53
+ }
54
+ function openClaim(filePath) {
55
+ const isCreated = !fs.existsSync(filePath);
56
+ const descriptor = fs.openSync(filePath, "a");
57
+ try {
58
+ const identity = identityOf(descriptor, filePath);
59
+ return { identity, createdFile: isCreated ? fs.realpathSync.native(filePath) : void 0 };
60
+ } finally {
61
+ fs.closeSync(descriptor);
62
+ }
63
+ }
64
+ function claimFileAt(filePath, queries) {
65
+ const directories = createDirectory(path.dirname(filePath), queries);
66
+ let opened;
67
+ try {
68
+ opened = openClaim(filePath);
69
+ } catch (error) {
70
+ removeDirectories(directories);
71
+ throw error;
72
+ }
73
+ const { identity, createdFile } = opened;
74
+ return {
75
+ identity,
76
+ release: () => {
77
+ if (createdFile !== void 0) {
78
+ fs.rmSync(createdFile, { force: true });
79
+ }
80
+ removeDirectories(directories);
81
+ }
82
+ };
83
+ }
84
+ function writeFileAt(filePath, contents, queries) {
85
+ const directories = createDirectory(path.dirname(filePath), queries);
86
+ try {
87
+ fs.writeFileSync(filePath, contents, "utf8");
88
+ } catch (error) {
89
+ removeDirectories(directories);
90
+ throw error;
91
+ }
92
+ }
93
+
94
+ export { claimFileAt, claimIdentity, missingDirectories, writeFileAt };
95
+ //# sourceMappingURL=file-claims.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-claims.mjs","sources":["../../../src/adapters/node-file-system/file-claims.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { ClaimedFile } from \"../../application/ports/file-system.js\";\nimport { creationPath, type FileNumber, type PathQueries } from \"./file-identity.js\";\n\n/**\n * Claiming an output on Node's `fs` (ADR-043, decision 1): the file is opened for appending,\n * which creates it when it is missing (also through a symbolic link) and never truncates it.\n * The device and file number of the open file are its identity. A claim or a write that fails\n * removes the directories it created.\n */\n\n/**\n * The directories that do not exist yet on the way to `directory`, deepest first: the ones a\n * recursive `fs.mkdirSync` creates. They are listed before it runs, in the form of `directory`\n * itself: on Windows the path that `fs.mkdirSync` returns did not match that form, so comparing\n * with it found no new directory and a failed run left them behind.\n */\nexport function missingDirectories(\n directory: string,\n exists: (candidate: string) => boolean,\n): string[] {\n const missing: string[] = [];\n for (let current = directory; !exists(current); current = path.dirname(current)) {\n missing.push(current);\n if (path.dirname(current) === current) {\n break;\n }\n }\n return missing;\n}\n\n/**\n * The identity of an open file: its device and file number, or its stored path on a file system\n * that reports the file number 0 (FAT, some network shares), which proves nothing.\n */\nexport function claimIdentity(number: FileNumber, storedPath: () => string): string {\n return number.file === 0n ? `path:${storedPath()}` : `${number.device}:${number.file}`;\n}\n\nfunction identityOf(descriptor: number, filePath: string): string {\n const status = fs.fstatSync(descriptor, { bigint: true });\n return claimIdentity({ device: status.dev, file: status.ino }, () =>\n fs.realpathSync.native(filePath),\n );\n}\n\nfunction errorCodeOf(error: unknown): unknown {\n return error instanceof Error && \"code\" in error ? error.code : undefined;\n}\n\n/** Remove the new directories, deepest first, until one holds another file. */\nfunction removeDirectories(directories: readonly string[]): void {\n for (const directory of directories) {\n try {\n fs.rmdirSync(directory);\n } catch (error) {\n const code = errorCodeOf(error);\n // Another output or the user put a file there since: the directory and its parents stay.\n if (code === \"ENOTEMPTY\" || code === \"EEXIST\") {\n return;\n }\n // Removed since: nothing to remove, but its parents may still be empty. Windows also gives\n // ENOENT for a file put in its place, which must fail like any other path it cannot remove.\n if (code !== \"ENOENT\" || fs.lstatSync(directory, { throwIfNoEntry: false }) !== undefined) {\n throw error;\n }\n }\n }\n}\n\n/**\n * Create `directory` and give the directories that this created. Through a link to a missing\n * directory it creates the link's target, as a write through the link would, so the path to\n * create and the new directories are real paths, where links are already followed. A recursive\n * `mkdir` that fails part of the way (a directory name too long for the file system) keeps the\n * directories above the failing one, so the ones that exist now are removed here.\n */\nfunction createDirectory(directory: string, queries: PathQueries): string[] {\n const target = creationPath(directory, queries);\n const missing = missingDirectories(target, fs.existsSync);\n try {\n fs.mkdirSync(target, { recursive: true });\n } catch (error) {\n removeDirectories(missing.filter((created) => fs.existsSync(created)));\n throw error;\n }\n return missing;\n}\n\n/** Open `filePath` for appending, which creates it, and give its identity and whether it is new. */\nfunction openClaim(filePath: string): { identity: string; createdFile: string | undefined } {\n const isCreated = !fs.existsSync(filePath);\n const descriptor = fs.openSync(filePath, \"a\");\n try {\n const identity = identityOf(descriptor, filePath);\n // A link to a missing file created its target: that file is what the claim removes.\n return { identity, createdFile: isCreated ? fs.realpathSync.native(filePath) : undefined };\n } finally {\n fs.closeSync(descriptor);\n }\n}\n\n/**\n * Claim the file at `filePath`; the caller turns a failure into a typed error. A claim that fails\n * after creating directories (a file name too long for the file system) removes them again.\n */\nexport function claimFileAt(filePath: string, queries: PathQueries): ClaimedFile {\n const directories = createDirectory(path.dirname(filePath), queries);\n let opened: ReturnType<typeof openClaim>;\n try {\n opened = openClaim(filePath);\n } catch (error) {\n removeDirectories(directories);\n throw error;\n }\n const { identity, createdFile } = opened;\n return {\n identity,\n release: () => {\n if (createdFile !== undefined) {\n fs.rmSync(createdFile, { force: true });\n }\n removeDirectories(directories);\n },\n };\n}\n\n/** Write `contents` to `filePath`, creating its directory; a failed write removes what it made. */\nexport function writeFileAt(filePath: string, contents: string, queries: PathQueries): void {\n const directories = createDirectory(path.dirname(filePath), queries);\n try {\n fs.writeFileSync(filePath, contents, \"utf8\");\n } catch (error) {\n removeDirectories(directories);\n throw error;\n }\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,82 @@
1
+ 'use strict';
2
+
3
+ const path = require('node:path');
4
+
5
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
6
+
7
+ const path__default = /*#__PURE__*/_interopDefaultCompat(path);
8
+
9
+ const MAX_LINK_HOPS = 40;
10
+ function isSameFileNumber(left, right) {
11
+ return left.file !== 0n && left.file === right.file && left.device === right.device;
12
+ }
13
+ function hasLetters(text) {
14
+ return text.toLowerCase() !== text.toUpperCase();
15
+ }
16
+ function flipCase(text) {
17
+ return [...text].map((character) => {
18
+ const upper = character.toUpperCase();
19
+ return character === upper ? character.toLowerCase() : upper;
20
+ }).join("");
21
+ }
22
+ function isFoundInOtherCase(entryPath, queries) {
23
+ const status = queries.status(entryPath);
24
+ if (!status) {
25
+ return void 0;
26
+ }
27
+ const flipped = path__default.join(path__default.dirname(entryPath), flipCase(path__default.basename(entryPath)));
28
+ const flippedStatus = queries.status(flipped);
29
+ return flippedStatus !== void 0 && isSameFileNumber(status, flippedStatus);
30
+ }
31
+ function ignoresCase(directory, queries) {
32
+ const candidates = [
33
+ ...queries.entries(directory).filter(hasLetters).map((name) => path__default.join(directory, name)),
34
+ ...hasLetters(path__default.basename(directory)) ? [directory] : []
35
+ ];
36
+ for (const candidate of candidates) {
37
+ const answer = isFoundInOtherCase(candidate, queries);
38
+ if (answer !== void 0) {
39
+ return answer;
40
+ }
41
+ }
42
+ return false;
43
+ }
44
+ function nearestExisting(filePath, queries) {
45
+ const missing = [];
46
+ let current = path__default.resolve(filePath);
47
+ let hops = 0;
48
+ while (queries.status(current) === void 0) {
49
+ const target = hops < MAX_LINK_HOPS ? queries.linkTarget(current) : void 0;
50
+ if (target !== void 0) {
51
+ current = path__default.resolve(queries.realPath(path__default.dirname(current)), target);
52
+ hops += 1;
53
+ continue;
54
+ }
55
+ const parent = path__default.dirname(current);
56
+ if (parent === current) {
57
+ break;
58
+ }
59
+ missing.unshift(path__default.basename(current));
60
+ current = parent;
61
+ }
62
+ return [current, missing];
63
+ }
64
+ function creationPath(filePath, queries) {
65
+ const [existing, missing] = nearestExisting(filePath, queries);
66
+ return path__default.join(queries.realPath(existing), ...missing);
67
+ }
68
+ function fileIdentityKey(filePath, queries) {
69
+ const [existing, missing] = nearestExisting(filePath, queries);
70
+ const realExisting = queries.realPath(existing);
71
+ if (missing.length === 0) {
72
+ return realExisting;
73
+ }
74
+ const isCaseIgnored = ignoresCase(realExisting, queries);
75
+ const rest = isCaseIgnored ? missing.map((segment) => segment.toLowerCase()) : missing;
76
+ return path__default.join(realExisting, ...rest);
77
+ }
78
+
79
+ exports.creationPath = creationPath;
80
+ exports.fileIdentityKey = fileIdentityKey;
81
+ exports.isSameFileNumber = isSameFileNumber;
82
+ //# sourceMappingURL=file-identity.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-identity.cjs","sources":["../../../src/adapters/node-file-system/file-identity.ts"],"sourcesContent":["import path from \"node:path\";\n\n/**\n * The identity of a file path, also of one that does not exist yet (an output before the first\n * run). Two paths are one file when their keys are equal: the real path of the nearest existing\n * ancestor (links resolved, letter case as stored on disk) followed by the segments that do not\n * exist yet, compared in the letter case rules of the file system that will hold them. Equal keys\n * prove one file; different keys do not prove two (Unicode normalization, full case folding):\n * the CLI claims every output before writing to decide those (ADR-043, decision 1).\n */\n\n/** A device and a file number: one file, also through a hard link. */\nexport interface FileNumber {\n readonly device: bigint;\n readonly file: bigint;\n}\n\n/** The file-system queries the identity needs; Node's `fs` in production, a fake in tests. */\nexport interface PathQueries {\n /** The file number of a path, following links; undefined when it cannot be read. */\n status(filePath: string): FileNumber | undefined;\n /** The path with every link resolved, in the letter case stored on disk. */\n realPath(filePath: string): string;\n /** The names in a directory; empty when it cannot be listed. */\n entries(directory: string): readonly string[];\n /** The target of a symbolic link as written in it; undefined for a path that is no link. */\n linkTarget(filePath: string): string | undefined;\n}\n\n/**\n * The most links followed for one path. Linux follows at most 40 (MAXSYMLINKS) and macOS 32\n * before it fails with ELOOP; a longer chain is a loop, and the path is taken as missing.\n */\nconst MAX_LINK_HOPS = 40;\n\n/** Whether two file numbers name one file. */\nexport function isSameFileNumber(left: FileNumber, right: FileNumber): boolean {\n // Some file systems (FAT, some network shares) report no file number: 0 then proves nothing.\n return left.file !== 0n && left.file === right.file && left.device === right.device;\n}\n\nfunction hasLetters(text: string): boolean {\n return text.toLowerCase() !== text.toUpperCase();\n}\n\nfunction flipCase(text: string): string {\n return [...text]\n .map((character) => {\n const upper = character.toUpperCase();\n return character === upper ? character.toLowerCase() : upper;\n })\n .join(\"\");\n}\n\n/** Whether a directory entry is also found under its name in the other letter case. */\nfunction isFoundInOtherCase(entryPath: string, queries: PathQueries): boolean | undefined {\n const status = queries.status(entryPath);\n if (!status) {\n return undefined;\n }\n const flipped = path.join(path.dirname(entryPath), flipCase(path.basename(entryPath)));\n const flippedStatus = queries.status(flipped);\n return flippedStatus !== undefined && isSameFileNumber(status, flippedStatus);\n}\n\n/**\n * Whether new names in `directory` ignore letter case. It is asked of the file system, not\n * assumed from the OS: macOS and Windows volumes can be case-sensitive, and Linux mounts can\n * ignore case. An entry of the directory is looked up in the other case; without one with\n * letters, the directory itself is. With nothing to look up, names are taken as case-sensitive.\n */\nfunction ignoresCase(directory: string, queries: PathQueries): boolean {\n const candidates = [\n ...queries\n .entries(directory)\n .filter(hasLetters)\n .map((name) => path.join(directory, name)),\n ...(hasLetters(path.basename(directory)) ? [directory] : []),\n ];\n for (const candidate of candidates) {\n const answer = isFoundInOtherCase(candidate, queries);\n if (answer !== undefined) {\n return answer;\n }\n }\n return false;\n}\n\n/**\n * The nearest ancestor of `filePath` that exists (or the path itself), and the rest below it. A\n * link to a missing file or directory is followed to its target, which a write would create: so\n * `link/x.ts` with `link -> missing` is `missing/x.ts`. A relative target is read from the real\n * directory of the link, as the system does: with `a -> real/sub` and\n * `real/sub/link -> ../missing`, `a/link` is `real/missing`, not `missing`.\n */\nfunction nearestExisting(filePath: string, queries: PathQueries): [string, string[]] {\n const missing: string[] = [];\n let current = path.resolve(filePath);\n let hops = 0;\n while (queries.status(current) === undefined) {\n const target = hops < MAX_LINK_HOPS ? queries.linkTarget(current) : undefined;\n if (target !== undefined) {\n current = path.resolve(queries.realPath(path.dirname(current)), target);\n hops += 1;\n continue;\n }\n const parent = path.dirname(current);\n if (parent === current) {\n break;\n }\n missing.unshift(path.basename(current));\n current = parent;\n }\n return [current, missing];\n}\n\n/**\n * The path that a write to `filePath` creates, with links followed also where their target is\n * missing: `link/x.ts` with `link -> missing` is the real path of `missing/x.ts`. `mkdir` does not\n * follow a link to a missing directory on Linux and Windows, so the directories are created here.\n */\nexport function creationPath(filePath: string, queries: PathQueries): string {\n const [existing, missing] = nearestExisting(filePath, queries);\n return path.join(queries.realPath(existing), ...missing);\n}\n\n/** The key of a path: equal keys are one file, whether or not it exists yet. */\nexport function fileIdentityKey(filePath: string, queries: PathQueries): string {\n const [existing, missing] = nearestExisting(filePath, queries);\n const realExisting = queries.realPath(existing);\n if (missing.length === 0) {\n return realExisting;\n }\n const isCaseIgnored = ignoresCase(realExisting, queries);\n const rest = isCaseIgnored ? missing.map((segment) => segment.toLowerCase()) : missing;\n return path.join(realExisting, ...rest);\n}\n"],"names":["path"],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ import path from 'node:path';
2
+
3
+ const MAX_LINK_HOPS = 40;
4
+ function isSameFileNumber(left, right) {
5
+ return left.file !== 0n && left.file === right.file && left.device === right.device;
6
+ }
7
+ function hasLetters(text) {
8
+ return text.toLowerCase() !== text.toUpperCase();
9
+ }
10
+ function flipCase(text) {
11
+ return [...text].map((character) => {
12
+ const upper = character.toUpperCase();
13
+ return character === upper ? character.toLowerCase() : upper;
14
+ }).join("");
15
+ }
16
+ function isFoundInOtherCase(entryPath, queries) {
17
+ const status = queries.status(entryPath);
18
+ if (!status) {
19
+ return void 0;
20
+ }
21
+ const flipped = path.join(path.dirname(entryPath), flipCase(path.basename(entryPath)));
22
+ const flippedStatus = queries.status(flipped);
23
+ return flippedStatus !== void 0 && isSameFileNumber(status, flippedStatus);
24
+ }
25
+ function ignoresCase(directory, queries) {
26
+ const candidates = [
27
+ ...queries.entries(directory).filter(hasLetters).map((name) => path.join(directory, name)),
28
+ ...hasLetters(path.basename(directory)) ? [directory] : []
29
+ ];
30
+ for (const candidate of candidates) {
31
+ const answer = isFoundInOtherCase(candidate, queries);
32
+ if (answer !== void 0) {
33
+ return answer;
34
+ }
35
+ }
36
+ return false;
37
+ }
38
+ function nearestExisting(filePath, queries) {
39
+ const missing = [];
40
+ let current = path.resolve(filePath);
41
+ let hops = 0;
42
+ while (queries.status(current) === void 0) {
43
+ const target = hops < MAX_LINK_HOPS ? queries.linkTarget(current) : void 0;
44
+ if (target !== void 0) {
45
+ current = path.resolve(queries.realPath(path.dirname(current)), target);
46
+ hops += 1;
47
+ continue;
48
+ }
49
+ const parent = path.dirname(current);
50
+ if (parent === current) {
51
+ break;
52
+ }
53
+ missing.unshift(path.basename(current));
54
+ current = parent;
55
+ }
56
+ return [current, missing];
57
+ }
58
+ function creationPath(filePath, queries) {
59
+ const [existing, missing] = nearestExisting(filePath, queries);
60
+ return path.join(queries.realPath(existing), ...missing);
61
+ }
62
+ function fileIdentityKey(filePath, queries) {
63
+ const [existing, missing] = nearestExisting(filePath, queries);
64
+ const realExisting = queries.realPath(existing);
65
+ if (missing.length === 0) {
66
+ return realExisting;
67
+ }
68
+ const isCaseIgnored = ignoresCase(realExisting, queries);
69
+ const rest = isCaseIgnored ? missing.map((segment) => segment.toLowerCase()) : missing;
70
+ return path.join(realExisting, ...rest);
71
+ }
72
+
73
+ export { creationPath, fileIdentityKey, isSameFileNumber };
74
+ //# sourceMappingURL=file-identity.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-identity.mjs","sources":["../../../src/adapters/node-file-system/file-identity.ts"],"sourcesContent":["import path from \"node:path\";\n\n/**\n * The identity of a file path, also of one that does not exist yet (an output before the first\n * run). Two paths are one file when their keys are equal: the real path of the nearest existing\n * ancestor (links resolved, letter case as stored on disk) followed by the segments that do not\n * exist yet, compared in the letter case rules of the file system that will hold them. Equal keys\n * prove one file; different keys do not prove two (Unicode normalization, full case folding):\n * the CLI claims every output before writing to decide those (ADR-043, decision 1).\n */\n\n/** A device and a file number: one file, also through a hard link. */\nexport interface FileNumber {\n readonly device: bigint;\n readonly file: bigint;\n}\n\n/** The file-system queries the identity needs; Node's `fs` in production, a fake in tests. */\nexport interface PathQueries {\n /** The file number of a path, following links; undefined when it cannot be read. */\n status(filePath: string): FileNumber | undefined;\n /** The path with every link resolved, in the letter case stored on disk. */\n realPath(filePath: string): string;\n /** The names in a directory; empty when it cannot be listed. */\n entries(directory: string): readonly string[];\n /** The target of a symbolic link as written in it; undefined for a path that is no link. */\n linkTarget(filePath: string): string | undefined;\n}\n\n/**\n * The most links followed for one path. Linux follows at most 40 (MAXSYMLINKS) and macOS 32\n * before it fails with ELOOP; a longer chain is a loop, and the path is taken as missing.\n */\nconst MAX_LINK_HOPS = 40;\n\n/** Whether two file numbers name one file. */\nexport function isSameFileNumber(left: FileNumber, right: FileNumber): boolean {\n // Some file systems (FAT, some network shares) report no file number: 0 then proves nothing.\n return left.file !== 0n && left.file === right.file && left.device === right.device;\n}\n\nfunction hasLetters(text: string): boolean {\n return text.toLowerCase() !== text.toUpperCase();\n}\n\nfunction flipCase(text: string): string {\n return [...text]\n .map((character) => {\n const upper = character.toUpperCase();\n return character === upper ? character.toLowerCase() : upper;\n })\n .join(\"\");\n}\n\n/** Whether a directory entry is also found under its name in the other letter case. */\nfunction isFoundInOtherCase(entryPath: string, queries: PathQueries): boolean | undefined {\n const status = queries.status(entryPath);\n if (!status) {\n return undefined;\n }\n const flipped = path.join(path.dirname(entryPath), flipCase(path.basename(entryPath)));\n const flippedStatus = queries.status(flipped);\n return flippedStatus !== undefined && isSameFileNumber(status, flippedStatus);\n}\n\n/**\n * Whether new names in `directory` ignore letter case. It is asked of the file system, not\n * assumed from the OS: macOS and Windows volumes can be case-sensitive, and Linux mounts can\n * ignore case. An entry of the directory is looked up in the other case; without one with\n * letters, the directory itself is. With nothing to look up, names are taken as case-sensitive.\n */\nfunction ignoresCase(directory: string, queries: PathQueries): boolean {\n const candidates = [\n ...queries\n .entries(directory)\n .filter(hasLetters)\n .map((name) => path.join(directory, name)),\n ...(hasLetters(path.basename(directory)) ? [directory] : []),\n ];\n for (const candidate of candidates) {\n const answer = isFoundInOtherCase(candidate, queries);\n if (answer !== undefined) {\n return answer;\n }\n }\n return false;\n}\n\n/**\n * The nearest ancestor of `filePath` that exists (or the path itself), and the rest below it. A\n * link to a missing file or directory is followed to its target, which a write would create: so\n * `link/x.ts` with `link -> missing` is `missing/x.ts`. A relative target is read from the real\n * directory of the link, as the system does: with `a -> real/sub` and\n * `real/sub/link -> ../missing`, `a/link` is `real/missing`, not `missing`.\n */\nfunction nearestExisting(filePath: string, queries: PathQueries): [string, string[]] {\n const missing: string[] = [];\n let current = path.resolve(filePath);\n let hops = 0;\n while (queries.status(current) === undefined) {\n const target = hops < MAX_LINK_HOPS ? queries.linkTarget(current) : undefined;\n if (target !== undefined) {\n current = path.resolve(queries.realPath(path.dirname(current)), target);\n hops += 1;\n continue;\n }\n const parent = path.dirname(current);\n if (parent === current) {\n break;\n }\n missing.unshift(path.basename(current));\n current = parent;\n }\n return [current, missing];\n}\n\n/**\n * The path that a write to `filePath` creates, with links followed also where their target is\n * missing: `link/x.ts` with `link -> missing` is the real path of `missing/x.ts`. `mkdir` does not\n * follow a link to a missing directory on Linux and Windows, so the directories are created here.\n */\nexport function creationPath(filePath: string, queries: PathQueries): string {\n const [existing, missing] = nearestExisting(filePath, queries);\n return path.join(queries.realPath(existing), ...missing);\n}\n\n/** The key of a path: equal keys are one file, whether or not it exists yet. */\nexport function fileIdentityKey(filePath: string, queries: PathQueries): string {\n const [existing, missing] = nearestExisting(filePath, queries);\n const realExisting = queries.realPath(existing);\n if (missing.length === 0) {\n return realExisting;\n }\n const isCaseIgnored = ignoresCase(realExisting, queries);\n const rest = isCaseIgnored ? missing.map((segment) => segment.toLowerCase()) : missing;\n return path.join(realExisting, ...rest);\n}\n"],"names":[],"mappings":"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"}
@@ -3,6 +3,8 @@
3
3
  const fs = require('node:fs');
4
4
  const node_url = require('node:url');
5
5
  const errors = require('../../domain/errors.cjs');
6
+ const fileClaims = require('./file-claims.cjs');
7
+ const fileIdentity = require('./file-identity.cjs');
6
8
 
7
9
  function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
8
10
 
@@ -11,31 +13,105 @@ const fs__default = /*#__PURE__*/_interopDefaultCompat(fs);
11
13
  function causeMessage(cause) {
12
14
  return cause instanceof Error ? cause.message : String(cause);
13
15
  }
16
+ function statusOf(filePath) {
17
+ try {
18
+ const status = fs__default.statSync(filePath, { bigint: true });
19
+ return { device: status.dev, file: status.ino };
20
+ } catch {
21
+ return void 0;
22
+ }
23
+ }
24
+ function realPathOf(filePath) {
25
+ try {
26
+ return fs__default.realpathSync.native(filePath);
27
+ } catch {
28
+ return filePath;
29
+ }
30
+ }
31
+ function entriesOf(directory) {
32
+ try {
33
+ return fs__default.readdirSync(directory);
34
+ } catch {
35
+ return [];
36
+ }
37
+ }
38
+ function linkTargetOf(filePath) {
39
+ try {
40
+ return fs__default.readlinkSync(filePath);
41
+ } catch {
42
+ return void 0;
43
+ }
44
+ }
45
+ const NODE_PATH_QUERIES = {
46
+ status: statusOf,
47
+ realPath: realPathOf,
48
+ entries: entriesOf,
49
+ linkTarget: linkTargetOf
50
+ };
51
+ function localPathOf(file) {
52
+ try {
53
+ return node_url.fileURLToPath(file);
54
+ } catch {
55
+ return void 0;
56
+ }
57
+ }
58
+ function fileError(code, verb, file, cause) {
59
+ const name = localPathOf(file) ?? file.href;
60
+ return new errors.OpenAPITypesafeError({
61
+ code,
62
+ message: `Cannot ${verb} ${name}: ${causeMessage(cause)}`,
63
+ details: { file: name },
64
+ cause
65
+ });
66
+ }
67
+ function typedWrite(file, step) {
68
+ try {
69
+ return step();
70
+ } catch (cause) {
71
+ throw fileError(errors.ERROR_CODES.OUTPUT_WRITE_FAILED, "write", file, cause);
72
+ }
73
+ }
14
74
  class NodeFileSystem {
15
75
  readText(file) {
16
76
  try {
17
77
  return fs__default.readFileSync(file, "utf8");
18
78
  } catch (cause) {
19
- throw new errors.OpenAPITypesafeError({
20
- code: errors.ERROR_CODES.OUTPUT_READ_FAILED,
21
- message: `Cannot read ${node_url.fileURLToPath(file)}: ${causeMessage(cause)}`,
22
- details: { file: node_url.fileURLToPath(file) },
23
- cause
24
- });
79
+ throw fileError(errors.ERROR_CODES.OUTPUT_READ_FAILED, "read", file, cause);
25
80
  }
26
81
  }
27
82
  writeText(file, contents) {
28
83
  try {
29
- fs__default.mkdirSync(new URL(".", file), { recursive: true });
30
- fs__default.writeFileSync(file, contents, "utf8");
84
+ fileClaims.writeFileAt(node_url.fileURLToPath(file), contents, NODE_PATH_QUERIES);
31
85
  } catch (cause) {
32
- throw new errors.OpenAPITypesafeError({
33
- code: errors.ERROR_CODES.OUTPUT_WRITE_FAILED,
34
- message: `Cannot write ${node_url.fileURLToPath(file)}: ${causeMessage(cause)}`,
35
- details: { file: node_url.fileURLToPath(file) },
36
- cause
37
- });
86
+ throw fileError(errors.ERROR_CODES.OUTPUT_WRITE_FAILED, "write", file, cause);
87
+ }
88
+ }
89
+ claimFile(file) {
90
+ const claim = typedWrite(file, () => fileClaims.claimFileAt(node_url.fileURLToPath(file), NODE_PATH_QUERIES));
91
+ return {
92
+ identity: claim.identity,
93
+ release: () => typedWrite(file, () => claim.release())
94
+ };
95
+ }
96
+ isDirectory(file) {
97
+ try {
98
+ return fs__default.statSync(file).isDirectory();
99
+ } catch {
100
+ return false;
101
+ }
102
+ }
103
+ isSameFile(left, right) {
104
+ const leftPath = node_url.fileURLToPath(left);
105
+ const rightPath = node_url.fileURLToPath(right);
106
+ if (leftPath === rightPath) {
107
+ return true;
108
+ }
109
+ const leftStatus = statusOf(leftPath);
110
+ const rightStatus = statusOf(rightPath);
111
+ if (leftStatus && rightStatus && fileIdentity.isSameFileNumber(leftStatus, rightStatus)) {
112
+ return true;
38
113
  }
114
+ return fileIdentity.fileIdentityKey(leftPath, NODE_PATH_QUERIES) === fileIdentity.fileIdentityKey(rightPath, NODE_PATH_QUERIES);
39
115
  }
40
116
  }
41
117
  function createNodeFileSystem() {
@@ -1 +1 @@
1
- {"version":3,"file":"node-file-system.cjs","sources":["../../../src/adapters/node-file-system/node-file-system.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport type { FileReader, FileWriter } from \"../../application/ports/file-system.js\";\nimport { ERROR_CODES, OpenAPITypesafeError } from \"../../domain/errors.js\";\n\n/** The file ports on Node's `fs`, with the failures as typed errors (ADR-011). */\n\nfunction causeMessage(cause: unknown): string {\n return cause instanceof Error ? cause.message : String(cause);\n}\n\nclass NodeFileSystem implements FileReader, FileWriter {\n readText(file: URL): string {\n try {\n return fs.readFileSync(file, \"utf8\");\n } catch (cause) {\n throw new OpenAPITypesafeError({\n code: ERROR_CODES.OUTPUT_READ_FAILED,\n message: `Cannot read ${fileURLToPath(file)}: ${causeMessage(cause)}`,\n details: { file: fileURLToPath(file) },\n cause,\n });\n }\n }\n\n writeText(file: URL, contents: string): void {\n try {\n fs.mkdirSync(new URL(\".\", file), { recursive: true });\n fs.writeFileSync(file, contents, \"utf8\");\n } catch (cause) {\n throw new OpenAPITypesafeError({\n code: ERROR_CODES.OUTPUT_WRITE_FAILED,\n message: `Cannot write ${fileURLToPath(file)}: ${causeMessage(cause)}`,\n details: { file: fileURLToPath(file) },\n cause,\n });\n }\n }\n}\n\n/** File access for the CLI. */\nexport function createNodeFileSystem(): FileReader & FileWriter {\n return new NodeFileSystem();\n}\n"],"names":["fs","OpenAPITypesafeError","ERROR_CODES","fileURLToPath"],"mappings":";;;;;;;;;;AAOA,SAAS,aAAa,KAAA,EAAwB;AAC5C,EAAA,OAAO,KAAA,YAAiB,KAAA,GAAQ,KAAA,CAAM,OAAA,GAAU,OAAO,KAAK,CAAA;AAC9D;AAEA,MAAM,cAAA,CAAiD;AAAA,EACrD,SAAS,IAAA,EAAmB;AAC1B,IAAA,IAAI;AACF,MAAA,OAAOA,WAAA,CAAG,YAAA,CAAa,IAAA,EAAM,MAAM,CAAA;AAAA,IACrC,SAAS,KAAA,EAAO;AACd,MAAA,MAAM,IAAIC,2BAAA,CAAqB;AAAA,QAC7B,MAAMC,kBAAA,CAAY,kBAAA;AAAA,QAClB,OAAA,EAAS,eAAeC,sBAAA,CAAc,IAAI,CAAC,CAAA,EAAA,EAAK,YAAA,CAAa,KAAK,CAAC,CAAA,CAAA;AAAA,QACnE,OAAA,EAAS,EAAE,IAAA,EAAMA,sBAAA,CAAc,IAAI,CAAA,EAAE;AAAA,QACrC;AAAA,OACD,CAAA;AAAA,IACH;AAAA,EACF;AAAA,EAEA,SAAA,CAAU,MAAW,QAAA,EAAwB;AAC3C,IAAA,IAAI;AACF,MAAAH,WAAA,CAAG,SAAA,CAAU,IAAI,GAAA,CAAI,GAAA,EAAK,IAAI,CAAA,EAAG,EAAE,SAAA,EAAW,IAAA,EAAM,CAAA;AACpD,MAAAA,WAAA,CAAG,aAAA,CAAc,IAAA,EAAM,QAAA,EAAU,MAAM,CAAA;AAAA,IACzC,SAAS,KAAA,EAAO;AACd,MAAA,MAAM,IAAIC,2BAAA,CAAqB;AAAA,QAC7B,MAAMC,kBAAA,CAAY,mBAAA;AAAA,QAClB,OAAA,EAAS,gBAAgBC,sBAAA,CAAc,IAAI,CAAC,CAAA,EAAA,EAAK,YAAA,CAAa,KAAK,CAAC,CAAA,CAAA;AAAA,QACpE,OAAA,EAAS,EAAE,IAAA,EAAMA,sBAAA,CAAc,IAAI,CAAA,EAAE;AAAA,QACrC;AAAA,OACD,CAAA;AAAA,IACH;AAAA,EACF;AACF;AAGO,SAAS,oBAAA,GAAgD;AAC9D,EAAA,OAAO,IAAI,cAAA,EAAe;AAC5B;;"}
1
+ {"version":3,"file":"node-file-system.cjs","sources":["../../../src/adapters/node-file-system/node-file-system.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport { fileURLToPath } from \"node:url\";\nimport type {\n ClaimedFile,\n DirectoryCheck,\n FileClaims,\n FileIdentity,\n FileReader,\n FileWriter,\n} from \"../../application/ports/file-system.js\";\nimport { ERROR_CODES, type ErrorCode, OpenAPITypesafeError } from \"../../domain/errors.js\";\nimport { claimFileAt, writeFileAt } from \"./file-claims.js\";\nimport {\n type FileNumber,\n fileIdentityKey,\n isSameFileNumber,\n type PathQueries,\n} from \"./file-identity.js\";\n\n/** The file ports on Node's `fs`, with the failures as typed errors (ADR-011). */\n\nfunction causeMessage(cause: unknown): string {\n return cause instanceof Error ? cause.message : String(cause);\n}\n\n/**\n * The file number of a path, or undefined when it cannot be read. A missing output is the common\n * case; any other failure (no access, a file used as a directory) is reported by the read or write\n * that follows, so the identity check does not report it a second time.\n */\nfunction statusOf(filePath: string): FileNumber | undefined {\n try {\n const status = fs.statSync(filePath, { bigint: true });\n return { device: status.dev, file: status.ino };\n } catch {\n return undefined;\n }\n}\n\n/**\n * The native `realpath` resolves symbolic links and returns the letter case stored on disk, which\n * the JavaScript one does not do on a case-insensitive file system. A path that vanished since its\n * status was read keeps its own spelling.\n */\nfunction realPathOf(filePath: string): string {\n try {\n return fs.realpathSync.native(filePath);\n } catch {\n return filePath;\n }\n}\n\n/** A directory that cannot be listed has no entry to tell its letter case rules by. */\nfunction entriesOf(directory: string): readonly string[] {\n try {\n return fs.readdirSync(directory);\n } catch {\n return [];\n }\n}\n\n/**\n * The target of a symbolic link, as written in it; undefined for a path that is no link or cannot\n * be read. The key then treats the path as missing, and the claim at write time decides.\n */\nfunction linkTargetOf(filePath: string): string | undefined {\n try {\n return fs.readlinkSync(filePath);\n } catch {\n return undefined;\n }\n}\n\nconst NODE_PATH_QUERIES: PathQueries = {\n status: statusOf,\n realPath: realPathOf,\n entries: entriesOf,\n linkTarget: linkTargetOf,\n};\n\n/** The path of a `file:` URL, or undefined for a URL that names no file on this platform. */\nfunction localPathOf(file: URL): string | undefined {\n try {\n return fileURLToPath(file);\n } catch {\n return undefined;\n }\n}\n\n/**\n * A failed read or write as a typed error. The location is named by its path, or by the URL when\n * it names no local file: computing the path must not throw a second, untyped error here.\n */\nfunction fileError(code: ErrorCode, verb: string, file: URL, cause: unknown): OpenAPITypesafeError {\n const name = localPathOf(file) ?? file.href;\n return new OpenAPITypesafeError({\n code,\n message: `Cannot ${verb} ${name}: ${causeMessage(cause)}`,\n details: { file: name },\n cause,\n });\n}\n\n/** Run a step that writes `file`, with its failure as `OUTPUT_WRITE_FAILED`. */\nfunction typedWrite<T>(file: URL, step: () => T): T {\n try {\n return step();\n } catch (cause) {\n throw fileError(ERROR_CODES.OUTPUT_WRITE_FAILED, \"write\", file, cause);\n }\n}\n\nclass NodeFileSystem implements FileReader, FileWriter, FileIdentity, DirectoryCheck, FileClaims {\n readText(file: URL): string {\n try {\n return fs.readFileSync(file, \"utf8\");\n } catch (cause) {\n throw fileError(ERROR_CODES.OUTPUT_READ_FAILED, \"read\", file, cause);\n }\n }\n\n writeText(file: URL, contents: string): void {\n try {\n writeFileAt(fileURLToPath(file), contents, NODE_PATH_QUERIES);\n } catch (cause) {\n throw fileError(ERROR_CODES.OUTPUT_WRITE_FAILED, \"write\", file, cause);\n }\n }\n\n claimFile(file: URL): ClaimedFile {\n const claim = typedWrite(file, () => claimFileAt(fileURLToPath(file), NODE_PATH_QUERIES));\n return {\n identity: claim.identity,\n release: () => typedWrite(file, () => claim.release()),\n };\n }\n\n isDirectory(file: URL): boolean {\n try {\n return fs.statSync(file).isDirectory();\n } catch {\n // Missing or unreadable: not a directory; the write that follows reports a failure.\n return false;\n }\n }\n\n isSameFile(left: URL, right: URL): boolean {\n const leftPath = fileURLToPath(left);\n const rightPath = fileURLToPath(right);\n if (leftPath === rightPath) {\n return true;\n }\n const leftStatus = statusOf(leftPath);\n const rightStatus = statusOf(rightPath);\n if (leftStatus && rightStatus && isSameFileNumber(leftStatus, rightStatus)) {\n return true;\n }\n return (\n fileIdentityKey(leftPath, NODE_PATH_QUERIES) === fileIdentityKey(rightPath, NODE_PATH_QUERIES)\n );\n }\n}\n\n/** File access for the CLI. */\nexport function createNodeFileSystem(): FileReader &\n FileWriter &\n FileIdentity &\n DirectoryCheck &\n FileClaims {\n return new NodeFileSystem();\n}\n"],"names":["fs","fileURLToPath","OpenAPITypesafeError","ERROR_CODES","writeFileAt","claimFileAt","isSameFileNumber","fileIdentityKey"],"mappings":"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"}