@fgv/ts-extras 5.1.0-5 → 5.1.0-50

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 (488) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +90 -0
  3. package/dist/index.browser.js +6 -2
  4. package/dist/index.browser.js.map +1 -0
  5. package/dist/index.js +2 -1
  6. package/dist/index.js.map +1 -0
  7. package/dist/packlets/ai-assist/chatRequestBuilders.js +282 -0
  8. package/dist/packlets/ai-assist/chatRequestBuilders.js.map +1 -0
  9. package/dist/packlets/ai-assist/{apiClient.js → completionClient.js} +165 -214
  10. package/dist/packlets/ai-assist/completionClient.js.map +1 -0
  11. package/dist/packlets/ai-assist/converters.js +47 -3
  12. package/dist/packlets/ai-assist/converters.js.map +1 -0
  13. package/dist/packlets/ai-assist/embeddingClient.js +345 -0
  14. package/dist/packlets/ai-assist/embeddingClient.js.map +1 -0
  15. package/dist/packlets/ai-assist/endpoint.js +107 -0
  16. package/dist/packlets/ai-assist/endpoint.js.map +1 -0
  17. package/dist/packlets/ai-assist/http.js +104 -0
  18. package/dist/packlets/ai-assist/http.js.map +1 -0
  19. package/dist/packlets/ai-assist/imageGenerationClient.js +454 -0
  20. package/dist/packlets/ai-assist/imageGenerationClient.js.map +1 -0
  21. package/dist/packlets/ai-assist/imageOptionsResolver.js +193 -0
  22. package/dist/packlets/ai-assist/imageOptionsResolver.js.map +1 -0
  23. package/dist/packlets/ai-assist/index.js +12 -4
  24. package/dist/packlets/ai-assist/index.js.map +1 -0
  25. package/dist/packlets/ai-assist/jsonCompletion.js +93 -0
  26. package/dist/packlets/ai-assist/jsonCompletion.js.map +1 -0
  27. package/dist/packlets/ai-assist/jsonResponse.js +423 -0
  28. package/dist/packlets/ai-assist/jsonResponse.js.map +1 -0
  29. package/dist/packlets/ai-assist/listModelsClient.js +294 -0
  30. package/dist/packlets/ai-assist/listModelsClient.js.map +1 -0
  31. package/dist/packlets/ai-assist/model.js +301 -9
  32. package/dist/packlets/ai-assist/model.js.map +1 -0
  33. package/dist/packlets/ai-assist/registry.js +376 -12
  34. package/dist/packlets/ai-assist/registry.js.map +1 -0
  35. package/dist/packlets/ai-assist/sseParser.js +123 -0
  36. package/dist/packlets/ai-assist/sseParser.js.map +1 -0
  37. package/dist/packlets/ai-assist/streamingAdapters/anthropic.js +376 -0
  38. package/dist/packlets/ai-assist/streamingAdapters/anthropic.js.map +1 -0
  39. package/dist/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js +687 -0
  40. package/dist/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js.map +1 -0
  41. package/dist/packlets/ai-assist/streamingAdapters/common.js +187 -0
  42. package/dist/packlets/ai-assist/streamingAdapters/common.js.map +1 -0
  43. package/dist/packlets/ai-assist/streamingAdapters/gemini.js +208 -0
  44. package/dist/packlets/ai-assist/streamingAdapters/gemini.js.map +1 -0
  45. package/dist/packlets/ai-assist/streamingAdapters/openaiChat.js +175 -0
  46. package/dist/packlets/ai-assist/streamingAdapters/openaiChat.js.map +1 -0
  47. package/dist/packlets/ai-assist/streamingAdapters/openaiResponses.js +390 -0
  48. package/dist/packlets/ai-assist/streamingAdapters/openaiResponses.js.map +1 -0
  49. package/dist/packlets/ai-assist/streamingAdapters/proxy.js +178 -0
  50. package/dist/packlets/ai-assist/streamingAdapters/proxy.js.map +1 -0
  51. package/dist/packlets/ai-assist/streamingClient.js +140 -0
  52. package/dist/packlets/ai-assist/streamingClient.js.map +1 -0
  53. package/dist/packlets/ai-assist/thinkingOptionsResolver.js +288 -0
  54. package/dist/packlets/ai-assist/thinkingOptionsResolver.js.map +1 -0
  55. package/dist/packlets/ai-assist/toolFormats.js +106 -10
  56. package/dist/packlets/ai-assist/toolFormats.js.map +1 -0
  57. package/dist/packlets/conversion/converters.js +35 -1
  58. package/dist/packlets/conversion/converters.js.map +1 -0
  59. package/dist/packlets/conversion/index.js.map +1 -0
  60. package/dist/packlets/crypto-utils/constants.js.map +1 -0
  61. package/dist/packlets/crypto-utils/converters.js +42 -4
  62. package/dist/packlets/crypto-utils/converters.js.map +1 -0
  63. package/dist/packlets/crypto-utils/directEncryptionProvider.js.map +1 -0
  64. package/dist/packlets/crypto-utils/encryptedFile.js +37 -0
  65. package/dist/packlets/crypto-utils/encryptedFile.js.map +1 -0
  66. package/dist/packlets/crypto-utils/hpkeProvider.js +355 -0
  67. package/dist/packlets/crypto-utils/hpkeProvider.js.map +1 -0
  68. package/dist/packlets/crypto-utils/index.browser.js +13 -2
  69. package/dist/packlets/crypto-utils/index.browser.js.map +1 -0
  70. package/dist/packlets/crypto-utils/index.js +9 -1
  71. package/dist/packlets/crypto-utils/index.js.map +1 -0
  72. package/dist/packlets/crypto-utils/keyPairAlgorithmParams.js +71 -0
  73. package/dist/packlets/crypto-utils/keyPairAlgorithmParams.js.map +1 -0
  74. package/dist/packlets/crypto-utils/keystore/converters.js +111 -16
  75. package/dist/packlets/crypto-utils/keystore/converters.js.map +1 -0
  76. package/dist/packlets/crypto-utils/keystore/encryptedFilePrivateKeyStorage.js +287 -0
  77. package/dist/packlets/crypto-utils/keystore/encryptedFilePrivateKeyStorage.js.map +1 -0
  78. package/dist/packlets/crypto-utils/keystore/index.browser.js +36 -0
  79. package/dist/packlets/crypto-utils/keystore/index.browser.js.map +1 -0
  80. package/dist/packlets/crypto-utils/keystore/index.js +3 -0
  81. package/dist/packlets/crypto-utils/keystore/index.js.map +1 -0
  82. package/dist/packlets/crypto-utils/keystore/keyStore.js +872 -119
  83. package/dist/packlets/crypto-utils/keystore/keyStore.js.map +1 -0
  84. package/dist/packlets/crypto-utils/keystore/model.js +35 -4
  85. package/dist/packlets/crypto-utils/keystore/model.js.map +1 -0
  86. package/dist/packlets/crypto-utils/keystore/privateKeyStorage.js +21 -0
  87. package/dist/packlets/crypto-utils/keystore/privateKeyStorage.js.map +1 -0
  88. package/dist/packlets/crypto-utils/model.js +32 -0
  89. package/dist/packlets/crypto-utils/model.js.map +1 -0
  90. package/dist/packlets/crypto-utils/nodeCryptoProvider.js +347 -1
  91. package/dist/packlets/crypto-utils/nodeCryptoProvider.js.map +1 -0
  92. package/dist/packlets/crypto-utils/seedDerivedKeyPair.js +120 -0
  93. package/dist/packlets/crypto-utils/seedDerivedKeyPair.js.map +1 -0
  94. package/dist/packlets/crypto-utils/spkiHelpers.js +284 -0
  95. package/dist/packlets/crypto-utils/spkiHelpers.js.map +1 -0
  96. package/dist/packlets/csv/csvFileHelpers.js +0 -14
  97. package/dist/packlets/csv/csvFileHelpers.js.map +1 -0
  98. package/dist/packlets/csv/csvHelpers.js +14 -0
  99. package/dist/packlets/csv/csvHelpers.js.map +1 -0
  100. package/dist/packlets/csv/index.browser.js +1 -3
  101. package/dist/packlets/csv/index.browser.js.map +1 -0
  102. package/dist/packlets/csv/index.js.map +1 -0
  103. package/dist/packlets/experimental/extendedArray.js.map +1 -0
  104. package/dist/packlets/experimental/formatter.js.map +1 -0
  105. package/dist/packlets/experimental/index.js.map +1 -0
  106. package/dist/packlets/experimental/rangeOf.js.map +1 -0
  107. package/dist/packlets/hash/index.browser.js.map +1 -0
  108. package/dist/packlets/hash/index.js.map +1 -0
  109. package/dist/packlets/hash/index.node.js.map +1 -0
  110. package/dist/packlets/hash/md5Normalizer.browser.js.map +1 -0
  111. package/dist/packlets/hash/md5Normalizer.js.map +1 -0
  112. package/dist/packlets/mustache/index.js.map +1 -0
  113. package/dist/packlets/mustache/interfaces.js.map +1 -0
  114. package/dist/packlets/mustache/mustacheTemplate.js +42 -4
  115. package/dist/packlets/mustache/mustacheTemplate.js.map +1 -0
  116. package/dist/packlets/record-jar/index.browser.js +1 -3
  117. package/dist/packlets/record-jar/index.browser.js.map +1 -0
  118. package/dist/packlets/record-jar/index.js.map +1 -0
  119. package/dist/packlets/record-jar/recordJarFileHelpers.js +0 -18
  120. package/dist/packlets/record-jar/recordJarFileHelpers.js.map +1 -0
  121. package/dist/packlets/record-jar/recordJarHelpers.js +18 -0
  122. package/dist/packlets/record-jar/recordJarHelpers.js.map +1 -0
  123. package/dist/packlets/safer-fetch/addressClassification.js +438 -0
  124. package/dist/packlets/safer-fetch/addressClassification.js.map +1 -0
  125. package/dist/packlets/safer-fetch/addressPolicy.js +119 -0
  126. package/dist/packlets/safer-fetch/addressPolicy.js.map +1 -0
  127. package/dist/packlets/safer-fetch/contentType.js +120 -0
  128. package/dist/packlets/safer-fetch/contentType.js.map +1 -0
  129. package/dist/packlets/safer-fetch/deadline.js +254 -0
  130. package/dist/packlets/safer-fetch/deadline.js.map +1 -0
  131. package/dist/packlets/safer-fetch/defaults.js +131 -0
  132. package/dist/packlets/safer-fetch/defaults.js.map +1 -0
  133. package/dist/packlets/safer-fetch/failureReason.js +21 -0
  134. package/dist/packlets/safer-fetch/failureReason.js.map +1 -0
  135. package/dist/packlets/safer-fetch/guards.js +148 -0
  136. package/dist/packlets/safer-fetch/guards.js.map +1 -0
  137. package/dist/packlets/safer-fetch/index.browser.js +58 -0
  138. package/dist/packlets/safer-fetch/index.browser.js.map +1 -0
  139. package/dist/packlets/safer-fetch/index.js +52 -0
  140. package/dist/packlets/safer-fetch/index.js.map +1 -0
  141. package/dist/packlets/safer-fetch/model.js +21 -0
  142. package/dist/packlets/safer-fetch/model.js.map +1 -0
  143. package/dist/packlets/safer-fetch/nodeAddressGuard.js +191 -0
  144. package/dist/packlets/safer-fetch/nodeAddressGuard.js.map +1 -0
  145. package/dist/packlets/safer-fetch/redirect.js +93 -0
  146. package/dist/packlets/safer-fetch/redirect.js.map +1 -0
  147. package/dist/packlets/safer-fetch/retry.js +188 -0
  148. package/dist/packlets/safer-fetch/retry.js.map +1 -0
  149. package/dist/packlets/safer-fetch/saferFetch.js +755 -0
  150. package/dist/packlets/safer-fetch/saferFetch.js.map +1 -0
  151. package/dist/packlets/safer-fetch/transport.js +50 -0
  152. package/dist/packlets/safer-fetch/transport.js.map +1 -0
  153. package/dist/packlets/yaml/converters.js.map +1 -0
  154. package/dist/packlets/yaml/index.js +1 -0
  155. package/dist/packlets/yaml/index.js.map +1 -0
  156. package/dist/packlets/yaml/serializers.js +48 -0
  157. package/dist/packlets/yaml/serializers.js.map +1 -0
  158. package/dist/packlets/zip-file-tree/index.js.map +1 -0
  159. package/dist/packlets/zip-file-tree/zipFileTreeAccessors.js +99 -9
  160. package/dist/packlets/zip-file-tree/zipFileTreeAccessors.js.map +1 -0
  161. package/dist/packlets/zip-file-tree/zipFileTreeWriter.js +16 -1
  162. package/dist/packlets/zip-file-tree/zipFileTreeWriter.js.map +1 -0
  163. package/dist/ts-extras.d.ts +6648 -553
  164. package/dist/tsdoc-metadata.json +1 -1
  165. package/lib/index.browser.d.ts +5 -2
  166. package/lib/index.browser.d.ts.map +1 -0
  167. package/lib/index.browser.js +11 -3
  168. package/lib/index.browser.js.map +1 -0
  169. package/lib/index.d.ts +2 -1
  170. package/lib/index.d.ts.map +1 -0
  171. package/lib/index.js +3 -1
  172. package/lib/index.js.map +1 -0
  173. package/lib/packlets/ai-assist/chatRequestBuilders.d.ts +149 -0
  174. package/lib/packlets/ai-assist/chatRequestBuilders.d.ts.map +1 -0
  175. package/lib/packlets/ai-assist/chatRequestBuilders.js +293 -0
  176. package/lib/packlets/ai-assist/chatRequestBuilders.js.map +1 -0
  177. package/lib/packlets/ai-assist/completionClient.d.ts +74 -0
  178. package/lib/packlets/ai-assist/completionClient.d.ts.map +1 -0
  179. package/lib/packlets/ai-assist/{apiClient.js → completionClient.js} +164 -213
  180. package/lib/packlets/ai-assist/completionClient.js.map +1 -0
  181. package/lib/packlets/ai-assist/converters.d.ts +15 -1
  182. package/lib/packlets/ai-assist/converters.d.ts.map +1 -0
  183. package/lib/packlets/ai-assist/converters.js +47 -3
  184. package/lib/packlets/ai-assist/converters.js.map +1 -0
  185. package/lib/packlets/ai-assist/embeddingClient.d.ts +69 -0
  186. package/lib/packlets/ai-assist/embeddingClient.d.ts.map +1 -0
  187. package/lib/packlets/ai-assist/embeddingClient.js +349 -0
  188. package/lib/packlets/ai-assist/embeddingClient.js.map +1 -0
  189. package/lib/packlets/ai-assist/endpoint.d.ts +49 -0
  190. package/lib/packlets/ai-assist/endpoint.d.ts.map +1 -0
  191. package/lib/packlets/ai-assist/endpoint.js +113 -0
  192. package/lib/packlets/ai-assist/endpoint.js.map +1 -0
  193. package/lib/packlets/ai-assist/http.d.ts +34 -0
  194. package/lib/packlets/ai-assist/http.d.ts.map +1 -0
  195. package/lib/packlets/ai-assist/http.js +109 -0
  196. package/lib/packlets/ai-assist/http.js.map +1 -0
  197. package/lib/packlets/ai-assist/imageGenerationClient.d.ts +46 -0
  198. package/lib/packlets/ai-assist/imageGenerationClient.d.ts.map +1 -0
  199. package/lib/packlets/ai-assist/imageGenerationClient.js +458 -0
  200. package/lib/packlets/ai-assist/imageGenerationClient.js.map +1 -0
  201. package/lib/packlets/ai-assist/imageOptionsResolver.d.ts +66 -0
  202. package/lib/packlets/ai-assist/imageOptionsResolver.d.ts.map +1 -0
  203. package/lib/packlets/ai-assist/imageOptionsResolver.js +197 -0
  204. package/lib/packlets/ai-assist/imageOptionsResolver.js.map +1 -0
  205. package/lib/packlets/ai-assist/index.d.ts +12 -4
  206. package/lib/packlets/ai-assist/index.d.ts.map +1 -0
  207. package/lib/packlets/ai-assist/index.js +47 -4
  208. package/lib/packlets/ai-assist/index.js.map +1 -0
  209. package/lib/packlets/ai-assist/jsonCompletion.d.ts +93 -0
  210. package/lib/packlets/ai-assist/jsonCompletion.d.ts.map +1 -0
  211. package/lib/packlets/ai-assist/jsonCompletion.js +97 -0
  212. package/lib/packlets/ai-assist/jsonCompletion.js.map +1 -0
  213. package/lib/packlets/ai-assist/jsonResponse.d.ts +194 -0
  214. package/lib/packlets/ai-assist/jsonResponse.d.ts.map +1 -0
  215. package/lib/packlets/ai-assist/jsonResponse.js +429 -0
  216. package/lib/packlets/ai-assist/jsonResponse.js.map +1 -0
  217. package/lib/packlets/ai-assist/listModelsClient.d.ts +46 -0
  218. package/lib/packlets/ai-assist/listModelsClient.d.ts.map +1 -0
  219. package/lib/packlets/ai-assist/listModelsClient.js +298 -0
  220. package/lib/packlets/ai-assist/listModelsClient.js.map +1 -0
  221. package/lib/packlets/ai-assist/model.d.ts +1346 -12
  222. package/lib/packlets/ai-assist/model.d.ts.map +1 -0
  223. package/lib/packlets/ai-assist/model.js +309 -10
  224. package/lib/packlets/ai-assist/model.js.map +1 -0
  225. package/lib/packlets/ai-assist/registry.d.ts +76 -1
  226. package/lib/packlets/ai-assist/registry.d.ts.map +1 -0
  227. package/lib/packlets/ai-assist/registry.js +381 -13
  228. package/lib/packlets/ai-assist/registry.js.map +1 -0
  229. package/lib/packlets/ai-assist/sseParser.d.ts +45 -0
  230. package/lib/packlets/ai-assist/sseParser.d.ts.map +1 -0
  231. package/lib/packlets/ai-assist/sseParser.js +128 -0
  232. package/lib/packlets/ai-assist/sseParser.js.map +1 -0
  233. package/lib/packlets/ai-assist/streamingAdapters/anthropic.d.ts +72 -0
  234. package/lib/packlets/ai-assist/streamingAdapters/anthropic.d.ts.map +1 -0
  235. package/lib/packlets/ai-assist/streamingAdapters/anthropic.js +379 -0
  236. package/lib/packlets/ai-assist/streamingAdapters/anthropic.js.map +1 -0
  237. package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.d.ts +269 -0
  238. package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.d.ts.map +1 -0
  239. package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js +694 -0
  240. package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js.map +1 -0
  241. package/lib/packlets/ai-assist/streamingAdapters/common.d.ts +163 -0
  242. package/lib/packlets/ai-assist/streamingAdapters/common.d.ts.map +1 -0
  243. package/lib/packlets/ai-assist/streamingAdapters/common.js +193 -0
  244. package/lib/packlets/ai-assist/streamingAdapters/common.js.map +1 -0
  245. package/lib/packlets/ai-assist/streamingAdapters/gemini.d.ts +43 -0
  246. package/lib/packlets/ai-assist/streamingAdapters/gemini.d.ts.map +1 -0
  247. package/lib/packlets/ai-assist/streamingAdapters/gemini.js +211 -0
  248. package/lib/packlets/ai-assist/streamingAdapters/gemini.js.map +1 -0
  249. package/lib/packlets/ai-assist/streamingAdapters/openaiChat.d.ts +19 -0
  250. package/lib/packlets/ai-assist/streamingAdapters/openaiChat.d.ts.map +1 -0
  251. package/lib/packlets/ai-assist/streamingAdapters/openaiChat.js +178 -0
  252. package/lib/packlets/ai-assist/streamingAdapters/openaiChat.js.map +1 -0
  253. package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.d.ts +33 -0
  254. package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.d.ts.map +1 -0
  255. package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.js +393 -0
  256. package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.js.map +1 -0
  257. package/lib/packlets/ai-assist/streamingAdapters/proxy.d.ts +34 -0
  258. package/lib/packlets/ai-assist/streamingAdapters/proxy.d.ts.map +1 -0
  259. package/lib/packlets/ai-assist/streamingAdapters/proxy.js +181 -0
  260. package/lib/packlets/ai-assist/streamingAdapters/proxy.js.map +1 -0
  261. package/lib/packlets/ai-assist/streamingClient.d.ts +50 -0
  262. package/lib/packlets/ai-assist/streamingClient.d.ts.map +1 -0
  263. package/lib/packlets/ai-assist/streamingClient.js +146 -0
  264. package/lib/packlets/ai-assist/streamingClient.js.map +1 -0
  265. package/lib/packlets/ai-assist/thinkingOptionsResolver.d.ts +87 -0
  266. package/lib/packlets/ai-assist/thinkingOptionsResolver.d.ts.map +1 -0
  267. package/lib/packlets/ai-assist/thinkingOptionsResolver.js +294 -0
  268. package/lib/packlets/ai-assist/thinkingOptionsResolver.js.map +1 -0
  269. package/lib/packlets/ai-assist/toolFormats.d.ts +40 -9
  270. package/lib/packlets/ai-assist/toolFormats.d.ts.map +1 -0
  271. package/lib/packlets/ai-assist/toolFormats.js +107 -10
  272. package/lib/packlets/ai-assist/toolFormats.js.map +1 -0
  273. package/lib/packlets/conversion/converters.d.ts +8 -1
  274. package/lib/packlets/conversion/converters.d.ts.map +1 -0
  275. package/lib/packlets/conversion/converters.js +36 -2
  276. package/lib/packlets/conversion/converters.js.map +1 -0
  277. package/lib/packlets/conversion/index.d.ts.map +1 -0
  278. package/lib/packlets/conversion/index.js.map +1 -0
  279. package/lib/packlets/crypto-utils/constants.d.ts.map +1 -0
  280. package/lib/packlets/crypto-utils/constants.js.map +1 -0
  281. package/lib/packlets/crypto-utils/converters.d.ts +20 -1
  282. package/lib/packlets/crypto-utils/converters.d.ts.map +1 -0
  283. package/lib/packlets/crypto-utils/converters.js +43 -5
  284. package/lib/packlets/crypto-utils/converters.js.map +1 -0
  285. package/lib/packlets/crypto-utils/directEncryptionProvider.d.ts.map +1 -0
  286. package/lib/packlets/crypto-utils/directEncryptionProvider.js.map +1 -0
  287. package/lib/packlets/crypto-utils/encryptedFile.d.ts +25 -0
  288. package/lib/packlets/crypto-utils/encryptedFile.d.ts.map +1 -0
  289. package/lib/packlets/crypto-utils/encryptedFile.js +38 -0
  290. package/lib/packlets/crypto-utils/encryptedFile.js.map +1 -0
  291. package/lib/packlets/crypto-utils/hpkeProvider.d.ts +150 -0
  292. package/lib/packlets/crypto-utils/hpkeProvider.d.ts.map +1 -0
  293. package/lib/packlets/crypto-utils/hpkeProvider.js +359 -0
  294. package/lib/packlets/crypto-utils/hpkeProvider.js.map +1 -0
  295. package/lib/packlets/crypto-utils/index.browser.d.ts +5 -1
  296. package/lib/packlets/crypto-utils/index.browser.d.ts.map +1 -0
  297. package/lib/packlets/crypto-utils/index.browser.js +28 -3
  298. package/lib/packlets/crypto-utils/index.browser.js.map +1 -0
  299. package/lib/packlets/crypto-utils/index.d.ts +5 -1
  300. package/lib/packlets/crypto-utils/index.d.ts.map +1 -0
  301. package/lib/packlets/crypto-utils/index.js +24 -1
  302. package/lib/packlets/crypto-utils/index.js.map +1 -0
  303. package/lib/packlets/crypto-utils/keyPairAlgorithmParams.d.ts +54 -0
  304. package/lib/packlets/crypto-utils/keyPairAlgorithmParams.d.ts.map +1 -0
  305. package/lib/packlets/crypto-utils/keyPairAlgorithmParams.js +74 -0
  306. package/lib/packlets/crypto-utils/keyPairAlgorithmParams.js.map +1 -0
  307. package/lib/packlets/crypto-utils/keystore/converters.d.ts +68 -6
  308. package/lib/packlets/crypto-utils/keystore/converters.d.ts.map +1 -0
  309. package/lib/packlets/crypto-utils/keystore/converters.js +109 -14
  310. package/lib/packlets/crypto-utils/keystore/converters.js.map +1 -0
  311. package/lib/packlets/crypto-utils/keystore/encryptedFilePrivateKeyStorage.d.ts +148 -0
  312. package/lib/packlets/crypto-utils/keystore/encryptedFilePrivateKeyStorage.d.ts.map +1 -0
  313. package/lib/packlets/crypto-utils/keystore/encryptedFilePrivateKeyStorage.js +324 -0
  314. package/lib/packlets/crypto-utils/keystore/encryptedFilePrivateKeyStorage.js.map +1 -0
  315. package/lib/packlets/crypto-utils/keystore/index.browser.d.ts +10 -0
  316. package/lib/packlets/crypto-utils/keystore/index.browser.d.ts.map +1 -0
  317. package/lib/packlets/crypto-utils/keystore/index.browser.js +76 -0
  318. package/lib/packlets/crypto-utils/keystore/index.browser.js.map +1 -0
  319. package/lib/packlets/crypto-utils/keystore/index.d.ts +2 -0
  320. package/lib/packlets/crypto-utils/keystore/index.d.ts.map +1 -0
  321. package/lib/packlets/crypto-utils/keystore/index.js +5 -1
  322. package/lib/packlets/crypto-utils/keystore/index.js.map +1 -0
  323. package/lib/packlets/crypto-utils/keystore/keyStore.d.ts +314 -13
  324. package/lib/packlets/crypto-utils/keystore/keyStore.d.ts.map +1 -0
  325. package/lib/packlets/crypto-utils/keystore/keyStore.js +877 -124
  326. package/lib/packlets/crypto-utils/keystore/keyStore.js.map +1 -0
  327. package/lib/packlets/crypto-utils/keystore/model.d.ts +404 -21
  328. package/lib/packlets/crypto-utils/keystore/model.d.ts.map +1 -0
  329. package/lib/packlets/crypto-utils/keystore/model.js +37 -5
  330. package/lib/packlets/crypto-utils/keystore/model.js.map +1 -0
  331. package/lib/packlets/crypto-utils/keystore/privateKeyStorage.d.ts +53 -0
  332. package/lib/packlets/crypto-utils/keystore/privateKeyStorage.d.ts.map +1 -0
  333. package/lib/packlets/crypto-utils/keystore/privateKeyStorage.js +22 -0
  334. package/lib/packlets/crypto-utils/keystore/privateKeyStorage.js.map +1 -0
  335. package/lib/packlets/crypto-utils/model.d.ts +518 -10
  336. package/lib/packlets/crypto-utils/model.d.ts.map +1 -0
  337. package/lib/packlets/crypto-utils/model.js +33 -1
  338. package/lib/packlets/crypto-utils/model.js.map +1 -0
  339. package/lib/packlets/crypto-utils/nodeCryptoProvider.d.ts +142 -2
  340. package/lib/packlets/crypto-utils/nodeCryptoProvider.d.ts.map +1 -0
  341. package/lib/packlets/crypto-utils/nodeCryptoProvider.js +346 -0
  342. package/lib/packlets/crypto-utils/nodeCryptoProvider.js.map +1 -0
  343. package/lib/packlets/crypto-utils/seedDerivedKeyPair.d.ts +42 -0
  344. package/lib/packlets/crypto-utils/seedDerivedKeyPair.d.ts.map +1 -0
  345. package/lib/packlets/crypto-utils/seedDerivedKeyPair.js +123 -0
  346. package/lib/packlets/crypto-utils/seedDerivedKeyPair.js.map +1 -0
  347. package/lib/packlets/crypto-utils/spkiHelpers.d.ts +151 -0
  348. package/lib/packlets/crypto-utils/spkiHelpers.d.ts.map +1 -0
  349. package/lib/packlets/crypto-utils/spkiHelpers.js +297 -0
  350. package/lib/packlets/crypto-utils/spkiHelpers.js.map +1 -0
  351. package/lib/packlets/csv/csvFileHelpers.d.ts +0 -10
  352. package/lib/packlets/csv/csvFileHelpers.d.ts.map +1 -0
  353. package/lib/packlets/csv/csvFileHelpers.js +0 -15
  354. package/lib/packlets/csv/csvFileHelpers.js.map +1 -0
  355. package/lib/packlets/csv/csvHelpers.d.ts +10 -0
  356. package/lib/packlets/csv/csvHelpers.d.ts.map +1 -0
  357. package/lib/packlets/csv/csvHelpers.js +15 -0
  358. package/lib/packlets/csv/csvHelpers.js.map +1 -0
  359. package/lib/packlets/csv/index.browser.d.ts +0 -1
  360. package/lib/packlets/csv/index.browser.d.ts.map +1 -0
  361. package/lib/packlets/csv/index.browser.js +1 -5
  362. package/lib/packlets/csv/index.browser.js.map +1 -0
  363. package/lib/packlets/csv/index.d.ts.map +1 -0
  364. package/lib/packlets/csv/index.js.map +1 -0
  365. package/lib/packlets/experimental/extendedArray.d.ts.map +1 -0
  366. package/lib/packlets/experimental/extendedArray.js.map +1 -0
  367. package/lib/packlets/experimental/formatter.d.ts.map +1 -0
  368. package/lib/packlets/experimental/formatter.js.map +1 -0
  369. package/lib/packlets/experimental/index.d.ts.map +1 -0
  370. package/lib/packlets/experimental/index.js.map +1 -0
  371. package/lib/packlets/experimental/rangeOf.d.ts.map +1 -0
  372. package/lib/packlets/experimental/rangeOf.js.map +1 -0
  373. package/lib/packlets/hash/index.browser.d.ts.map +1 -0
  374. package/lib/packlets/hash/index.browser.js.map +1 -0
  375. package/lib/packlets/hash/index.d.ts.map +1 -0
  376. package/lib/packlets/hash/index.js.map +1 -0
  377. package/lib/packlets/hash/index.node.d.ts.map +1 -0
  378. package/lib/packlets/hash/index.node.js.map +1 -0
  379. package/lib/packlets/hash/md5Normalizer.browser.d.ts.map +1 -0
  380. package/lib/packlets/hash/md5Normalizer.browser.js.map +1 -0
  381. package/lib/packlets/hash/md5Normalizer.d.ts.map +1 -0
  382. package/lib/packlets/hash/md5Normalizer.js.map +1 -0
  383. package/lib/packlets/mustache/index.d.ts +1 -1
  384. package/lib/packlets/mustache/index.d.ts.map +1 -0
  385. package/lib/packlets/mustache/index.js.map +1 -0
  386. package/lib/packlets/mustache/interfaces.d.ts +34 -0
  387. package/lib/packlets/mustache/interfaces.d.ts.map +1 -0
  388. package/lib/packlets/mustache/interfaces.js.map +1 -0
  389. package/lib/packlets/mustache/mustacheTemplate.d.ts +2 -0
  390. package/lib/packlets/mustache/mustacheTemplate.d.ts.map +1 -0
  391. package/lib/packlets/mustache/mustacheTemplate.js +42 -4
  392. package/lib/packlets/mustache/mustacheTemplate.js.map +1 -0
  393. package/lib/packlets/record-jar/index.browser.d.ts +0 -1
  394. package/lib/packlets/record-jar/index.browser.d.ts.map +1 -0
  395. package/lib/packlets/record-jar/index.browser.js +1 -5
  396. package/lib/packlets/record-jar/index.browser.js.map +1 -0
  397. package/lib/packlets/record-jar/index.d.ts.map +1 -0
  398. package/lib/packlets/record-jar/index.js.map +1 -0
  399. package/lib/packlets/record-jar/recordJarFileHelpers.d.ts +0 -11
  400. package/lib/packlets/record-jar/recordJarFileHelpers.d.ts.map +1 -0
  401. package/lib/packlets/record-jar/recordJarFileHelpers.js +0 -19
  402. package/lib/packlets/record-jar/recordJarFileHelpers.js.map +1 -0
  403. package/lib/packlets/record-jar/recordJarHelpers.d.ts +11 -0
  404. package/lib/packlets/record-jar/recordJarHelpers.d.ts.map +1 -0
  405. package/lib/packlets/record-jar/recordJarHelpers.js +19 -0
  406. package/lib/packlets/record-jar/recordJarHelpers.js.map +1 -0
  407. package/lib/packlets/safer-fetch/addressClassification.d.ts +144 -0
  408. package/lib/packlets/safer-fetch/addressClassification.d.ts.map +1 -0
  409. package/lib/packlets/safer-fetch/addressClassification.js +441 -0
  410. package/lib/packlets/safer-fetch/addressClassification.js.map +1 -0
  411. package/lib/packlets/safer-fetch/addressPolicy.d.ts +113 -0
  412. package/lib/packlets/safer-fetch/addressPolicy.d.ts.map +1 -0
  413. package/lib/packlets/safer-fetch/addressPolicy.js +123 -0
  414. package/lib/packlets/safer-fetch/addressPolicy.js.map +1 -0
  415. package/lib/packlets/safer-fetch/contentType.d.ts +51 -0
  416. package/lib/packlets/safer-fetch/contentType.d.ts.map +1 -0
  417. package/lib/packlets/safer-fetch/contentType.js +127 -0
  418. package/lib/packlets/safer-fetch/contentType.js.map +1 -0
  419. package/lib/packlets/safer-fetch/deadline.d.ts +138 -0
  420. package/lib/packlets/safer-fetch/deadline.d.ts.map +1 -0
  421. package/lib/packlets/safer-fetch/deadline.js +258 -0
  422. package/lib/packlets/safer-fetch/deadline.js.map +1 -0
  423. package/lib/packlets/safer-fetch/defaults.d.ts +108 -0
  424. package/lib/packlets/safer-fetch/defaults.d.ts.map +1 -0
  425. package/lib/packlets/safer-fetch/defaults.js +134 -0
  426. package/lib/packlets/safer-fetch/defaults.js.map +1 -0
  427. package/lib/packlets/safer-fetch/failureReason.d.ts +148 -0
  428. package/lib/packlets/safer-fetch/failureReason.d.ts.map +1 -0
  429. package/lib/packlets/safer-fetch/failureReason.js +22 -0
  430. package/lib/packlets/safer-fetch/failureReason.js.map +1 -0
  431. package/lib/packlets/safer-fetch/guards.d.ts +62 -0
  432. package/lib/packlets/safer-fetch/guards.d.ts.map +1 -0
  433. package/lib/packlets/safer-fetch/guards.js +153 -0
  434. package/lib/packlets/safer-fetch/guards.js.map +1 -0
  435. package/lib/packlets/safer-fetch/index.browser.d.ts +33 -0
  436. package/lib/packlets/safer-fetch/index.browser.d.ts.map +1 -0
  437. package/lib/packlets/safer-fetch/index.browser.js +82 -0
  438. package/lib/packlets/safer-fetch/index.browser.js.map +1 -0
  439. package/lib/packlets/safer-fetch/index.d.ts +27 -0
  440. package/lib/packlets/safer-fetch/index.d.ts.map +1 -0
  441. package/lib/packlets/safer-fetch/index.js +78 -0
  442. package/lib/packlets/safer-fetch/index.js.map +1 -0
  443. package/lib/packlets/safer-fetch/model.d.ts +373 -0
  444. package/lib/packlets/safer-fetch/model.d.ts.map +1 -0
  445. package/lib/packlets/safer-fetch/model.js +22 -0
  446. package/lib/packlets/safer-fetch/model.js.map +1 -0
  447. package/lib/packlets/safer-fetch/nodeAddressGuard.d.ts +129 -0
  448. package/lib/packlets/safer-fetch/nodeAddressGuard.d.ts.map +1 -0
  449. package/lib/packlets/safer-fetch/nodeAddressGuard.js +196 -0
  450. package/lib/packlets/safer-fetch/nodeAddressGuard.js.map +1 -0
  451. package/lib/packlets/safer-fetch/redirect.d.ts +62 -0
  452. package/lib/packlets/safer-fetch/redirect.d.ts.map +1 -0
  453. package/lib/packlets/safer-fetch/redirect.js +98 -0
  454. package/lib/packlets/safer-fetch/redirect.js.map +1 -0
  455. package/lib/packlets/safer-fetch/retry.d.ts +162 -0
  456. package/lib/packlets/safer-fetch/retry.d.ts.map +1 -0
  457. package/lib/packlets/safer-fetch/retry.js +197 -0
  458. package/lib/packlets/safer-fetch/retry.js.map +1 -0
  459. package/lib/packlets/safer-fetch/saferFetch.d.ts +108 -0
  460. package/lib/packlets/safer-fetch/saferFetch.d.ts.map +1 -0
  461. package/lib/packlets/safer-fetch/saferFetch.js +760 -0
  462. package/lib/packlets/safer-fetch/saferFetch.js.map +1 -0
  463. package/lib/packlets/safer-fetch/transport.d.ts +18 -0
  464. package/lib/packlets/safer-fetch/transport.d.ts.map +1 -0
  465. package/lib/packlets/safer-fetch/transport.js +53 -0
  466. package/lib/packlets/safer-fetch/transport.js.map +1 -0
  467. package/lib/packlets/yaml/converters.d.ts.map +1 -0
  468. package/lib/packlets/yaml/converters.js.map +1 -0
  469. package/lib/packlets/yaml/index.d.ts +1 -0
  470. package/lib/packlets/yaml/index.d.ts.map +1 -0
  471. package/lib/packlets/yaml/index.js +1 -0
  472. package/lib/packlets/yaml/index.js.map +1 -0
  473. package/lib/packlets/yaml/serializers.d.ts +45 -0
  474. package/lib/packlets/yaml/serializers.d.ts.map +1 -0
  475. package/lib/packlets/yaml/serializers.js +84 -0
  476. package/lib/packlets/yaml/serializers.js.map +1 -0
  477. package/lib/packlets/zip-file-tree/index.d.ts.map +1 -0
  478. package/lib/packlets/zip-file-tree/index.js.map +1 -0
  479. package/lib/packlets/zip-file-tree/zipFileTreeAccessors.d.ts +84 -8
  480. package/lib/packlets/zip-file-tree/zipFileTreeAccessors.d.ts.map +1 -0
  481. package/lib/packlets/zip-file-tree/zipFileTreeAccessors.js +99 -9
  482. package/lib/packlets/zip-file-tree/zipFileTreeAccessors.js.map +1 -0
  483. package/lib/packlets/zip-file-tree/zipFileTreeWriter.d.ts +26 -1
  484. package/lib/packlets/zip-file-tree/zipFileTreeWriter.d.ts.map +1 -0
  485. package/lib/packlets/zip-file-tree/zipFileTreeWriter.js +17 -1
  486. package/lib/packlets/zip-file-tree/zipFileTreeWriter.js.map +1 -0
  487. package/package.json +30 -15
  488. package/lib/packlets/ai-assist/apiClient.d.ts +0 -60
@@ -0,0 +1,760 @@
1
+ "use strict";
2
+ // Copyright (c) 2026 Erik Fortune
3
+ //
4
+ // Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ // of this software and associated documentation files (the "Software"), to deal
6
+ // in the Software without restriction, including without limitation the rights
7
+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ // copies of the Software, and to permit persons to whom the Software is
9
+ // furnished to do so, subject to the following conditions:
10
+ //
11
+ // The above copyright notice and this permission notice shall be included in all
12
+ // copies or substantial portions of the Software.
13
+ //
14
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ // SOFTWARE.
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.saferFetchBytes = saferFetchBytes;
23
+ exports.saferFetchText = saferFetchText;
24
+ exports.saferFetchJson = saferFetchJson;
25
+ const ts_utils_1 = require("@fgv/ts-utils");
26
+ const ts_json_base_1 = require("@fgv/ts-json-base");
27
+ const contentType_1 = require("./contentType");
28
+ const deadline_1 = require("./deadline");
29
+ const defaults_1 = require("./defaults");
30
+ const guards_1 = require("./guards");
31
+ const retry_1 = require("./retry");
32
+ const redirect_1 = require("./redirect");
33
+ const transport_1 = require("./transport");
34
+ const METHODS_WITHOUT_BODY = ['GET', 'HEAD'];
35
+ const REDIRECT_POLICIES = ['reject', 'validate-each-hop'];
36
+ function _succeed(value) {
37
+ return (0, ts_utils_1.succeedWithDetail)(value);
38
+ }
39
+ function _fail(detail, message) {
40
+ return (0, ts_utils_1.failWithDetail)(message, detail);
41
+ }
42
+ /** Re-types a failure for the next stage without re-deriving its message or detail. */
43
+ function _propagate(from) {
44
+ return (0, ts_utils_1.failWithDetail)(from.message, from.detail);
45
+ }
46
+ function _unknown(message) {
47
+ return _fail({ kind: 'unknown', detail: message }, message);
48
+ }
49
+ /** The wrapping that every escaped-exception path reports, defined once so the two agree exactly. */
50
+ function _unexpected(message) {
51
+ return _unknown(`saferFetch: unexpected error: ${message}`);
52
+ }
53
+ /**
54
+ * Restores the taxonomy's invariant that **every** failure leaving this module carries a
55
+ * `FetchFailureReason`.
56
+ *
57
+ * @remarks
58
+ * Only one thing can produce a detail-less failure: a bug inside this module that throws or
59
+ * rejects somewhere a `thenOnSuccess` callback catches it. `AsyncDetailedResult` converts such a
60
+ * throw to a `DetailedFailure` with `detail: undefined` — correctly, since a thrown error supplies
61
+ * no reason and inventing one would be worse — but that is a *chaining* primitive's contract, not
62
+ * this module's.
63
+ *
64
+ * Before the chaining pass those throws propagated as real rejections all the way to
65
+ * {@link _execute}'s `captureAsyncResult` and were reported as `'unknown'`. Catching them earlier
66
+ * must not change what the caller sees, so the same wrapping is reapplied here. Without this a
67
+ * caller switching on `detail.kind` — the documented way to consume this API — would fault on
68
+ * `undefined`.
69
+ */
70
+ function _withReason(outcome) {
71
+ return outcome.isFailure() && outcome.detail === undefined ? _unexpected(outcome.message) : outcome;
72
+ }
73
+ function _blocked(seam, url, hop, guard, detail) {
74
+ return _fail({ kind: 'blocked-by-guard', url: url.toString(), hop, guard, detail }, `${seam} guard "${guard}" rejected ${url.toString()} at hop ${hop}: ${detail}`);
75
+ }
76
+ /**
77
+ * Awaits a guard or transport call, converting a thrown error or rejected promise into a
78
+ * `Failure` so a misbehaving implementation cannot escape the taxonomy as an exception.
79
+ */
80
+ async function _capture(fn) {
81
+ return (0, ts_utils_1.captureAsyncResult)(fn).onSuccess((inner) => inner);
82
+ }
83
+ /**
84
+ * Runs a guard or transport call against the deadlines and folds all three outcomes — stopped,
85
+ * refused, cleared — into one `DetailedResult` the caller can chain from.
86
+ *
87
+ * @remarks
88
+ * Exists so the request path reads as a chain rather than as five copies of the same
89
+ * stopped-then-failed ladder. The taxonomy mapping stays per-site, because it genuinely differs
90
+ * per site: an address guard's refusal is `'blocked-by-guard'`, a transport's is `'network'`,
91
+ * and a content-type guard's is `'unsupported-content-type'`.
92
+ *
93
+ * **A stop is checked first and is never re-derived afterwards.** A stop resolves every
94
+ * in-flight race the instant it fires, so reaching the failure branch means the work settled
95
+ * *before* the call gave up — and relabelling that as a timeout because the deadline expired a
96
+ * microsecond later would be the taxonomy lying about which came first.
97
+ *
98
+ * @param onAbandon - Released whenever the value will not be used — on a stop and on a refusal
99
+ * alike. In practice this is the response body: leaving one unconsumed holds the connection
100
+ * open for a call that has already failed.
101
+ */
102
+ async function _raced(watch, work, toFailure, onAbandon) {
103
+ const race = await watch.race(_capture(work));
104
+ if (race.stopped) {
105
+ onAbandon === null || onAbandon === void 0 ? void 0 : onAbandon();
106
+ return _stopped(watch, race.cause);
107
+ }
108
+ if (race.value.isFailure()) {
109
+ onAbandon === null || onAbandon === void 0 ? void 0 : onAbandon();
110
+ return toFailure(race.value.message);
111
+ }
112
+ return _succeed(race.value.value);
113
+ }
114
+ function _stopMessage(cause) {
115
+ return cause === 'caller-aborted' ? 'request aborted by caller.' : `request timed out (${cause}).`;
116
+ }
117
+ function _stopped(watch, cause) {
118
+ return _fail(watch.toFailureReason(cause), _stopMessage(cause));
119
+ }
120
+ /**
121
+ * Rejects any scheme this primitive will never request.
122
+ *
123
+ * `file:`, `data:`, `blob:`, `ftp:`, `gopher:` and `ws:` are refused here, in the core, because
124
+ * none is a legitimate network fetch and each is a standard SSRF payload. Choosing between
125
+ * `http:` and `https:`, and deciding which ports are acceptable, is the address guard's job —
126
+ * which is why the core does not narrow further, and why `allowInsecureHttp` can exist there.
127
+ */
128
+ function _checkScheme(url) {
129
+ if (!defaults_1.SUPPORTED_SCHEMES.includes(url.protocol)) {
130
+ const detail = `scheme "${url.protocol}" is not supported (expected ${defaults_1.SUPPORTED_SCHEMES.join(' or ')})`;
131
+ return _fail({ kind: 'invalid-url', url: url.toString(), detail }, `invalid URL: ${detail}`);
132
+ }
133
+ return _succeed(url);
134
+ }
135
+ function _parseUrl(url) {
136
+ const text = String(url);
137
+ const parsed = (0, ts_utils_1.captureResult)(() => new URL(text));
138
+ if (parsed.isFailure()) {
139
+ return _fail({ kind: 'invalid-url', url: text, detail: parsed.message }, `invalid URL "${text}": ${parsed.message}`);
140
+ }
141
+ return _checkScheme(parsed.value);
142
+ }
143
+ function _lowercaseHeaders(headers) {
144
+ const result = {};
145
+ for (const [name, value] of Object.entries(headers)) {
146
+ result[name.toLowerCase()] = value;
147
+ }
148
+ return result;
149
+ }
150
+ function _readHeaders(response) {
151
+ const headers = {};
152
+ response.headers.forEach((value, name) => {
153
+ headers[name.toLowerCase()] = value;
154
+ });
155
+ return headers;
156
+ }
157
+ /** Releases a response body that will not be read, so the socket is not held open. */
158
+ function _discardBody(response) {
159
+ var _a;
160
+ const body = (_a = response.body) !== null && _a !== void 0 ? _a : undefined;
161
+ if (body !== undefined) {
162
+ // Best-effort and deliberately not awaited: the failure is already decided, and a cancel
163
+ // that rejects (an already-errored stream) must not replace it.
164
+ body.cancel().catch(() => undefined);
165
+ }
166
+ }
167
+ async function _cancelReader(reader) {
168
+ // A cancel that rejects (an already-errored or already-released stream) must not mask the
169
+ // failure that prompted it.
170
+ await reader.cancel().catch(() => undefined);
171
+ }
172
+ function _resolveCallOptions(options) {
173
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
174
+ const method = (_a = options.method) !== null && _a !== void 0 ? _a : 'GET';
175
+ const body = (_b = options.body) !== null && _b !== void 0 ? _b : undefined;
176
+ if (body !== undefined && METHODS_WITHOUT_BODY.includes(method)) {
177
+ return _unknown(`a ${method} request cannot carry a body.`);
178
+ }
179
+ const timeoutMs = (_c = options.timeoutMs) !== null && _c !== void 0 ? _c : defaults_1.DEFAULT_TIMEOUT_MS;
180
+ const headersTimeoutMs = (_d = options.headersTimeoutMs) !== null && _d !== void 0 ? _d : defaults_1.DEFAULT_HEADERS_TIMEOUT_MS;
181
+ const maxResponseBytes = (_e = options.maxResponseBytes) !== null && _e !== void 0 ? _e : defaults_1.DEFAULT_MAX_RESPONSE_BYTES;
182
+ const limits = [
183
+ ['timeoutMs', timeoutMs],
184
+ ['headersTimeoutMs', headersTimeoutMs],
185
+ ['maxResponseBytes', maxResponseBytes]
186
+ ];
187
+ for (const [name, value] of limits) {
188
+ if (!Number.isFinite(value) || value <= 0) {
189
+ return _unknown(`${name} must be a positive finite number; got ${value}.`);
190
+ }
191
+ }
192
+ // `String(...)` and a lookup rather than a literal comparison, so that a JavaScript caller
193
+ // supplying a policy this release does not implement is rejected instead of silently getting a
194
+ // different one. The lookup also narrows to the union with no cast.
195
+ const requestedPolicy = String((_f = options.redirectPolicy) !== null && _f !== void 0 ? _f : 'reject');
196
+ const redirectPolicy = REDIRECT_POLICIES.find((p) => p === requestedPolicy);
197
+ if (redirectPolicy === undefined) {
198
+ return _unknown(`redirectPolicy "${requestedPolicy}" is not supported; expected ${REDIRECT_POLICIES.join(' or ')}.`);
199
+ }
200
+ const maxRedirects = (_g = options.maxRedirects) !== null && _g !== void 0 ? _g : defaults_1.DEFAULT_MAX_REDIRECTS;
201
+ if (!Number.isInteger(maxRedirects) || maxRedirects < 0) {
202
+ return _unknown(`maxRedirects must be a non-negative integer; got ${maxRedirects}.`);
203
+ }
204
+ // Same reasoning as `redirectPolicy` above, and the stakes are higher: these name the headers
205
+ // that get stripped on a cross-origin hop. A JavaScript caller can pass anything, and a
206
+ // non-string entry would throw out of an API contracted to only ever return a `Result`.
207
+ // Coercing instead of rejecting would be worse than either — `String(123)` is a header name
208
+ // that silently never matches, so a caller who fumbled the type would believe a credential was
209
+ // being stripped while it was carried across origins. A non-array is rejected for the same
210
+ // reason: spreading a bare string would enumerate it into single characters.
211
+ const extraSensitive = options.sensitiveHeaders;
212
+ if (extraSensitive !== undefined) {
213
+ if (!Array.isArray(extraSensitive)) {
214
+ return _unknown(`sensitiveHeaders must be an array of header names; got ${typeof extraSensitive}.`);
215
+ }
216
+ // `findIndex`, not `find`: an array containing `undefined` is itself invalid, and `find`
217
+ // cannot distinguish that from "no offender".
218
+ const badIndex = extraSensitive.findIndex((n) => typeof n !== 'string');
219
+ if (badIndex >= 0) {
220
+ return _unknown(`sensitiveHeaders[${badIndex}] must be a string header name; got ${typeof extraSensitive[badIndex]}.`);
221
+ }
222
+ }
223
+ const retry = (0, retry_1.resolveRetryPolicy)(options.retry);
224
+ if (retry.isFailure()) {
225
+ return _unknown(retry.message);
226
+ }
227
+ return _succeed({
228
+ method,
229
+ headers: _lowercaseHeaders((_h = options.headers) !== null && _h !== void 0 ? _h : {}),
230
+ body,
231
+ timeoutMs,
232
+ headersTimeoutMs,
233
+ maxResponseBytes,
234
+ redirectPolicy,
235
+ maxRedirects,
236
+ sensitiveHeaders: (0, redirect_1.sensitiveHeaderSet)(options.sensitiveHeaders),
237
+ retry: retry.value,
238
+ logger: (_j = options.logger) !== null && _j !== void 0 ? _j : new ts_utils_1.Logging.NoOpLogger()
239
+ });
240
+ }
241
+ /**
242
+ * Reads the body, counting decoded bytes as they arrive and failing the moment the cap is
243
+ * exceeded.
244
+ *
245
+ * `Content-Length` is a fast-reject path only. It is absent on chunked responses, and it is a
246
+ * value a hostile server chooses freely — a `Content-Length: 12` on a ten-gigabyte body costs
247
+ * the attacker nothing. The cap must therefore hold with no help from it, which is what
248
+ * counting during the read buys.
249
+ *
250
+ * The count is of *decoded* bytes, because the platform transparently decompresses
251
+ * `Content-Encoding`. That is also the decompression-bomb defense: a 2 KB gzip that expands to
252
+ * 10 GB clears the fast reject and then trips the cap after `limit` decoded bytes. It is why
253
+ * `too-large.declared` reads as "what the wire claimed", never as "how big it was".
254
+ */
255
+ async function _readCappedBody(response, head, limit, watch) {
256
+ var _a;
257
+ const declared = head.contentLength;
258
+ if (declared !== undefined && declared > limit) {
259
+ _discardBody(response);
260
+ return _fail({ kind: 'too-large', bytesRead: 0, limit, declared }, `response declares ${declared} bytes, over the ${limit}-byte cap.`);
261
+ }
262
+ const body = (_a = response.body) !== null && _a !== void 0 ? _a : undefined;
263
+ if (body === undefined) {
264
+ return _succeed(new Uint8Array(0));
265
+ }
266
+ const reader = body.getReader();
267
+ const chunks = [];
268
+ let total = 0;
269
+ for (;;) {
270
+ // The read must be captured, not raced raw. `reader.read()` rejects whenever the connection
271
+ // drops mid-transfer — a server reset, a proxy killing an idle connection, a TLS teardown —
272
+ // which is ordinary AC2 behaviour, not an exotic one. Racing the bare promise would let that
273
+ // rejection escape as a throw out of an entry point documented to always return a Result,
274
+ // which is precisely the guarantee this primitive sells.
275
+ const race = await watch.race(_capture(() => reader.read().then(ts_utils_1.succeed)));
276
+ if (race.stopped) {
277
+ // Cancelling is what makes the deadline effective rather than decorative: without it the
278
+ // transfer continues against a call that has already given up.
279
+ await _cancelReader(reader);
280
+ return _stopped(watch, race.cause);
281
+ }
282
+ if (race.value.isFailure()) {
283
+ // No cancel: the stream has already errored, so there is nothing left to cancel. Reported
284
+ // as `network` — the same kind a transport-level failure gets, because it is the same
285
+ // class of event, observed a few frames later.
286
+ return _fail({ kind: 'network', detail: race.value.message }, `response body read failed: ${race.value.message}`);
287
+ }
288
+ if (race.value.value.done) {
289
+ break;
290
+ }
291
+ const chunk = race.value.value.value;
292
+ total += chunk.byteLength;
293
+ if (total > limit) {
294
+ // Returning without cancelling would leave the body unconsumed and the connection held,
295
+ // turning the size-cap rejection into the exact resource exhaustion the cap exists to
296
+ // prevent.
297
+ await _cancelReader(reader);
298
+ return _fail({ kind: 'too-large', bytesRead: total, limit, declared }, `response exceeded the ${limit}-byte cap after ${total} bytes.`);
299
+ }
300
+ chunks.push(chunk);
301
+ }
302
+ const bytes = new Uint8Array(total);
303
+ let offset = 0;
304
+ for (const chunk of chunks) {
305
+ bytes.set(chunk, offset);
306
+ offset += chunk.byteLength;
307
+ }
308
+ return _succeed(bytes);
309
+ }
310
+ async function _receive(response, url, chain, guards, resolved, watch, notes) {
311
+ var _a;
312
+ if (response.type === 'opaqueredirect') {
313
+ _discardBody(response);
314
+ return _fail({ kind: 'redirect-opaque' }, 'the platform returned an opaque redirect, whose target cannot be inspected or guarded.');
315
+ }
316
+ if (defaults_1.REDIRECT_STATUSES.includes(response.status)) {
317
+ // The body of a redirect is never the body this call wanted, and leaving it unconsumed holds
318
+ // the connection open for the whole rest of the chain.
319
+ _discardBody(response);
320
+ if (resolved.redirectPolicy === 'reject') {
321
+ return _fail({ kind: 'redirect-rejected', url: url.toString(), status: response.status }, `${url.toString()} redirected with status ${response.status}; redirects are rejected.`);
322
+ }
323
+ const location = (_a = response.headers.get('location')) !== null && _a !== void 0 ? _a : undefined;
324
+ if (location === undefined || location.trim().length === 0) {
325
+ return _fail({ kind: 'redirect-rejected', url: url.toString(), status: response.status }, `${url.toString()} redirected with status ${response.status} but sent no usable Location header.`);
326
+ }
327
+ return _succeed({ kind: 'redirect', status: response.status, location });
328
+ }
329
+ const headers = _readHeaders(response);
330
+ // Annotated because a `Record<string, string>` lookup is typed as present even when the
331
+ // header was never sent; these are read as optional everywhere downstream.
332
+ const contentType = headers['content-type'];
333
+ const contentLength = headers['content-length'];
334
+ const head = {
335
+ status: response.status,
336
+ statusText: response.statusText,
337
+ headers,
338
+ contentType,
339
+ contentLength: (0, contentType_1.parseContentLength)(contentLength)
340
+ };
341
+ if (!response.ok) {
342
+ _discardBody(response);
343
+ // Recorded before the failure is built rather than carried on it: `Retry-After` is
344
+ // scheduling advice for this process, not a fact about the response worth widening the
345
+ // taxonomy — and the taxonomy is a surface callers may echo, which advice from an
346
+ // attacker-controlled server should not join.
347
+ notes.retryAfter = headers['retry-after'];
348
+ return _fail(
349
+ // `bodyPreview` is deliberately never populated: error bodies routinely echo request
350
+ // content, including credentials.
351
+ { kind: 'http-status', status: response.status, statusText: response.statusText }, `${url.toString()} returned ${response.status} ${response.statusText}.`);
352
+ }
353
+ const hop = chain.length - 1;
354
+ // Chained rather than stepped: `thenOnSuccess` now preserves `FetchFailureReason` across an
355
+ // async step, so each stage's failure propagates with its detail intact and the two
356
+ // `_propagate` re-types this sequence used to need are gone.
357
+ return (await _raced(watch, () => guards.responseHeaders.check(head, chain), (message) => {
358
+ var _a;
359
+ // A content-type allowlist names itself by exposing `acceptedContentTypes`, and its
360
+ // rejections carry a more useful payload than an opaque policy refusal would.
361
+ const accepted = (_a = guards.responseHeaders.acceptedContentTypes) !== null && _a !== void 0 ? _a : undefined;
362
+ return accepted !== undefined
363
+ ? _fail({ kind: 'unsupported-content-type', contentType: head.contentType, accepted }, `${url.toString()}: ${message}`)
364
+ : _blocked('response-headers', url, hop, guards.responseHeaders.name, message);
365
+ }, () => _discardBody(response)))
366
+ .thenOnSuccess(async () => _readCappedBody(response, head, resolved.maxResponseBytes, watch))
367
+ .thenOnSuccess(async (bytes) => (await _raced(watch, () => guards.responseBody.check(bytes, head), (message) => _blocked('response-body', url, hop, guards.responseBody.name, message))).onSuccess(() => _succeed({ kind: 'final', bytes, head })));
368
+ }
369
+ /**
370
+ * Runs the address guard on the hop about to be requested and returns the URL it cleared.
371
+ *
372
+ * @remarks
373
+ * Split out of {@link _connect} so that the guard's verdict is available to the caller *before*
374
+ * anything is put on the wire. Redirect loop detection needs exactly that: it compares the hop
375
+ * about to be requested against the hops already requested, and both sides have to be
376
+ * guard-cleared URLs for the comparison to mean anything (see {@link _detectLoop}).
377
+ *
378
+ * It runs after the request guard, so a request guard that returned a replacement request
379
+ * cannot route around it, and it runs on **every** hop rather than only the first: a `302` to
380
+ * `http://169.254.169.254/` is exactly what a guard that only saw the caller's URL would miss.
381
+ */
382
+ async function _clearAddress(chain, guards, watch) {
383
+ const hop = chain[chain.length - 1];
384
+ const hopIndex = chain.length - 1;
385
+ return (await _raced(watch, () => guards.address.check(chain), (message) => _blocked('address', hop.url, hopIndex, guards.address.name, message))).onSuccess((verdict) =>
386
+ // A guard may normalize the URL it cleared; the normalized URL is what gets requested. The
387
+ // scheme is re-checked because normalization must never be able to widen it.
388
+ _checkScheme(verdict.url).onSuccess((url) => { var _a; return _succeed({ url, pinnedAddress: (_a = verdict.pinnedAddress) !== null && _a !== void 0 ? _a : undefined }); }));
389
+ }
390
+ /**
391
+ * Rejects a hop that revisits a URL already requested on this walk.
392
+ *
393
+ * @remarks
394
+ * `A → B → A → B` passes every per-hop check while consuming the whole redirect budget, and the
395
+ * hop cap alone would let it — the same reason the address guard is handed the chain rather
396
+ * than a counter.
397
+ *
398
+ * **Both sides of the comparison are guard-cleared URLs**, which is why this runs after
399
+ * {@link _clearAddress} rather than on the raw `Location` target. A guard may normalize the URL
400
+ * it clears — `IGuardVerdict.url` explicitly permits lowercasing a host, stripping a trailing
401
+ * dot, punycoding an IDN — so comparing a cleared URL against an uncleared one lets a repeat
402
+ * escape detection and run to `maxRedirects` instead. Comparing like with like is the whole
403
+ * fix; special-casing particular normalizations here would be worse than leaving it, because
404
+ * this layer does not own normalization and cannot anticipate what a custom guard does.
405
+ *
406
+ * `url` on the failure is the URL that **issued** the redirect, never the repeated target — the
407
+ * contract `FetchFailureReason.redirect-rejected` documents. The target is named in the message.
408
+ */
409
+ function _detectLoop(cleared, completed) {
410
+ var _a;
411
+ const issuer = (_a = completed[completed.length - 1]) !== null && _a !== void 0 ? _a : undefined;
412
+ // Hop 0 cannot repeat anything, so an empty chain is trivially clear — and there would be no
413
+ // issuing hop to name if it could.
414
+ if (issuer === undefined || !completed.some((hop) => hop.url.toString() === cleared.toString())) {
415
+ return _succeed(cleared);
416
+ }
417
+ return _fail({ kind: 'redirect-rejected', url: issuer.url.toString(), status: issuer.status }, `${issuer.url.toString()} redirected with status ${issuer.status} to ${cleared.toString()}, which is already in the chain.`);
418
+ }
419
+ async function _connect(request, cleared, chain, guards, options, resolved, watch, notes) {
420
+ var _a;
421
+ const transport = (_a = options.transport) !== null && _a !== void 0 ? _a : transport_1.platformFetchTransport;
422
+ const init = {
423
+ method: request.method,
424
+ headers: request.headers,
425
+ // `'manual'` rather than `'follow'`: the platform must not follow anything on our behalf,
426
+ // because a hop the guard never sees is a hop the guard never guarded.
427
+ redirect: 'manual',
428
+ signal: watch.signal
429
+ };
430
+ if (request.body !== undefined) {
431
+ // A byte body is copied into a fresh buffer rather than handed over by reference: it keeps
432
+ // the platform's non-shared `BufferSource` requirement satisfied without a cast, and it
433
+ // means a caller mutating its array after the call cannot change what goes on the wire.
434
+ init.body = typeof request.body === 'string' ? request.body : new Uint8Array(request.body);
435
+ }
436
+ return (await _raced(watch, () => transport.fetch(cleared.url, init, { pinnedAddress: cleared.pinnedAddress }), (message) => _fail({ kind: 'network', detail: message }, `transport "${transport.name}" failed: ${message}`)))
437
+ .thenOnSuccess(async (response) => {
438
+ // Inside the continuation rather than before it, which is where it already ran: the
439
+ // headers deadline is retired only once a response has actually arrived.
440
+ watch.headersReceived();
441
+ return _receive(response, cleared.url, chain, guards, resolved, watch, notes);
442
+ })
443
+ .onSuccess((outcome) =>
444
+ // The pin is recorded only once the transport has accepted it. A transport that cannot
445
+ // honor a pin is required to fail rather than connect by hostname, so a settled request
446
+ // with a pin set is evidence the pin held — which is the only basis on which recording it
447
+ // would be honest.
448
+ _succeed({ url: cleared.url, connectedAddress: cleared.pinnedAddress, outcome }));
449
+ }
450
+ /**
451
+ * Derives the next hop's request from a redirect, or explains why the chain stops here.
452
+ *
453
+ * @remarks
454
+ * The hop cap is checked before the target is resolved: a chain that has already run out of
455
+ * budget stops for that reason, whatever the `Location` header happens to contain.
456
+ *
457
+ * Loop detection is deliberately **not** here — it belongs after the address guard has cleared
458
+ * the next hop, where both sides of the comparison are cleared URLs. See {@link _detectLoop}.
459
+ */
460
+ function _nextHop(from, completed, redirect, request, resolved) {
461
+ const followed = completed.length;
462
+ if (followed >= resolved.maxRedirects) {
463
+ return _fail({ kind: 'too-many-redirects', hops: followed + 1, limit: resolved.maxRedirects }, `redirect chain exceeded the limit of ${resolved.maxRedirects} hops.`);
464
+ }
465
+ // The plain-`Result`-to-`DetailedResult` conversion is done here rather than chained, for the
466
+ // same reason `_parseUrl` and `_decodeText` do it: it is the boundary where a `Result` becomes
467
+ // an `Outcome`, and naming the boundary is what lets everything downstream of it chain.
468
+ const located = (0, redirect_1.resolveLocation)(redirect.location, from);
469
+ if (located.isFailure()) {
470
+ return _fail({ kind: 'invalid-url', url: redirect.location, detail: located.message }, `invalid redirect target: ${located.message}`);
471
+ }
472
+ return _checkScheme(located.value).onSuccess((to) => {
473
+ // Headers come from the request that was actually sent on this hop, never from the
474
+ // caller's original set — that carry-forward is what makes credential stripping monotonic.
475
+ const rewritten = (0, redirect_1.rewriteForRedirect)({
476
+ from,
477
+ to,
478
+ status: redirect.status,
479
+ method: request.method,
480
+ headers: request.headers,
481
+ body: request.body,
482
+ sensitiveHeaders: resolved.sensitiveHeaders
483
+ });
484
+ return _succeed(Object.assign({ url: to, method: rewritten.method, headers: rewritten.headers }, (rewritten.body !== undefined ? { body: rewritten.body } : {})));
485
+ });
486
+ }
487
+ /**
488
+ * Walks one attempt: the caller's URL, then every redirect hop it leads to, each fully guarded.
489
+ *
490
+ * @remarks
491
+ * **A retry calls this again from the top, with a request rebuilt from the caller's resolved
492
+ * options.** Nothing is carried across attempts — not the guard's verdict, not the redirect
493
+ * chain, not the headers a previous attempt's cross-origin hop had already stripped. That is
494
+ * what makes a retry a full re-walk rather than a resume, and the reason is in
495
+ * {@link SaferFetch.IRetryPolicy}: a cached verdict would turn retry into a DNS-rebinding
496
+ * amplifier, and a resumed chain would revalidate hops the new attempt no longer follows.
497
+ */
498
+ async function _walk(url, guards, options, resolved, watch, notes) {
499
+ let request = Object.assign({ url, method: resolved.method, headers: resolved.headers }, (resolved.body !== undefined ? { body: resolved.body } : {}));
500
+ /** Hops already requested and redirected away from, oldest first. */
501
+ const completed = [];
502
+ for (;;) {
503
+ // Re-arms the per-attempt headers deadline. Without this, the first hop's
504
+ // `headersReceived()` would retire it for the whole call and a later host that never
505
+ // answers would be bounded only by the overall deadline.
506
+ watch.attemptStarted();
507
+ const guarded = await _raced(watch, () => guards.request.check(request, [...completed, { url: request.url }]), (message) => _blocked('request', request.url, completed.length, guards.request.name, message));
508
+ if (guarded.isFailure()) {
509
+ return _propagate(guarded);
510
+ }
511
+ const checked = guarded.value;
512
+ const rechecked = _checkScheme(checked.url);
513
+ if (rechecked.isFailure()) {
514
+ return _propagate(rechecked);
515
+ }
516
+ // The address guard runs immediately before the connect and after the request guard, so it
517
+ // is always the last word before anything reaches the wire.
518
+ const cleared = await _clearAddress([...completed, { url: checked.url }], guards, watch);
519
+ if (cleared.isFailure()) {
520
+ return _propagate(cleared);
521
+ }
522
+ const unrepeated = _detectLoop(cleared.value.url, completed);
523
+ if (unrepeated.isFailure()) {
524
+ resolved.logger.detail(`saferFetch: ${cleared.value.url.toString()} failed: ${unrepeated.message}`);
525
+ return _propagate(unrepeated);
526
+ }
527
+ // Downstream guards see the chain as it was actually requested, so the URL a response guard
528
+ // reads is the URL the address guard cleared — not the pre-normalization spelling.
529
+ const requested = [...completed, { url: cleared.value.url }];
530
+ const attempted = await _connect(checked, cleared.value, requested, guards, options, resolved, watch, notes);
531
+ if (attempted.isFailure()) {
532
+ resolved.logger.detail(`saferFetch: ${cleared.value.url.toString()} failed: ${attempted.message}`);
533
+ return _propagate(attempted);
534
+ }
535
+ const attempt = attempted.value;
536
+ if (attempt.outcome.kind === 'final') {
537
+ return _succeed({
538
+ bytes: attempt.outcome.bytes,
539
+ head: attempt.outcome.head,
540
+ urlChain: [...completed.map((hop) => hop.url.toString()), attempt.url.toString()]
541
+ });
542
+ }
543
+ const next = _nextHop(attempt.url, completed, attempt.outcome, checked, resolved);
544
+ if (next.isFailure()) {
545
+ resolved.logger.detail(`saferFetch: ${attempt.url.toString()} failed: ${next.message}`);
546
+ return _propagate(next);
547
+ }
548
+ completed.push(Object.assign({ url: attempt.url, status: attempt.outcome.status }, (attempt.connectedAddress !== undefined ? { connectedAddress: attempt.connectedAddress } : {})));
549
+ request = next.value;
550
+ }
551
+ }
552
+ /**
553
+ * Decides how long to wait before retrying a failed walk, or that it must not be retried.
554
+ *
555
+ * @remarks
556
+ * Every rule that can refuse a retry is applied here, in one place, so "would this have been
557
+ * retried?" is answerable by reading one function: the attempt budget, the retryable-failure
558
+ * set, the idempotency rule, the `Retry-After` clamp, and the overall deadline as ceiling.
559
+ *
560
+ * **The deadline is the last word.** When the remaining budget is shorter than the computed
561
+ * delay, the call fails with the attempt's own failure rather than sleeping past the deadline
562
+ * the caller set — a `timeout` reported at `timeoutMs` is what the caller asked for, and one
563
+ * reported at `timeoutMs + backoff` is a primitive that does not keep its own promises.
564
+ */
565
+ function _retryDelayMs(reason, attempt, resolved, notes, watch) {
566
+ const policy = resolved.retry;
567
+ if (attempt >= policy.attempts) {
568
+ return undefined;
569
+ }
570
+ if (!(0, retry_1.isRetryableFailure)(reason) || !(0, retry_1.isRetryableMethod)(resolved.method, policy)) {
571
+ return undefined;
572
+ }
573
+ const retryAfterMs = (0, retry_1.honorsRetryAfter)(reason, policy)
574
+ ? (0, retry_1.parseRetryAfterMs)(notes.retryAfter, Date.now())
575
+ : undefined;
576
+ const delay = (0, retry_1.computeRetryDelayMs)(Object.assign(Object.assign({ attempt,
577
+ policy }, (retryAfterMs !== undefined ? { retryAfterMs } : {})), { random: Math.random }));
578
+ return delay <= watch.remainingMs ? delay : undefined;
579
+ }
580
+ /**
581
+ * Runs one attempt and, if the policy says so, the next one after it.
582
+ *
583
+ * @remarks
584
+ * Recursive rather than a loop, because **a retry is a fresh start rather than another turn of
585
+ * something already in progress**: every attempt is a full {@link _walk} from hop 0, and nothing
586
+ * survives the boundary — not the guard's verdict, not the redirect chain, not the headers a
587
+ * cross-origin hop stripped, not the previous response's `Retry-After`. Expressing that as a
588
+ * self-call keeps the "nothing carries over" property visible in the shape of the code rather
589
+ * than resting on a reader noticing which variables are declared inside the loop body.
590
+ *
591
+ * Depth is bounded by `retry.attempts` and each level is `await`ed, so the frames unwind rather
592
+ * than nest.
593
+ *
594
+ * @param attempt - 0-based index of the attempt about to run; `0` is the caller's first request.
595
+ */
596
+ async function _runAttempt(url, guards, options, resolved, watch, attempt) {
597
+ // Fresh per attempt: notes describe the attempt that just failed, and carrying a previous
598
+ // attempt's `Retry-After` into a later decision would schedule against a response that is no
599
+ // longer the reason we are retrying.
600
+ const notes = {};
601
+ const walked = await _walk(url, guards, options, resolved, watch, notes);
602
+ // `!isFailure()` rather than `isSuccess()`: only the former narrows to the failure branch
603
+ // below, where `detail` is known to be present.
604
+ if (!walked.isFailure()) {
605
+ return walked;
606
+ }
607
+ const delay = _retryDelayMs(walked.detail, attempt, resolved, notes, watch);
608
+ if (delay === undefined) {
609
+ return walked;
610
+ }
611
+ // Clears an attempt-scoped stop — a headers timeout ends the attempt, not the call — so the
612
+ // backoff below is not answered instantly by the previous attempt's own deadline. A terminal
613
+ // stop deliberately survives, and ends the call at the first race below.
614
+ watch.attemptEnded();
615
+ resolved.logger.detail(`saferFetch: ${url.toString()} failed (${walked.message}); retrying in ` +
616
+ `${Math.round(delay)}ms — attempt ${attempt + 2} of ${resolved.retry.attempts + 1}.`);
617
+ // Delayed on the same watch the request races against, so a caller who aborts during a backoff
618
+ // is answered then rather than after the sleep.
619
+ const slept = await watch.delay(delay);
620
+ return slept.stopped
621
+ ? _stopped(watch, slept.cause)
622
+ : _runAttempt(url, guards, options, resolved, watch, attempt + 1);
623
+ }
624
+ async function _execute(url, options) {
625
+ const resolvedOptions = _resolveCallOptions(options);
626
+ if (resolvedOptions.isFailure()) {
627
+ return _propagate(resolvedOptions);
628
+ }
629
+ const resolved = resolvedOptions.value;
630
+ const resolvedGuards = (0, guards_1.resolveGuards)(options);
631
+ if (resolvedGuards.isFailure()) {
632
+ return _unknown(resolvedGuards.message);
633
+ }
634
+ const guards = resolvedGuards.value;
635
+ const parsed = _parseUrl(url);
636
+ if (parsed.isFailure()) {
637
+ return _propagate(parsed);
638
+ }
639
+ const watch = new deadline_1.DeadlineWatch(resolved.timeoutMs, resolved.headersTimeoutMs, options.signal);
640
+ // `captureAsyncResult` rather than `try`/`finally`, for the same reason `_capture` wraps every
641
+ // guard and transport call: an entry point documented to always return a `Result` has to keep
642
+ // that promise even when a collaborator breaks its own. Nothing inside is *supposed* to throw
643
+ // — guards, transports and the body read are each captured already — but the caller's
644
+ // `logger` is collaborator code this primitive does not wrap, and a thrown diagnostic must not
645
+ // become the caller's problem. The watch is disposed on both paths because the capture has
646
+ // already converted the throw into a value.
647
+ const ran = await (0, ts_utils_1.captureAsyncResult)(async () => _runAttempt(parsed.value, guards, options, resolved, watch, 0));
648
+ watch.dispose();
649
+ return ran.isFailure()
650
+ ? // `'unknown'` is the taxonomy's honest slot for "something violated its contract", rather
651
+ // than a guess at a more specific kind.
652
+ _unexpected(ran.message)
653
+ : // Same wrapping for a throw a `thenOnSuccess` callback caught before it could reach the
654
+ // capture above — see {@link _withReason}. This is the single boundary where an `Outcome`
655
+ // becomes the caller's result, so it is the one place the invariant has to hold.
656
+ _withReason(ran.value);
657
+ }
658
+ function _toResponse(value, raw) {
659
+ return {
660
+ value,
661
+ status: raw.head.status,
662
+ headers: raw.head.headers,
663
+ urlChain: raw.urlChain,
664
+ bytesRead: raw.bytes.byteLength
665
+ };
666
+ }
667
+ /**
668
+ * Applies the caller's parser to the decoded body.
669
+ *
670
+ * @remarks
671
+ * Exists for the same reason as {@link _decodeText}: it is the boundary where a plain
672
+ * `Result` from a `Converter` becomes a `DetailedResult` carrying a `FetchFailureReason`.
673
+ * Isolating that conversion in a named helper is what lets the entry points stay chained.
674
+ */
675
+ function _parseJson(text, parser) {
676
+ const parsed = parser.convert(text);
677
+ if (parsed.isFailure()) {
678
+ return _fail({ kind: 'parse', detail: parsed.message }, `failed to parse response as JSON: ${parsed.message}`);
679
+ }
680
+ return _succeed(parsed.value);
681
+ }
682
+ function _decodeText(raw) {
683
+ var _a;
684
+ const charset = (_a = (0, contentType_1.parseCharset)(raw.head.contentType)) !== null && _a !== void 0 ? _a : 'utf-8';
685
+ // `fatal: true` so an unknown charset, or a byte sequence invalid in the declared one,
686
+ // surfaces as a decode failure rather than as silent mojibake some caller treats as data.
687
+ const decoded = (0, ts_utils_1.captureResult)(() => new TextDecoder(charset, { fatal: true }).decode(raw.bytes));
688
+ if (decoded.isFailure()) {
689
+ return _fail({ kind: 'decode', detail: decoded.message }, `failed to decode response as "${charset}": ${decoded.message}`);
690
+ }
691
+ return _succeed(decoded.value);
692
+ }
693
+ /**
694
+ * Fetches a URL and returns the raw response bytes, subject to the scheme check, the address
695
+ * guard, the request and response guards, the deadlines, and the response size cap.
696
+ *
697
+ * @remarks
698
+ * **What this does not protect against.** It is not a sandbox: a caller who wants to reach
699
+ * `127.0.0.1` can call `globalThis.fetch` directly, so this guards untrusted *data*, never
700
+ * untrusted *code in the same process*. It does no certificate pinning and adds nothing
701
+ * against a network-position attacker — TLS is that control. It does not resist DNS rebinding:
702
+ * an address guard validates a resolved address and the transport then re-resolves, so a
703
+ * hostile resolver can answer the two lookups differently. And it inspects nothing in the
704
+ * response body. If your deployment has an egress proxy or firewall, that control is strictly
705
+ * stronger than this one; this is defense in depth for deployments that do not.
706
+ *
707
+ * **The failure detail is an internal-network scanning oracle.** Log it; do not echo it, or
708
+ * any string derived from it, to an untrusted caller. The detail is structured precisely so
709
+ * that mapping it to a coarse public code is trivial.
710
+ *
711
+ * **Redirects are rejected unless you ask for them.** `redirectPolicy: 'validate-each-hop'`
712
+ * follows them, runs the address guard on every hop before any connection, and drops credential
713
+ * headers the first time the chain leaves an origin — and never restores them, so an
714
+ * `A` → `B` → `A` chain does not hand the token back to `A`.
715
+ *
716
+ * @param url - The URL to fetch. Only `http:` and `https:` are ever requested; every other
717
+ * scheme fails as `'invalid-url'`.
718
+ * @param options - Call options. `addressGuard` is required and has no default — use
719
+ * `blockPrivateNetworks()` on Node, or `allowAnyAddress()` to name the absence of the
720
+ * guarantee at the call site. `maxResponseBytes` defaults to 5 MiB and is meant to be tuned
721
+ * per call: real documents clear that, and raising it for the calls that need it is the
722
+ * intended use, not a workaround.
723
+ * @public
724
+ */
725
+ async function saferFetchBytes(url, options) {
726
+ return (await _execute(url, options)).onSuccess((raw) => _succeed(_toResponse(raw.bytes, raw)));
727
+ }
728
+ /**
729
+ * Fetches a URL and decodes the response body as text.
730
+ *
731
+ * @remarks
732
+ * The charset comes from the `Content-Type` parameter and defaults to UTF-8. Decoding is
733
+ * strict: an unknown charset, or a byte sequence that is not valid in the declared one, fails
734
+ * as `'decode'` rather than silently producing mojibake that some caller downstream treats as
735
+ * data.
736
+ *
737
+ * See {@link SaferFetch.saferFetchBytes} for what this primitive does **not** protect against,
738
+ * and for the warning about echoing failure detail to untrusted callers.
739
+ *
740
+ * @param url - The URL to fetch.
741
+ * @param options - Call options. `addressGuard` is required. `maxResponseBytes` defaults to
742
+ * 5 MiB and is meant to be tuned per call.
743
+ * @public
744
+ */
745
+ async function saferFetchText(url, options) {
746
+ // Nested rather than flat: `_toResponse` needs both the decoded text and the raw outcome it
747
+ // came from, and nesting is what keeps `raw` in scope for it.
748
+ return (await _execute(url, options)).onSuccess((raw) => _decodeText(raw).onSuccess((text) => _succeed(_toResponse(text, raw))));
749
+ }
750
+ async function saferFetchJson(url, options) {
751
+ var _a;
752
+ const converter = (_a = options.converter) !== null && _a !== void 0 ? _a : undefined;
753
+ const parser = converter !== undefined
754
+ ? ts_json_base_1.Converters.stringifiedJson(converter)
755
+ : ts_json_base_1.Converters.stringifiedJson();
756
+ // Nested for the same reason as `saferFetchText`: the response needs the raw outcome as well
757
+ // as the parsed value, and nesting is what keeps `raw` in scope.
758
+ return (await _execute(url, options)).onSuccess((raw) => _decodeText(raw).onSuccess((text) => _parseJson(text, parser).onSuccess((parsed) => _succeed(_toResponse(parsed, raw)))));
759
+ }
760
+ //# sourceMappingURL=saferFetch.js.map